예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
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
예제 #4
0
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
예제 #5
0
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)
예제 #6
0
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)