def run_kordesii_decoder(self, decoder_name, warn_no_strings=True, decoderdir=None): """ 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 :param warn_no_strings: Whether to produce a warning if no string were found. :param decoderdir: Custom decoder directory to use instead of the default. :return: Instance of the kordesii_reporter. :raises RuntimeError: If kordesii is not installed. """ if not kordesii: raise RuntimeError('Please install kordesii to use this function.') # Pull from cache if we already ran this decoder. if decoder_name in self._kordesii_cache: return self._kordesii_cache[decoder_name] logger.info('Running {} kordesii decoder on file {}.'.format( decoder_name, self.file_name)) kordesii_reporter = kordesii.Reporter(decoderdir=decoderdir, base64outputfiles=True) kordesii_reporter.run_decoder(decoder_name, data=self.file_data, log=True) if warn_no_strings: decrypted_strings = kordesii_reporter.get_strings() if not decrypted_strings: # Not necessarily a bad thing, the decoder might be used for something else. logger.info( 'No decrypted strings were returned by the decoder for file {}.' .format(self.file_name)) # Cache results self._kordesii_cache[decoder_name] = kordesii_reporter return kordesii_reporter
def run_kordesii_decoder(self, decoder_name: str, warn_no_strings=True): """ 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 :param warn_no_strings: Whether to produce a warning if no string were found. :return: Instance of the kordesii_reporter. :raises RuntimeError: If kordesii is not installed. """ if not kordesii: raise RuntimeError("Please install kordesii to use this function.") # Pull from cache if we already ran this decoder. if decoder_name in self._kordesii_cache: return self._kordesii_cache[decoder_name] logger.info( f"Running {decoder_name} kordesii decoder on file {self.file_name}." ) # Ensure decoderdir sources are populated kordesii.register_entry_points() kordesii_reporter = kordesii.Reporter(base64outputfiles=True) kordesii_reporter.run_decoder(decoder_name, data=self.file_data, log=True) if warn_no_strings: decrypted_strings = kordesii_reporter.get_strings() if not decrypted_strings: # Not necessarily a bad thing, the decoder might be used for something else. logger.info( f"No decrypted strings were returned by the decoder for file {self.file_name}." ) # Cache results self._kordesii_cache[decoder_name] = kordesii_reporter return kordesii_reporter
def _run_decoder(name, data, filename, append_output_text=True): logger.info("_run_decoder %s %s %s" % (name, filename, hashlib.md5(data).hexdigest())) try: reporter = kordesii.Reporter(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: logger.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 = {'error': traceback.format_exc()} logger.error("_run_decoder %s %s %s" % (name, filename, traceback.format_exc())) return output
def _run_decoder(name, data, filename, append_output_text=True): output = {} kordesii_logger = logging.getLogger() list_handler = _get_log_handler() try: kordesii_logger.addHandler(list_handler) reporter = kordesii.Reporter(base64outputfiles=True) with _make_temp_folder(prefix="kordesii-server_tempdir-") as tempdir: # 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. file_path = os.path.join(tempdir, filename) with open(file_path, "wb") as fp: fp.write(data) # Run decoder reporter.run_decoder(name, filename=file_path) # Format and return results output = reporter.metadata output["debug"] = [msg for msg in list_handler.messages] # To stay consistent with the current major version API, "error" is singular. output["error"] = reporter.errors if append_output_text: output["output_text"] = reporter.get_output_text() except Exception as e: output = {"error": [str(e)]} if f.has_app_context(): f.current_app.logger.exception( "Error running decoder '{}': {}".format(name, str(e))) finally: return output
def test(testcase_dir, malware_repo, nprocs, update, add, add_filelist, delete, yes, force, show_passed, silent, decoder): """ Testing utility to create and execute decoder test cases. \b DECODER: Decoders to test. Test all decoders if not provided. \b Common usages:: kordesii test - Run all tests cases. kordesii test foo - Run test cases for foo decoder. kordesii test foo -u - Update existing test cases for foo decoder. kordesii test -u - Update existing test cases for all decoders. kordesii test foo --add=./malware.bin - Add test case for malware.bin sample for foo parser. kordesii test foo --add-filelist=./paths.txt - Add tests cases for foo decoder using text file of paths. kordesii test foo --delete=./malware.bin - Delete test case for malware.bin sample for foo parser. """ # Configure test object reporter = kordesii.Reporter() tester = Tester( reporter, results_dir=testcase_dir, decoder_names=decoder or [None], nprocs=nprocs, malware_repo=malware_repo, ) # Add/Delete if add or add_filelist or delete: if not decoder: # Don't allow adding a file to ALL test cases. raise click.BadParameter( "DECODER must be provided when adding or deleting a file from a test case." ) # Cast tuple to list so we can manipulate. add = list(add) for filelist in add_filelist: with open(filelist, "r") as f: for file_path in f.readlines(): add.append(file_path.rstrip("\n")) for file_path in add: click.echo("Adding new test cases. May take a while...") if malware_repo: file_path = _add_to_malware_repo(file_path, malware_repo) tester.add_test(file_path, force) for file_path in delete: if malware_repo: file_path = _get_malware_repo_path(file_path, malware_repo) tester.remove_test(file_path) # Update elif update: if not decoder and not yes: click.confirm( "WARNING: About to update test cases for ALL decoders. Continue?", abort=True) click.echo("Updating test cases. May take a while...") tester.update_tests(force) # Run tests else: if not decoder and not yes: click.confirm( "DECODER argument not provided. Run tests for ALL decoders?", default=True, abort=True) # Force ERROR level logs so we don't spam the console. logging.root.setLevel(logging.ERROR) _run_tests(tester, silent, show_passed)
def parse(decoder, input, json_, output_files, cleanup, tempdir, enable_ida_log, timeout, is_64bit): """ Parses given input with given parser. \b DECODER: Name of decoder to run. INPUT: One or more input file paths. (Wildcards are allowed). \b Common usages:: kordesii parse foo ./malware.bin - Run foo decoder on ./malware.bin kordesii parse foo ./repo/* - Run foo decoder on files found in repo directory. kordesii parse --json foo ./malware.bin - Run foo decoder and display results as json. """ # Python won't process wildcards when used through Windows command prompt. if any("*" in path for path in input): new_input = [] for path in input: if "*" in path: new_input.extend(glob.glob(path)) else: new_input.append(path) input = new_input input_files = list(filter(os.path.isfile, input)) if not input_files: sys.exit("Unable to find any input files.") # Run Kordesii try: reporter = kordesii.Reporter(tempdir=tempdir, disabletempcleanup=not cleanup) results = [] for path in input_files: logger.info("Parsing: {}".format(path)) input_file = os.path.abspath(path) reporter.run_decoder( decoder, input_file, timeout=timeout, log=enable_ida_log, cleanup_txt_files=cleanup, cleanup_idb_files=cleanup, cleanup_output_files=not output_files, is_64bit=is_64bit, ) # TODO: Pull errors and ida logs from logger? result = reporter.metadata if reporter.errors: result["errors"] = reporter.errors if reporter.ida_log: result["ida_log"] = reporter.ida_log results.append(result) if not json_: reporter.print_report() if json_: print(json.dumps(results, indent=4)) except Exception as e: error_message = "Error running DC3-Kordesii: {}".format(e) traceback.print_exc() if format == "json": print(json.dumps({"errors": [error_message]})) else: print(error_message) sys.exit(1)