예제 #1
0
    def run_kordesii_decoder(self, decoder_name):
        """
        Run the specified kordesii decoder against the file data.  The reporter object is returned
        and can be accessed as necessary to obtain output files, etc.

        :param decoder_name: name of the decoder to run
        :return: Instance of the kordesii_reporter.

        :raises RuntimeError: If kordesii is not installed.
        """
        if not kordesiireporter:
            raise RuntimeError('Please install kordesii to use this function.')

        logger.info('Running {} kordesii decoder on file {}.'.format(decoder_name, self.file_name))
        kordesii_reporter = kordesiireporter(base64outputfiles=True, enableidalog=True)

        kordesii_reporter.run_decoder(decoder_name, data=self.file_data)
        for message in kordesii_reporter.get_debug():
            logger.info('[kordesii_debug] {}'.format(message))

        for message in kordesii_reporter.get_errors():
            logger.error('[kordesii_error] {}'.format(message))

        decrypted_strings = kordesii_reporter.get_strings()
        if not decrypted_strings:
            logger.warn(
                'No decrypted strings were returned by the decoder for file {}.'.format(self.file_name))

        return kordesii_reporter
예제 #2
0
def descriptions():
    """
    List descriptions of decoder modules
    """
    try:
        response.content_type = "application/json"
        reporter = kordesiireporter(decoderdir=DECODER_DIR, base64outputfiles=True)
        output = {"decoders": reporter.list_decoders()}
        return reporter.pprint(output)
    except Exception as e:
        output = {'error': traceback.format_exc()}
        logger.error("descriptions %s" % (traceback.format_exc()))
        return output
def __run_decoder(name, data, filename, append_output_text=True):
    output = {}
    logger.info("__run_decoder %s %s %s" %
                (name, filename, hashlib.md5(data).hexdigest()))
    try:
        reporter = kordesiireporter(decoderdir=DECODER_DIR,
                                    base64outputfiles=True)

        # Since we want the marked up IDB returned using the original filename, we
        # want to pass in a file to the reporter instead of data.
        tempdir = tempfile.mkdtemp(prefix="kordesii-server_tempdir-")
        file_path = os.path.join(tempdir, filename)
        with open(file_path, "wb") as f:
            f.write(data)

        # Run decoder
        reporter.run_decoder(name, filename=file_path)

        # Since we used our own temp directory to pass in a file, we have to
        # clean it up manually.
        try:
            shutil.rmtree(tempdir, ignore_errors=True)
        except Exception as e:
            reporter.debug("Failed to purge server temp dir: %s, %s" %
                           (tempdir, str(e)))

        # Format and return results
        output = reporter.metadata
        if reporter.errors:
            output["error"] = str(reporter.errors)
            for error in reporter.errors:
                logger.error("__run_decoder %s %s %s" %
                             (name, filename, error))
        if append_output_text:
            output["output_text"] = reporter.get_output_text()

        return output

    except Exception as e:
        output = {}
        output['error'] = traceback.format_exc()
        logger.error("__run_decoder %s %s %s" %
                     (name, filename, traceback.format_exc()))
        return output
예제 #4
0
def make_opt_parser():
    '''
    create a option parser to handle command line inputs
    '''
    usage_str = 'usage:  %s [options] FILE' % (os.path.basename(sys.argv[0]))
    description = "DC3-Kordesii Framework: utility for executing string decoder modules"
    opt_parser = optparse.OptionParser(usage_str, description=description)

    default_decoderdir = ''

    #create reporter to get default paths, ignore if this fails
    try:
        default_reporter = kordesiireporter()
        default_decoderdir = default_reporter.decoderdir
    except Exception as e:
        traceback.print_exc()

    opt_parser.add_option(
        '-a',
        '--autonomous',
        action='store_false',
        default=True,
        dest='autonomous',
        help='Launch IDA without autonomous mode so IDA GUI appears.')
    opt_parser.add_option('-c',
                          '--hidedebug',
                          action="store_true",
                          default=False,
                          dest='hidedebug',
                          help='Hide debug messages in output')
    opt_parser.add_option('-p',
                          '--decoder',
                          action='store',
                          type='string',
                          default='',
                          dest='decoder',
                          help='string decoder to call')
    opt_parser.add_option(
        '-e',
        '--enableidalog',
        action="store_true",
        default=False,
        dest='enableidalog',
        help='include the log contents produced by IDA in the results')
    opt_parser.add_option(
        '-f',
        '--includefileinfo',
        action='store_true',
        default=False,
        dest='includefilename',
        help=
        'include input file information such as filename, hashes, and compile time in parser output'
    )
    opt_parser.add_option(
        '-g',
        '--disabletempcleanup',
        action='store_true',
        default=False,
        dest='disabletempcleanup',
        help=
        'Disable cleanup of framework created temp files including managed tempdir'
    )
    opt_parser.add_option(
        '-j',
        '--jsonoutput',
        action='store_true',
        default=False,
        dest='jsonoutput',
        help='Enable json output for parser reports (instead of formatted text)'
    )
    opt_parser.add_option('-l',
                          '--list',
                          action="store_true",
                          default=False,
                          dest='list',
                          help='list all kordesii string decoders')
    opt_parser.add_option('-m',
                          '--tempdir',
                          action='store',
                          type='string',
                          metavar='DIR',
                          default=tempfile.gettempdir(),
                          dest='tempdir',
                          help='temp directory' + ' [default: %default]')
    opt_parser.add_option('-d',
                          '--decoderdir',
                          action='store',
                          type='string',
                          metavar='DIR',
                          default=default_decoderdir,
                          dest='decoderdir',
                          help='decoders directory' + ' [default: %default]')
    opt_parser.add_option(
        '-t',
        '--timeout',
        action='store',
        type=int,
        default=10,
        dest='timeout',
        help=
        'Timeout for running IDA (default = %default). A timout of 0 disables '
        + 'the timeout.')
    opt_parser.add_option(
        '-x',
        '--disabletxtcleanup',
        action='store_true',
        default=False,
        dest='disabletxtcleanup',
        help=
        'Disable cleanup of txt files generated by IDA (ida_strings.txt, ida_log.txt, ida_debug.txt)'
    )
    opt_parser.add_option(
        '-y',
        '--enableidbcleanup',
        action='store_true',
        default=False,
        dest='enableidbcleanup',
        help='Enable cleanup of IDB files and any IDA component files')
    opt_parser.add_option(
        '-z',
        '--enableoutputfilecleanup',
        action='store_true',
        default=False,
        dest='enableoutputfilecleanup',
        help='Enable any cleanup of unique output files generated by a decoder'
    )

    return opt_parser
예제 #5
0
def main():
    '''
    Takes args from the command line, runs IDA, and returns with IDA's returncode on success or a message
    on failure.
    '''
    opt_parse = make_opt_parser()
    options, args = opt_parse.parse_args()

    # If we can not create reporter object there is very little we can do. Just die immediately.
    try:
        reporter = kordesiireporter(
            decoderdir=options.decoderdir,
            tempdir=options.tempdir,
            disabletempcleanup=options.disabletempcleanup,
            disabledebug=options.hidedebug,
            enableidalog=options.enableidalog)
    except Exception as e:
        error_message = "Error loading DC3-MWCP reporter object, please check installation: %s" % (
            traceback.format_exc())
        if options.jsonoutput:
            print('{"errors": ["%s"]}' % (error_message))
        else:
            print(error_message)
        sys.exit(1)

    # List out decoder names and exit
    if options.list:
        decoders = reporter.list_decoders()

        if options.jsonoutput:
            if reporter.errors:
                decoders.append({"errors": reporter.errors})
            print reporter.pprint(decoders)
        else:
            for name in decoders:
                print(name)
            if reporter.errors:
                print("")
                print("Errors:")
                for error in reporter.errors:
                    print("    %s" % (error))
        return

    # Currently only allow one file to be passed in
    if not args or len(args) != 1:
        opt_parse.print_help()
        return

    # Run decoder
    if options.decoder:

        # Grab file from arguments
        input_file = os.path.abspath(args[0])

        # Verify provided decoder name is valid
        decoder = options.decoder
        try:
            decoder_path = reporter.get_decoder_path(decoder)
        except ValueError as e:
            print "Error: {}".format(e)
            return

        # Ensure the decoder script will close IDA properly
        if "idc.Exit(" not in open(decoder_path, 'r').read():
            print "Error: The decoder will leave IDA open indefinitely. Ensure it has a call to 'idc.Exit'."

        # IDA doesn't like backslashes in it's argv.
        input_file = input_file.replace('\\', '/').strip()
        decoder_path = decoder_path.replace('\\', '/').strip()

        # Run the decoder
        reporter.run_decoder(
            decoder,
            input_file,
            timeout=options.timeout,
            autonomous=options.autonomous,
            cleanup_txt_files=not options.disabletxtcleanup,
            cleanup_output_files=options.enableoutputfilecleanup,
            cleanup_idb_files=options.enableidbcleanup)

        # Output results
        if options.jsonoutput:
            output = reporter.metadata
            if reporter.errors:
                output["errors"] = reporter.errors
            if reporter.ida_log:
                output["ida_log"] = reporter.ida_log
            print reporter.pprint(output)
        else:
            reporter.output_text()
예제 #6
0
def main():
    ''' Run tool. '''

    print ''

    # Get command line arguments
    argparser = get_arg_parser()
    args = argparser.parse_args()

    # Configure reporter based on args
    reporter = kordesiireporter(enableidalog=True)

    # Configure test object
    tester = kordesiitester(reporter=reporter, results_dir=args.test_case_dir)

    valid_decoder_names = reporter.list_decoders()

    decoders = []
    if args.decoder_name:
        if args.decoder_name in valid_decoder_names:
            decoders = [args.decoder_name]
        else:
            print "Error: Invalid decoder name(s) specified. decoder names are case sensitive."
            exit(1)
    if args.all_tests:
        decoders = valid_decoder_names

    if not decoders:
        print "You must specify the decoder to test (or test all decoders)"
        exit(2)

    if args.decoder_name:
        results_file_path = tester.get_results_filepath(args.decoder_name)

    # Gather all our input files
    input_files = []
    if args.input_file:
        input_files = read_input_list(args.input_file)

    # Default is to run test cases
    if args.run_tests:
        print "Running test cases. May take a while..."
        all_passed, test_results = tester.run_tests(
            decoders,
            filter(None, args.field_names.split(",")),
            ignore_field_names=filter(None,
                                      args.exclude_field_names.split(",")))
        print "All Passed = {0}\n".format(all_passed)
        if not args.silent:
            if args.only_failed_tests:
                tester.print_test_results(test_results,
                                          failed_tests=True,
                                          passed_tests=False,
                                          json_format=args.json)
            else:
                tester.print_test_results(test_results,
                                          failed_tests=True,
                                          passed_tests=True,
                                          json_format=args.json)
        if all_passed:
            exit(0)
        else:
            exit(1)

    #add files to test cases
    elif args.delete:
        removed_files = tester.remove_test_results(args.decoder_name,
                                                   input_files)
        for filename in removed_files:
            print("Removing results for %s in %s" %
                  (filename, results_file_path))
    elif args.decoder_name and (args.update or
                                (not args.delete and input_files)):
        if args.update:
            input_files.extend(tester.list_test_files(args.decoder_name))

        for input_file in input_files:
            tester.gen_results(decoder_name=args.decoder_name,
                               input_file_path=input_file)

            if len(reporter.metadata) > 1 and len(reporter.errors) == 0:
                print("Updating results for %s in %s" %
                      (input_file, results_file_path))
                tester.update_test_results(results_file_path=results_file_path,
                                           results_data=reporter.metadata,
                                           replace=True)
            elif len(reporter.metadata) > 1 and len(reporter.errors) > 0:
                print("Error occurred for %s in %s, not updating" %
                      (input_file, results_file_path))
            else:
                print("Empty results for %s in %s, not updating" %
                      (input_file, results_file_path))
    else:
        argparser.print_help()
예제 #7
0
def main():
    """ Run tool. """

    print('')

    # Get command line arguments
    argparser = get_arg_parser()
    args = argparser.parse_args()

    # Configure reporter based on args
    reporter = kordesiireporter(enableidalog=True)

    # Configure test object
    tester = kordesiitester(reporter=reporter, results_dir=args.test_case_dir)

    valid_decoder_names = reporter.list_decoders()

    decoders = []
    if args.decoder_name:
        if args.decoder_name in valid_decoder_names:
            decoders = [args.decoder_name]
        else:
            print(
                "Error: Invalid decoder name(s) specified. decoder names are case sensitive."
            )
            exit(1)
    if args.all_tests:
        decoders = valid_decoder_names

    if not decoders:
        print("You must specify the decoder to test (or test all decoders)")
        exit(2)

    if args.decoder_name:
        results_file_path = tester.get_results_filepath(args.decoder_name)

    # Gather all our input files
    input_files = []
    if args.input_file:
        input_files = read_input_list(args.input_file)

    # Default is to run test cases
    if args.run_tests:
        print("Running test cases. May take a while...")
        start_time = timeit.default_timer()
        test_infos = []
        json_list = []
        all_passed = True
        for passed, test_result, test_info in tester.run_tests(
                decoders,
                filter(None, args.field_names.split(",")),
                ignore_field_names=filter(None,
                                          args.exclude_field_names.split(",")),
                nprocs=args.nprocs):
            test_infos.append(test_info)
            if not passed:
                all_passed = False
            if not args.silent:
                sys.stdout.write(
                    "{finished}/{total} - {decoder} {filename} {run_time:.4f}s\n"
                    .format(**test_info))
                sys.stdout.flush()
                if not passed or not args.only_failed_tests:
                    # TODO: Refactor support for json.
                    if args.json:
                        json_list.append(
                            tester.format_test_result(test_result,
                                                      json_format=True))
                    else:
                        display = tester.format_test_result(test_result)
                        print(
                            display.encode(locale.getpreferredencoding(),
                                           'replace'))

        if args.json:
            print(json.dumps(json_list))

        # Don't count calculating the stats and printing them as test running time
        end_time = timeit.default_timer()

        if not args.silent:
            print('\nTest stats:')
            print('\nTop 10 Slowest Test Cases:')
            # Cases sorted slowest first
            sorted_cases = sorted(test_infos,
                                  key=lambda x: x['run_time'],
                                  reverse=True)
            for i, info in enumerate(sorted_cases[:10]):
                print('{:2}. {} {} {:.4f}s'.format(i + 1, info['decoder'],
                                                   info['filename'],
                                                   info['run_time']))

            print('\nTop 10 Fastest Test Cases:')
            for i, info in enumerate(list(reversed(sorted_cases))[:10]):
                print('{:2}. {} {} {:.4f}s'.format(i + 1, info['decoder'],
                                                   info['filename'],
                                                   info['run_time']))

            run_times = [info['run_time'] for info in test_infos]
            print('\nMean Running Time: {:.4}s'.format(
                sum(run_times) / len(test_infos)))
            print('Median Running Time: {:.4f}s'.format(_median(run_times)))
            print()

        print("Total Running Time: {}".format(
            datetime.timedelta(seconds=end_time - start_time)))
        print("All Passed = {0}\n".format(all_passed))
        exit(0 if all_passed else 1)

    # add files to test cases
    elif args.decoder_name and args.delete:
        removed_files = tester.remove_test_results(args.decoder_name,
                                                   input_files)
        for filename in removed_files:
            print("Removing results for %s in %s" %
                  (filename, results_file_path))
    elif args.decoder_name and (args.update or
                                (not args.delete and input_files)):
        if args.update:
            input_files.extend(tester.list_test_files(args.decoder_name))

        for input_file in input_files:
            tester.gen_results(decoder_name=args.decoder_name,
                               input_file_path=input_file)

            if len(reporter.metadata) > 1 and len(reporter.errors) == 0:
                print("Updating results for %s in %s" %
                      (input_file, results_file_path))
                tester.update_test_results(results_file_path=results_file_path,
                                           results_data=reporter.metadata,
                                           replace=True)
            elif len(reporter.metadata) > 1 and len(reporter.errors) > 0:
                print("Error occurred for %s in %s, not updating: " %
                      (input_file, results_file_path))
                print("\n".join(reporter.get_debug()))
                print("\n".join(reporter.errors))
            else:
                print("Empty results for %s in %s, not updating" %
                      (input_file, results_file_path))
    else:
        argparser.print_help()