Beispiel #1
0
def test_output_file(tmpdir):
    test_file = tmpdir / 'foo.txt'
    reporter = mwcp.Reporter(outputdir=str(tmpdir))
    reporter.output_file(b'This is data!', 'foo.txt', description='A foo file')

    assert test_file.exists()
    assert test_file.read_binary() == b'This is data!'
    assert reporter.outputfiles['foo.txt'] == {
        'data': b'This is data!',
        'description': 'A foo file',
        'md5': '9c91e665b5b7ba5a3066c92dd02d3d7c',
        'path': str(test_file)
    }
    assert reporter.metadata['outputfile'] == [[
        'foo.txt', 'A foo file', '9c91e665b5b7ba5a3066c92dd02d3d7c'
    ]]

    # Add file with same name to test name collision code.
    test_file_2 = tmpdir / 'foo.txt_4d8cf'
    reporter.output_file(b'More data!',
                         'foo.txt',
                         description='Another foo file')

    assert test_file_2.exists()
    assert test_file_2.read_binary() == b'More data!'
    assert reporter.metadata['outputfile'] == [
        ['foo.txt', 'A foo file', '9c91e665b5b7ba5a3066c92dd02d3d7c'],
        [
            'foo.txt_4d8cf', 'Another foo file',
            '4d8cfa4b19f5f971b0e6d79250cb1321'
        ],
    ]
Beispiel #2
0
def test_file_object(tmpdir):
    """Tests the mwcp.FileObject class"""
    output_dir = str(tmpdir)
    reporter = mwcp.Reporter(tempdir=output_dir, outputdir=output_dir)
    file_object = mwcp.FileObject(b'This is some test data!', reporter)

    assert file_object.file_name == u'fb843efb2ffec987db12e72ca75c9ea2.bin'
    assert file_object.file_data == b'This is some test data!'
    assert file_object.md5 == u'fb843efb2ffec987db12e72ca75c9ea2'
    assert file_object.resources is None
    assert file_object.pe is None
    assert file_object.file_path.startswith(
        os.path.join(output_dir, 'mwcp-managed_tempdir-'))

    with file_object as fo:
        assert fo.read() == b'This is some test data!'

    assert not reporter.outputfiles
    file_object.output()
    file_path = os.path.join(output_dir,
                             'fb843efb2ffec987db12e72ca75c9ea2.bin')
    assert file_object.file_name in reporter.outputfiles
    assert reporter.outputfiles[file_object.file_name] == {
        'data': b'This is some test data!',
        'path': file_path,
        'description': '',
        'md5': 'fb843efb2ffec987db12e72ca75c9ea2'
    }
    assert os.path.exists(file_path)
Beispiel #3
0
def parse(parser, input, format, output_dir, output_files, cleanup, prefix, include_filename):
    """
    Parses given input with given parser.

    \b
    PARSER: Name of parser to run.
    INPUT: One or more input file paths. (Wildcards are allowed).

    \b
    Common usages::
        mwcp parse foo ./malware.bin                          - Run foo parser on ./malware.bin
        mwcp parse foo ./repo/*                               - Run foo parser on files found in repo directory.
        mwcp parse -f json foo ./malware.bin                  - Run foo parser and display results as json.
        mwcp parse -f csv foo ./repo/* > ./results.csv        - Run foo parser on a directory and output results as a csv file.
    """
    # 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))
    output_dir = output_dir or ""

    # Run MWCP
    try:
        results = []
        for path in input_files:
            reporter = mwcp.Reporter(
                # Store output files to a folder with the same name as the input file.
                outputdir=os.path.join(output_dir, os.path.basename(path) + "_mwcp_output"),
                disable_output_files=not output_files,
                disable_temp_cleanup=not cleanup,
                prefix_output_files=prefix,
            )
            logger.info("Parsing: {}".format(path))
            result = _parse_file(reporter, path, parser, include_filename=include_filename)
            results.append(result)
            if not format:
                reporter.print_report()

        if format == "csv":
            _write_csv(input_files, results)
        elif format == "json":
            print(json.dumps(results, indent=4))

    except Exception as e:
        error_message = "Error running DC3-MWCP: {}".format(e)
        traceback.print_exc()
        if format == "json":
            print(json.dumps({"errors": [error_message]}))
        else:
            print(error_message)
        sys.exit(1)
Beispiel #4
0
def test_other_add_metadata():
    """Tests that adding multiple 'other' keys of same will convert to a list."""
    reporter = mwcp.Reporter()
    reporter.add_metadata('other', {b'foo': b'bar', 'biz': 'baz'})
    assert reporter.metadata == {'other': {'foo': 'bar', 'biz': 'baz'}}
    reporter.add_metadata('other', {b'foo': b'boop'})
    assert reporter.metadata == {
        'other': {
            'foo': ['bar', 'boop'],
            'biz': 'baz'
        }
    }
Beispiel #5
0
def test_output_file(tmpdir):
    test_file = tmpdir / '9c91e_foo.txt'
    reporter = mwcp.Reporter(outputdir=str(tmpdir))
    assert reporter.output_file(b'This is data!',
                                'foo.txt',
                                description='A foo file') == str(test_file)

    assert test_file.exists()
    assert test_file.read_binary() == b'This is data!'
    assert reporter.metadata['outputfile'] == [[
        'foo.txt', 'A foo file', '9c91e665b5b7ba5a3066c92dd02d3d7c'
    ]]

    # Add file with same name to test name collision code.
    test_file = tmpdir / '4d8cf_foo.txt'
    assert reporter.output_file(
        b'More data!', 'foo.txt',
        description='Another foo file') == str(test_file)

    assert test_file.exists()
    assert test_file.read_binary() == b'More data!'
    assert reporter.metadata['outputfile'] == [
        ['foo.txt', 'A foo file', '9c91e665b5b7ba5a3066c92dd02d3d7c'],
        ['foo.txt', 'Another foo file', '4d8cfa4b19f5f971b0e6d79250cb1321'],
    ]

    # Test file sanitization
    test_file = tmpdir / '6f1ed_hello.txt'
    reporter = mwcp.Reporter(outputdir=str(tmpdir))
    assert reporter.output_file(b'blah', u'héllo!!\x08.txt') == str(test_file)

    assert test_file.exists()
    assert test_file.read_binary() == b'blah'
    assert reporter.metadata['outputfile'] == [[
        u'héllo!!\x08.txt', '', '6f1ed002ab5595859014ebf0951522d9'
    ]]
Beispiel #6
0
def descriptions():
    """
    List descriptions of parser modules
    """

    try:
        response.content_type = "application/json"
        reporter = mwcp.Reporter(base64outputfiles=True,
                                 disableoutputfiles=True,
                                 parserdir=PARSERDIR)
        return json.dumps(mwcp.get_parser_descriptions(), indent=4)
    except Exception:
        output = {'errors': [traceback.format_exc()]}
        logger.error("descriptions %s" % (traceback.format_exc()))
        return output
Beispiel #7
0
def get_parser_descriptions(name=None, source=None):
    """
    Retrieve list of parser descriptions

    Returns list of tuples per parser. Tuple contains parser name, author, and description.
    """
    descriptions = []
    # Since, description and author are instance variables, we are going to have to
    # temporarily initialize them in order to extract their info.
    # TODO: In the future, this information should be static attributes on the class itself.
    reporter = mwcp.Reporter()
    for _name, _source, klass in sorted(iter_parsers(name=name, source=None)):
        parser = klass(reporter)
        descriptions.append(
            (_name, _source, parser.author, parser.description))
    return descriptions
Beispiel #8
0
def _print_fields(json_output=False):
    """
    Prints a table of available metadata fields to stdout.

    :param json_output: Print json
    :return:
    """
    # TODO: reporter shouldn't be generating the fields.
    reporter = mwcp.Reporter()
    fields = reporter.fields
    if json_output:
        print(json.dumps(fields, indent=4))
    else:
        for name, value in sorted(fields.items()):
            print('%-20s %s' % (name, value['description']))
            for example in value['examples']:
                print("{} {}".format(" " * 24, json.dumps(example)))
Beispiel #9
0
def _run_parser(name, data=b'', append_output_text=True):
    output = {}
    logger.info("__run_parser %s %s" % (name, hashlib.md5(data).hexdigest()))
    try:
        reporter = mwcp.Reporter(base64_output_files=True)
        reporter.run_parser(name, data=data)
        output = reporter.metadata
        if reporter.errors:
            output["errors"] = reporter.errors
            for error in reporter.errors:
                logger.error("_run_parser %s %s" % (name, error))
        if append_output_text:
            output["output_text"] = reporter.get_output_text()
        return output
    except Exception:
        output = {'errors': [traceback.format_exc()]}
        logger.error("__run_parser %s %s" % (name, traceback.format_exc()))
        return output
Beispiel #10
0
def test_file_object(tmpdir):
    """Tests the mwcp.FileObject class"""
    reporter = mwcp.Reporter(tempdir=str(tmpdir), outputdir=str(tmpdir))
    file_object = mwcp.FileObject(b'This is some test data!', reporter)

    assert file_object.file_name == u'fb843efb2ffec987db12e72ca75c9ea2.bin'
    assert file_object.file_data == b'This is some test data!'
    assert file_object.md5 == u'fb843efb2ffec987db12e72ca75c9ea2'
    assert file_object.resources is None
    assert file_object.pe is None
    assert file_object.file_path.startswith(
        os.path.join(str(tmpdir), 'mwcp-managed_tempdir-'))

    with file_object as fo:
        assert fo.read() == b'This is some test data!'

    file_object.output()
    assert (tmpdir / 'fb843_fb843efb2ffec987db12e72ca75c9ea2.bin').exists()
    assert reporter.metadata['outputfile'] == [[
        file_object.file_name, '', 'fb843efb2ffec987db12e72ca75c9ea2'
    ]]
Beispiel #11
0
def __run_parser(name, data=b'', modargs=b'', append_output_text=True):
    output = {}
    logger.info("__run_parser %s %s" % (name, hashlib.md5(data).hexdigest()))
    try:
        reporter = mwcp.Reporter(base64outputfiles=True, parserdir=PARSERDIR)
        kwargs = {}
        if modargs:
            kwargs = dict(json.loads(modargs))
        reporter.run_parser(name, data=data, **kwargs)
        output = reporter.metadata
        if reporter.errors:
            output["errors"] = reporter.errors
            for error in reporter.errors:
                logger.error("__run_parser %s %s" % (name, error))
        if append_output_text:
            output["output_text"] = reporter.get_output_text()
        return output
    except Exception:
        output = {'errors': [traceback.format_exc()]}
        logger.error("__run_parser %s %s" % (name, traceback.format_exc()))
        return output
Beispiel #12
0
def components():
    """
    Setup for testing some of the dispatcher components.
    (Set it as a fixture so we can reuse the variables without having to remake)
    """
    reporter = mwcp.Reporter()
    file_A = mwcp.FileObject(b'This is file A',
                             reporter,
                             file_name='A_match.txt',
                             output_file=False)
    file_B = mwcp.FileObject(b'This is file B',
                             reporter,
                             file_name='B_match.txt',
                             output_file=False)
    file_C = mwcp.FileObject(b'This is file C',
                             reporter,
                             file_name='no_match.txt',
                             output_file=False)

    class A(mwcp.Parser):
        DESCRIPTION = 'A Component'

        @classmethod
        def identify(cls, file_object):
            return file_object.file_name == 'A_match.txt'

        def run(self):
            self.dispatcher.add_to_queue(file_B)
            self.dispatcher.add_to_queue(file_C)

    class B(mwcp.Parser):
        DESCRIPTION = 'B Component'

        @classmethod
        def identify(cls, file_object):
            return file_object.file_name == 'B_match.txt'

    dispatcher = mwcp.Dispatcher('my_dispatcher', parsers=[A, B])

    return locals()
Beispiel #13
0
def _run_parser(name, data=b"", append_output_text=True):
    """
    Run an MWCP parser on given data.

    Logs to a list handler that is locked to the current request.

    :param str name: Name of the parser to run
    :param bytes data: Data to run parser on
    :param bool append_output_text: If the text that would otherwise be printed is
        added to the output data
    :return: Output from the reporter
    :rtype: dict
    """
    output = {}
    mwcp_logger = logging.getLogger("mwcp")
    list_handler = _get_log_handler()
    try:
        # Record only records created in the context of this request
        list_handler.addFilter(RequestFilter(request=f.request))
        mwcp_logger.addHandler(list_handler)

        reporter = mwcp.Reporter(base64_output_files=True)
        reporter.run_parser(name, data=data)
        output = reporter.metadata

        output["debug"] = [msg for msg in list_handler.messages]

        if append_output_text:
            output["output_text"] = reporter.get_output_text()
    except Exception as e:
        output = {"errors": [str(e)]}
        if f.has_app_context():
            f.current_app.logger.exception(
                "Error running parser '%s': %s", name, str(e)
            )
    finally:
        mwcp_logger.removeHandler(list_handler)
        return output
Beispiel #14
0
def test_print_report(tmpdir, capsys):
    """Tests the text report generation."""
    reporter = mwcp.Reporter(outputdir=str(tmpdir))
    reporter.add_metadata('proxy',
                          (b'admin', b'pass', b'192.168.1.1', b'80', 'tcp'))
    reporter.add_metadata('other', {b'foo': b'bar', 'biz': 'baz\x00\x01'})
    reporter.output_file(b'data', 'file_1.exe', 'example output file')

    expected_output = u'''
----Standard Metadata----

proxy                admin pass 192.168.1.1 80 tcp
proxy_socketaddress  192.168.1.1:80/tcp
proxy_address        192.168.1.1
socketaddress        192.168.1.1:80/tcp
address              192.168.1.1
port                 80/tcp
credential           admin:pass
username             admin
password             pass

----Other Metadata----

biz                  baz\x00\x01
foo                  bar

----Output Files----

file_1.exe           example output file
                     8d777f385d3dfec8815d20f7496026dc
'''

    assert reporter.get_output_text() == expected_output

    reporter.print_report()
    assert capsys.readouterr().out == expected_output + u'\n'
Beispiel #15
0
def test(testcase_dir, malware_repo, nprocs, update, add, add_filelist, delete,
         yes, show_passed, silent, parser):
    """
    Testing utility to create and execute parser test cases.

    \b
    PARSER: Parsers to test. Test all parers if not provided.

    \b
    Common usages::
        mwcp test                                             - Run all tests cases.
        mwcp test foo                                         - Run test cases for foo parser.
        mwcp test foo -u                                      - Update existing test cases for foo parser.
        mwcp test -u                                          - Update existing test cases for all parsers.
        mwcp test foo --add=./malware.bin                     - Add test case for malware.bin sample for foo parser.
        mwcp test foo --add-filelist=./paths.txt              - Add tests cases for foo parser using text file of paths.
        mwcp test foo --delete=./malware.bin                  - Delete test case for malware.bin sample for foo parser.
    """
    # Configure test object
    reporter = mwcp.Reporter(disable_output_files=True)
    tester = Tester(
        reporter=reporter,
        results_dir=testcase_dir,
        parser_names=parser or [None],
        nprocs=nprocs,
    )

    # Add/Delete
    if add or add_filelist or delete:
        click.echo('Adding new test cases. May take a while...')
        if not parser:
            # Don't allow adding a file to ALL test cases.
            raise click.BadParameter(
                'PARSER 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:
            if malware_repo:
                file_path = _add_to_malware_repo(file_path, malware_repo)
            tester.add_test(file_path)

        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 parser and not yes:
            click.confirm(
                'WARNING: About to update test cases for ALL parsers. Continue?',
                abort=True)
        click.echo('Updating test cases. May take a while...')
        tester.update_tests()

    # Run tests
    else:
        if not parser and not yes:
            click.confirm(
                'PARSER argument not provided. Run tests for ALL parsers?',
                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)
Beispiel #16
0
#!/usr/bin/env python
"""
Simple example to demonstrate use of the API provided by DC3-MWCP framework.
"""

# first, import mwcp
import mwcp

# create an instance of the Reporter class
reporter = mwcp.Reporter()
"""
The Reporter object is the primary DC3-MWCP framework object, containing most input and output data
and controlling execution of the parser modules.

The most common parameters to provide are parserdir and resourcedir, depending upon your installation.
"""
# view location of resource and parser directories
print(reporter.parserdir)

# view available parsers
print(mwcp.get_parser_descriptions())

# run the dummy config parser, view the output
reporter.run_parser("foo", "README.md")

# alternate, run on provided buffer:
reporter.run_parser("foo", data=b"lorem ipsum")

# Print results.
reporter.print_report()
Beispiel #17
0
def static_config_parsers(yara_hit, file_data, cape_config):
    """Process CAPE Yara hits"""
    cape_name = yara_hit.replace('_', ' ')
    parser_loaded = False
    # Attempt to import a parser for the hit
    # DC3-MWCP

    if "cape_config" not in cape_config:
        cape_config.setdefault("cape_config", dict())

    if cape_name and HAS_MWCP and cape_name in malware_parsers:
        try:
            reporter = mwcp.Reporter()

            reporter.run_parser(malware_parsers[cape_name], data=file_data)

            if not reporter.errors:
                log.info("CAPE: Imported DC3-MWCP parser %s", cape_name)
                parser_loaded = True
                try:
                    tmp_dict = dict()
                    if reporter.metadata.get("debug"):
                        del reporter.metadata["debug"]
                    if reporter.metadata.get("other"):
                        for key, value in reporter.metadata["other"].items():
                            tmp_dict.setdefault(key, [])
                            if value not in tmp_dict[key]:
                                tmp_dict[key].append(value)
                        del reporter.metadata["other"]

                    tmp_dict.update(reporter.metadata)

                    if "cape_config" not in cape_config:
                        cape_config.setdefault("cape_config", dict())
                        #ToDo do we really need to convert it?
                        cape_config["cape_config"] = convert(tmp_dict)
                    else:
                        cape_config["cape_config"].update(convert(tmp_dict))
                except Exception as e:
                    log.error(
                        "CAPE: DC3-MWCP config parsing error with {}: {}".
                        format(cape_name, e))
            else:
                error_lines = reporter.errors[0].split("\n")
                for line in error_lines:
                    if line.startswith('ImportError: '):
                        log.info("CAPE: DC3-MWCP parser: %s",
                                 line.split(': ')[1])
            reporter._Reporter__cleanup()
            del reporter
        except (ImportError, IndexError, TypeError) as e:
            log.error("MWCP Error: {}".format(e))

    if not parser_loaded and cape_name in cape_malware_parsers:
        try:
            #changed from cape_config to cape_configraw because of avoiding overridden. duplicated value name.
            cape_configraw = cape_malware_parsers[cape_name].config(file_data)
            if isinstance(cape_configraw, list):
                for (key, value) in cape_configraw[0].items():
                    #python3 map object returns iterator by default, not list and not serializeable in JSON.
                    if isinstance(value, map):
                        value = list(value)
                    cape_config["cape_config"].update({key: [value]})
            elif isinstance(cape_configraw, dict):
                for (key, value) in cape_configraw.items():
                    #python3 map object returns iterator by default, not list and not serializeable in JSON.
                    if isinstance(value, map):
                        value = list(value)
                    cape_config["cape_config"].update({key: [value]})
        except Exception as e:
            log.error("CAPE: parsing error with {}: {}".format(cape_name, e))

    elif not parser_loaded and cape_name in __decoders__:
        try:
            file_info = fileparser.FileParser(rawdata=file_data)
            module = __decoders__[file_info.malware_name]['obj']()
            module.set_file(file_info)
            module.get_config()
            malwareconfig_config = module.config
            #ToDo remove
            if isinstance(malwareconfig_config, list):
                for (key, value) in malwareconfig_config[0].items():
                    cape_config["cape_config"].update({key: [value]})
            elif isinstance(malwareconfig_config, dict):
                for (key, value) in malwareconfig_config.items():
                    cape_config["cape_config"].update({key: [value]})
        except Exception as e:
            log.warning(
                "malwareconfig parsing error with %s: %s, you should submit issue/fix to https://github.com/kevthehermit/RATDecoders/",
                cape_name, e)

        if "cape_config" in cape_config:
            if cape_config["cape_config"] == {}:
                del cape_config["cape_config"]

    return cape_config
Beispiel #18
0
def test(
    testcase_dir, malware_repo, nprocs, update, add, add_filelist, delete, yes, force, show_passed, silent, parser
):
    """
    Testing utility to create and execute parser test cases.

    \b
    PARSER: Parsers to test. Test all parers if not provided.

    \b
    Common usages::
        mwcp test                                             - Run all tests cases.
        mwcp test foo                                         - Run test cases for foo parser.
        mwcp test foo -u                                      - Update existing test cases for foo parser.
        mwcp test -u                                          - Update existing test cases for all parsers.
        mwcp test foo --add=./malware.bin                     - Add test case for malware.bin sample for foo parser.
        mwcp test foo -u --add=./malware.bin                  - Add test case for malware.bin sample.
                                                                Allow updating if a test case for this file already exists.
        mwcp test foo --add-filelist=./paths.txt              - Add tests cases for foo parser using text file of paths.
        mwcp test foo --delete=./malware.bin                  - Delete test case for malware.bin sample for foo parser.
    """
    # Overwrite configuration with command line flags.
    if testcase_dir:
        mwcp.config["TESTCASE_DIR"] = testcase_dir
    if malware_repo:
        mwcp.config["MALWARE_REPO"] = malware_repo

    # Configure test object
    reporter = mwcp.Reporter(disable_output_files=True)
    tester = Tester(reporter=reporter, parser_names=parser or [None], nprocs=nprocs,)

    # Add/Delete
    if add or add_filelist or delete:
        click.echo("Adding new test cases. May take a while...")
        if not parser:
            # Don't allow adding a file to ALL test cases.
            raise click.BadParameter("PARSER 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:
            if mwcp.config.get("MALWARE_REPO"):
                file_path = _add_to_malware_repo(file_path)
            tester.add_test(file_path, force=force, update=update)

        for file_path in delete:
            if mwcp.config.get("MALWARE_REPO"):
                file_path = _get_malware_repo_path(file_path)
            tester.remove_test(file_path)

    # Update
    elif update:
        if not parser and not yes:
            click.confirm("WARNING: About to update test cases for ALL parsers. Continue?", abort=True)
        click.echo("Updating test cases. May take a while...")
        tester.update_tests(force=force)

    # Run tests
    else:
        if not parser and not yes:
            click.confirm("PARSER argument not provided. Run tests for ALL parsers?", 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)
Beispiel #19
0
def static_config_parsers(yara_hit, file_data, cape_config):
    # Process CAPE Yara hits

        cape_name = yara_hit.replace('_', ' ')
        parser_loaded = False
        # Attempt to import a parser for the hit
        # DC3-MWCP

        if "cape_config" not in cape_config:
            cape_config.setdefault("cape_config", dict())

        if cape_name and HAS_MWCP and cape_name in malware_parsers:
            try:
                reporter = mwcp.Reporter()
                reporter.run_parser(malware_parsers[cape_name], data=file_data)
                if reporter.errors == []:
                    log.info("CAPE: Imported DC3-MWCP parser %s", cape_name)
                    parser_loaded = True
                    try:
                        tmp_dict = dict()
                        if reporter.metadata.get("debug"):
                            del reporter.metadata["debug"]
                        if reporter.metadata.get("other"):
                            for key, value in reporter.metadata["other"].items():
                                tmp_dict.setdefault(key, [])
                                if value not in tmp_dict[key]:
                                    tmp_dict[key].append(value)
                            del reporter.metadata["other"]

                        tmp_dict.update(reporter.metadata)

                        if "cape_config" not in cape_config:
                            cape_config.setdefault("cape_config", dict())
                            #ToDo do we really need to convert it?
                            cape_config["cape_config"] = convert(tmp_dict)
                        else:
                            cape_config["cape_config"].update(convert(tmp_dict))
                    except Exception as e:
                        log.error("CAPE: DC3-MWCP config parsing error with %s: %s", cape_name, e)
                else:
                    error_lines = reporter.errors[0].split("\n")
                    for line in error_lines:
                        if line.startswith('ImportError: '):
                            log.info("CAPE: DC3-MWCP parser: %s", line.split(': ')[1])
                reporter._Reporter__cleanup()
                del reporter
            except (ImportError, IndexError) as e:
                log.error(e)

            if not parser_loaded and cape_name in malware_parsers:
                parser_loaded = True
                try:
                    cape_config = malware_parsers[cape_name].config(file_data)
                    if isinstance(cape_config, list):
                        for (key, value) in cape_config[0].items():
                            cape_config["cape_config"].update({key: [value]})
                    elif isinstance(cape_config, dict):
                        for (key, value) in cape_config.items():
                            cape_config["cape_config"].update({key: [value]})
                except Exception as e:
                    log.error("CAPE: parsing error with %s: %s", cape_name, e)

            if not parser_loaded and cape_name in __decoders__:
                try:
                    file_info = fileparser.FileParser(rawdata=file_data)
                    module = __decoders__[file_info.malware_name]['obj']()
                    module.set_file(file_info)
                    module.get_config()
                    malwareconfig_config = module.config
                    #ToDo remove
                    if isinstance(malwareconfig_config, list):
                        for (key, value) in malwareconfig_config[0].items():
                            cape_config["cape_config"].update({key: [value]})
                    elif isinstance(malwareconfig_config, dict):
                        for (key, value) in malwareconfig_config.items():
                            cape_config["cape_config"].update({key: [value]})
                except Exception as e:
                    log.error("CAPE: malwareconfig parsing error with %s: %s", cape_name, e)

            if "cape_config" in cape_config:
                if cape_config["cape_config"] == {}:
                    del cape_config["cape_config"]

        return cape_config
Beispiel #20
0
def get_arg_parser():
    """
    create a option parser to handle command line inputs
    """
    usage_str = 'usage:  %s [options] FILES_DIRS' % (os.path.basename(
        sys.argv[0]))
    description = "DC3-MWCP Framework: utility for executing parser modules"
    parser = argparse.ArgumentParser(
        description=description,
        formatter_class=argparse.RawDescriptionHelpFormatter,
        usage=usage_str)
    default_parserdir = ''

    # Create reporter to get default paths, ignore if this fails
    try:
        default_reporter = mwcp.Reporter()
        default_parserdir = default_reporter.parserdir
    except Exception:
        pass

    parser.add_argument(
        "-p",
        default="",
        type=str,
        dest="parser",
        help=
        "Malware config parser to call. (use ':' notation to specify source if necessary e.g. 'mwcp-acme:Foo')"
    )
    parser.add_argument(
        "-l",
        "--parsers",
        default=0,
        action="count",
        dest="list",
        help=
        "list all malware config parsers. (use -ll to also list component parsers)"
    )
    parser.add_argument(
        "-k",
        "--fields",
        action="store_true",
        default=False,
        dest="fields",
        help=
        "List all standardized fields and examples. See resources/fields.json")
    parser.add_argument("--parserdir",
                        metavar="DIR",
                        default=None,
                        dest="parserdir",
                        help="Optional extra parser directory")
    parser.add_argument(
        "--parserconfig",
        metavar="FILE",
        default=None,
        dest="parserconfig",
        help=
        "Optional parser configuration file to use with extra parser directory."
    )
    parser.add_argument(
        "--parsersource",
        metavar="SOURCE_NAME",
        default=None,
        dest="parsersource",
        help="Set a default parser source to use. "
        "If not provided parsers from all sources will be available.")
    parser.add_argument("-o",
                        "--outputdir",
                        metavar="DIR",
                        default="",
                        dest="outputdir",
                        help="Output directory.")
    parser.add_argument("-c",
                        "--csv",
                        metavar="CSVWRITE",
                        default="",
                        dest="csvwrite",
                        help="Output CSV file.")
    parser.add_argument("-t",
                        "--tempdir",
                        metavar="DIR",
                        default=tempfile.gettempdir(),
                        dest="tempdir",
                        help="Temp directory." +
                        " [default: {}]".format(tempfile.gettempdir()))
    parser.add_argument(
        "-j",
        "--json",
        action="store_true",
        default=False,
        dest="jsonoutput",
        help=
        "Enable json output for parser reports (instead of formatted text).")
    parser.add_argument("-n",
                        "--disable_output",
                        action="store_true",
                        default=False,
                        dest="disableoutputfiles",
                        help="Disable writing output files to filesystem.")
    parser.add_argument(
        "-g",
        "--disable-temp-cleanup",
        action="store_true",
        default=False,
        dest="disabletempcleanup",
        help=
        "Disable cleanup of framework created temp files including managed tempdir."
    )
    parser.add_argument(
        "-f",
        "--include-filename",
        action="store_true",
        default=False,
        dest="includefilename",
        help=
        "Include file information such as filename, hashes, and compile time in parser output."
    )
    # TODO: Determine if we can remove this option. It is conflicting what we call "debug".
    parser.add_argument("--no-debug",
                        action="store_true",
                        default=False,
                        dest="hidedebug",
                        help="Hide debug messages in output.")
    parser.add_argument(
        "-d",
        "--debug",
        action="store_true",
        default=False,
        dest="debug",
        help=
        "Turn on all debugging messages. (WARNING: This WILL spam the console)"
    )
    parser.add_argument(
        "-u",
        "--output-prefix",
        metavar="FILENAME",
        default="",
        dest="outputfile_prefix",
        help=
        "String prepended to output files written to filesystem. Specifying 'md5' will cause "
        +
        "output files to be prefixed with the md5 of the input file. When passing in multiple "
        +
        "files for analysis, the default will be 'md5'. Passing in a value with the -u option "
        +
        "or using the -U option can be used to override the 'md5' default for multiple files. "
        + "[default: (No prefix|md5)]")
    parser.add_argument(
        "-U",
        "--no-output-prefix",
        action="store_true",
        default=False,
        dest="disableoutputfileprefix",
        help=
        "When in effect, parser output files will not have a filename prefix.")
    parser.add_argument(
        "-i",
        "--filelist",
        action="store_true",
        default=False,
        dest="filelistindirection",
        help="Input file contains a list of filenames to process.")
    parser.add_argument(
        "-b",
        "--base64",
        action="store_true",
        default=False,
        dest="base64outputfiles",
        help="Base64 encode output files and include in metadata.")

    return parser
Beispiel #21
0
def main(args=None):
    warnings.warn(
        "WARNING: mwcp-tool is deprecated. Please use \"mwcp parse\" instead.")

    argparser = get_arg_parser()
    args, input_files = argparser.parse_known_args(args)

    # Setup logging
    mwcp.setup_logging()
    if args.hidedebug:
        logging.root.setLevel(logging.WARNING)
    elif args.debug:
        logging.root.setLevel(logging.DEBUG)

    # This is a preliminary check before creating the reporter to establish how output
    # file prefixes should be set.
    if args.disableoutputfileprefix:
        args.outputfile_prefix = ''
    elif args.filelistindirection or len(input_files) > 1 or any(
        [os.path.isdir(x) for x in input_files]):
        args.outputfile_prefix = 'md5'

    if args.fields:
        _print_fields(json_output=args.jsonoutput)
        sys.exit(0)

    # Register parsers
    mwcp.register_entry_points()
    if args.parserdir:
        mwcp.register_parser_directory(args.parserdir,
                                       config_file_path=args.parserconfig)

    if args.parsersource:
        mwcp.set_default_source(args.parsersource)

    if args.list:
        _print_parsers(json_output=args.jsonoutput, config_only=args.list < 2)
        sys.exit(0)

    if not input_files or not args.parser:
        argparser.print_help()
        sys.exit(0)

    file_paths = _get_file_paths(input_files,
                                 is_filelist=args.filelistindirection)

    # Run MWCP
    try:
        reporter = mwcp.Reporter(outputdir=args.outputdir,
                                 outputfile_prefix=args.outputfile_prefix,
                                 tempdir=args.tempdir,
                                 disable_output_files=args.disableoutputfiles,
                                 disable_temp_cleanup=args.disabletempcleanup,
                                 base64_output_files=args.base64outputfiles)
        results = []
        for file_path in file_paths:
            result = _parse_file(reporter,
                                 file_path,
                                 args.parser,
                                 include_filename=args.includefilename)
            results.append(result)
            if not args.jsonoutput:
                reporter.print_report()

        if args.csvwrite:
            csv_path = args.csvwrite
            _write_csv(file_paths, results, csv_path, args.base64outputfiles)
            if not args.jsonoutput:
                print('Wrote csv file: {}'.format(csv_path))

        if args.jsonoutput:
            print(json.dumps(results, indent=4))

    except Exception as e:
        error_message = "Error running DC3-MWCP: {}".format(e)
        traceback.print_exc()
        if args.jsonoutput:
            print(json.dumps({'errors': [error_message]}))
        else:
            print(error_message)
        sys.exit(1)
Beispiel #22
0
def main():
    """Run tool."""

    print('')

    # Get command line arguments
    argparser = get_arg_parser()
    args, input_files = argparser.parse_known_args()

    # Setup logging
    mwcp.setup_logging()
    logging.root.setLevel(logging.WARNING - (args.verbose * 10))

    if args.all_tests or not args.parser_name:
        parsers = [None]
    else:
        parsers = [args.parser_name]

    # Configure reporter based on args
    reporter = mwcp.Reporter(disableoutputfiles=True, parserdir=args.parserdir)

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

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

    # Run test cases
    if args.run_tests:
        print("Running test cases. May take a while...")

        start_time = timeit.default_timer()
        test_infos = []
        test_results = []
        # json_list = []
        all_passed = True

        test_iter = tester.run_tests(
            parsers,
            list(filter(None, args.field_names.split(","))),
            ignore_field_names=list(
                filter(None, args.exclude_field_names.split(","))),
            nprocs=args.nprocs)

        for test_result, test_info in test_iter:
            test_infos.append(test_info)
            test_results.append(test_result)
            all_passed &= test_result.passed

            if not args.silent:
                # Skip print() to immediately flush stdout buffer (issue in Docker containers)
                sys.stdout.write(
                    "{finished}/{total} - {parser} {filename} {run_time:.4f}s\n"
                    .format(**test_info))
                sys.stdout.flush()
                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)

        end_time = timeit.default_timer()

        # Avoid a ZeroDivisionError.
        if not test_infos:
            return

        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['parser'],
                                                   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['parser'],
                                                   info['filename'],
                                                   info['run_time']))

            run_times = [info['run_time'] for info in test_infos]
            print('\nMean Running Time: {:.4f}s'.format(
                sum(run_times) / len(test_infos)))
            print('Median Running Time: {:.4f}s'.format(_median(run_times)))
            print('Cumulative Running Time: {}'.format(
                datetime.timedelta(seconds=sum(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)

    # Delete files from test cases
    elif args.delete:
        removed_files = tester.remove_test_results(args.parser_name,
                                                   input_files)
        for filename in removed_files:
            print(u"Removing results for {} in {}".format(
                filename, tester.get_results_filepath(args.parser_name)))

    # Update previously existing test cases
    elif args.update and args.parser_name:
        logging.root.setLevel(
            logging.INFO
        )  # Force info level logs so test cases stay consistent.
        print("Updating test cases. May take a while...")
        results_file_path = tester.get_results_filepath(args.parser_name)
        if os.path.isfile(results_file_path):
            input_files = tester.list_test_files(args.parser_name)
        else:
            print(
                "No test case file found for parser '{}'. No update could be made."
                .format(args.parser_name))
            return

        for input_file in input_files:
            metadata = tester.gen_results(parser_name=args.parser_name,
                                          input_file_path=input_file)
            if len(metadata) > 1 and len(reporter.errors) == 0:
                print(u"Updating results for {} in {}".format(
                    input_file, results_file_path))
                tester.update_test_results(results_file_path=results_file_path,
                                           results_data=metadata,
                                           replace=True)
            elif len(metadata) > 1 and len(reporter.errors) > 0:
                print(u"Error occurred for {} in {}, not updating".format(
                    input_file, results_file_path))
                print('\n'.join(reporter.errors))
            else:
                print(u"Empty results for {} in {}, not updating".format(
                    input_file, results_file_path))

    # Add/update test cases for specified input files and specified parser
    elif args.parser_name and not args.delete and input_files:
        logging.root.setLevel(
            logging.INFO
        )  # Force info level logs so test cases stay consistent.
        results_file_path = tester.get_results_filepath(args.parser_name)
        for input_file in input_files:
            metadata = tester.gen_results(parser_name=args.parser_name,
                                          input_file_path=input_file)
            if len(metadata) > 1 and len(reporter.errors) == 0:
                print(u"Updating results for {} in {}".format(
                    input_file, results_file_path))
                tester.update_test_results(results_file_path=results_file_path,
                                           results_data=metadata,
                                           replace=True)
            elif len(metadata) > 1 and len(reporter.errors) > 0:
                print(u"Error occurred for {} in {}, not updating".format(
                    input_file, results_file_path))
            else:
                print(u"Empty results for {} in {}, not updating".format(
                    input_file, results_file_path))
    else:
        argparser.print_help()
Beispiel #23
0
def main(args=None):
    argparser = get_arg_parser()
    args, input_files = argparser.parse_known_args(args)

    # This is a preliminary check before creating the reporter to establish how output
    # file prefixes should be set.
    if args.disableoutputfileprefix:
        args.outputfile_prefix = ''
    elif args.filelistindirection or len(input_files) > 1 or any(
        [os.path.isdir(x) for x in input_files]):
        args.outputfile_prefix = 'md5'

    if args.list:
        if args.parserdir:
            mwcp.register_parser_directory(args.parserdir)
        _print_parsers(json_output=args.jsonoutput)
        sys.exit(0)

    if args.fields:
        _print_fields(json_output=args.jsonoutput)
        sys.exit(0)

    if not input_files or not args.parser:
        argparser.print_help()
        sys.exit(0)

    file_paths = _get_file_paths(input_files,
                                 is_filelist=args.filelistindirection)

    kwargs = {}
    if args.kwargs_raw:
        kwargs = dict(json.loads(args.kwargs_raw))
        for key, value in list(kwargs.items()):
            if value and value.startswith('b64file(') and value.endswith(')'):
                tmp_filename = value[len('b64file('):-1]
                with open(tmp_filename, 'rb') as f:
                    kwargs[key] = base64.b64encode(f.read())

    # Run MWCP
    try:
        reporter = mwcp.Reporter(parserdir=args.parserdir,
                                 outputdir=args.outputdir,
                                 outputfile_prefix=args.outputfile_prefix,
                                 tempdir=args.tempdir,
                                 disabledebug=args.hidedebug,
                                 disableoutputfiles=args.disableoutputfiles,
                                 disabletempcleanup=args.disabletempcleanup,
                                 base64outputfiles=args.base64outputfiles)
        results = []
        for file_path in file_paths:
            result = _parse_file(reporter,
                                 file_path,
                                 args.parser,
                                 options=kwargs,
                                 include_filename=args.includefilename)
            results.append(result)
            if not args.jsonoutput:
                reporter.print_report()

        if args.csvwrite:
            csv_path = args.csvwrite
            _write_csv(input_files, results, csv_path, args.base64outputfiles)
            if not args.jsonoutput:
                print('Wrote csv file: {}'.format(csv_path))

        if args.jsonoutput:
            print(json.dumps(results, indent=4))

    except Exception as e:
        error_message = "Error running DC3-MWCP: {}".format(e)
        traceback.print_exc()
        if args.jsonoutput:
            print(json.dumps({'errors': [error_message]}))
        else:
            print(error_message)
        sys.exit(1)
Beispiel #24
0
def main():
    """Run tool."""

    warnings.warn("WARNING: mwcp-test is deprecated. Please use \"mwcp test\" instead.")

    print('')

    # Get command line arguments
    argparser = get_arg_parser()
    args, input_files = argparser.parse_known_args()

    # Setup logging
    mwcp.setup_logging()
    logging.root.setLevel(logging.ERROR - (args.verbose * 10))

    if args.all_tests or not args.parser_name:
        parsers = [None]
    else:
        parsers = [args.parser_name]

    if args.parserdir and args.parserconfig:
        mwcp.register_parser_directory(args.parserdir, args.parserconfig)
    elif args.parserdir or args.parserconfig:
        raise ValueError('Both --parserdir and --parserconfig must be specified.')
    else:
        mwcp.register_entry_points()

    if args.parsersource:
        mwcp.set_default_source(args.parsersource)

    # Configure reporter based on args
    reporter = mwcp.Reporter(disable_output_files=True)

    # Configure test object
    tester = Tester(
        reporter=reporter, results_dir=args.test_case_dir, parser_names=parsers, nprocs=args.nprocs,
        field_names=filter(None, args.field_names.split(",")),
        ignore_field_names=filter(None, args.exclude_field_names.split(","))
    )

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

    # Delete files from test cases
    if args.delete:
        removed_files = tester.remove_test_results(
            args.parser_name, input_files)
        for filename in removed_files:
            print(u"Removing results for {} in {}".format(
                filename, tester.get_results_filepath(args.parser_name)))

    # Update previously existing test cases
    elif args.update and args.parser_name:
        print("Updating test cases. May take a while...")
        results_file_path = tester.get_results_filepath(args.parser_name)
        if os.path.isfile(results_file_path):
            input_files = tester.list_test_files(args.parser_name)
        else:
            sys.exit(u"No test case file found for parser '{}'. "
                     u"No update could be made.".format(args.parser_name))
        update_tests(tester, input_files, args.parser_name)

    # Add/update test cases for specified input files and specified parser
    elif args.parser_name and not args.delete and input_files:
        update_tests(tester, input_files, args.parser_name)

    # Run test cases
    else:
        print("Running test cases. May take a while...")

        start_time = timeit.default_timer()
        test_results = []
        all_passed = True
        total = tester.total
        failed = []

        # Generate format string.
        digits = len(str(total))
        if not tester.test_cases:
            parser_len = 10
            filename_len = 10
        else:
            parser_len = max(len(test_case.parser_name) for test_case in tester.test_cases)
            filename_len = max(len(test_case.filename) for test_case in tester.test_cases)
        msg_format = "{{parser:{0}}} {{filename:{1}}} {{run_time:.4f}}s".format(parser_len, filename_len)

        format_str = "{{count:> {0}d}}/{{total:0{0}d}} - ".format(digits) + msg_format

        # Run tests and output progress results.
        for count, test_result in enumerate(tester, start=1):
            all_passed &= test_result.passed
            if not test_result.passed:
                failed.append((count, test_result.parser_name, test_result.filename))

            if test_result.run_time:  # Ignore missing tests from stat summary.
                test_results.append(test_result)

            if not args.silent:
                message = format_str.format(
                    count=count,
                    total=total,
                    parser=test_result.parser_name,
                    filename=test_result.filename,
                    run_time=test_result.run_time
                )
                # Skip print() to immediately flush stdout buffer (issue in Docker containers)
                sys.stdout.write(message + '\n')
                sys.stdout.flush()
                test_result.print(
                    failed_tests=True, passed_tests=not args.only_failed_tests, json_format=args.json
                )

        end_time = timeit.default_timer()

        # Present test statistics
        if not args.silent and test_results:
            print('\nTest stats:')
            print('\nTop 10 Slowest Test Cases:')

            format_str = "{index:2}. " + msg_format

            # Cases sorted slowest first
            sorted_cases = sorted(test_results, key=lambda x: x.run_time, reverse=True)
            for i, test_result in enumerate(sorted_cases[:10], start=1):
                print(format_str.format(
                    index=i,
                    parser=test_result.parser_name,
                    filename=test_result.filename,
                    run_time=test_result.run_time
                ))

            print('\nTop 10 Fastest Test Cases:')
            for i, test_result in enumerate(list(reversed(sorted_cases))[:10], start=1):
                print(format_str.format(
                    index=i,
                    parser=test_result.parser_name,
                    filename=test_result.filename,
                    run_time=test_result.run_time
                ))

            run_times = [test_result.run_time for test_result in test_results]
            print('\nMean Running Time: {:.4f}s'.format(
                sum(run_times) / len(test_results)
            ))
            print('Median Running Time: {:.4f}s'.format(
                _median(run_times)
            ))
            print('Cumulative Running Time: {}'.format(datetime.timedelta(seconds=sum(run_times))))
            print()

        print("Total Running Time: {}".format(datetime.timedelta(seconds=end_time - start_time)))

        if failed:
            print()
            print("Failed tests:")
            for test_info in failed:
                print("#{} - {}\t{}".format(*test_info))
            print()

        print("All Passed = {0}\n".format(all_passed))
        exit(0 if all_passed else 1)
Beispiel #25
0
def static_config_parsers(yara_hit, file_data):
    """Process CAPE Yara hits"""

    cape_name = yara_hit.replace("_", " ")
    cape_config = dict()
    cape_config[cape_name] = dict()
    parser_loaded = False
    # Attempt to import a parser for the hit
    # DC3-MWCP
    if HAS_MWCP and cape_name and cape_name in malware_parsers:
        logging.debug("Running MWCP")
        try:
            reporter = mwcp.Reporter()
            reporter.run_parser(malware_parsers[cape_name], data=file_data)
            if not reporter.errors:
                parser_loaded = True
                tmp_dict = dict()
                if reporter.metadata.get("debug"):
                    del reporter.metadata["debug"]
                if reporter.metadata.get("other"):
                    for key, value in reporter.metadata["other"].items():
                        tmp_dict.setdefault(key, [])
                        if value not in tmp_dict[key]:
                            tmp_dict[key].append(value)
                    del reporter.metadata["other"]

                tmp_dict.update(reporter.metadata)
                cape_config[cape_name] = convert(tmp_dict)
                logging.debug("CAPE: DC3-MWCP parser for %s completed", cape_name)
            else:
                error_lines = reporter.errors[0].split("\n")
                for line in error_lines:
                    if line.startswith("ImportError: "):
                        logging.debug("CAPE: DC3-MWCP parser: %s", line.split(": ")[1])
            reporter._Reporter__cleanup()
            del reporter
        except pefile.PEFormatError:
            logging.error("pefile PEFormatError")
        except Exception as e:
            logging.error("CAPE: DC3-MWCP config parsing error with {}: {}".format(cape_name, e))

    if HAVE_CAPE_EXTRACTORS and not parser_loaded and cape_name in cape_malware_parsers:
        logging.debug("Running CAPE")
        try:
            # changed from cape_config to cape_configraw because of avoiding overridden. duplicated value name.
            cape_configraw = cape_malware_parsers[cape_name].config(file_data)
            if isinstance(cape_configraw, list):
                for (key, value) in cape_configraw[0].items():
                    # python3 map object returns iterator by default, not list and not serializeable in JSON.
                    if isinstance(value, map):
                        value = list(value)
                    cape_config[cape_name].update({key: [value]})
                parser_loaded = True
            elif isinstance(cape_configraw, dict):
                for (key, value) in cape_configraw.items():
                    # python3 map object returns iterator by default, not list and not serializeable in JSON.
                    if isinstance(value, map):
                        value = list(value)
                    cape_config[cape_name].update({key: [value]})
                parser_loaded = True
        except Exception as e:
            logging.error("CAPE: parsing error with {}: {}".format(cape_name, e))

    elif HAS_MALWARECONFIGS and not parser_loaded and cape_name in __decoders__:
        logging.debug("Running Malwareconfigs")
        try:
            module = False
            file_info = fileparser.FileParser(rawdata=file_data)
            # Detects name by embed yara
            if file_info.malware_name in __decoders__:
                module = __decoders__[file_info.malware_name]["obj"]()
            elif cape_name in __decoders__:
                module = __decoders__[cape_name]["obj"]()
            else:
                logging.warning(f"{cape_name}: wasn't matched by plugin's yara")

            if module:
                module.set_file(file_info)
                module.get_config()
                malwareconfig_config = module.config
                # ToDo remove
                if isinstance(malwareconfig_config, list):
                    for (key, value) in malwareconfig_config[0].items():
                        cape_config[cape_name].update({key: [value]})
                elif isinstance(malwareconfig_config, dict):
                    for (key, value) in malwareconfig_config.items():
                        cape_config[cape_name].update({key: [value]})
        except Exception as e:
            if "rules" in str(e):
                logging.warning("You probably need to compile yara-python with dotnet support")
            else:
                logging.error(e, exc_info=True)
                logging.warning(
                    "malwareconfig parsing error with %s: %s, you should submit issue/fix to https://github.com/kevthehermit/RATDecoders/",
                    cape_name,
                    e,
            )

        if cape_name in cape_config and cape_config[cape_name] == {}:
            return {}

    elif HAVE_MALDUCK and not parser_loaded and cape_name.lower() in malduck_modules_names:
        logging.debug("Running Malduck")
        if not File.yara_initialized:
            init_yara()
        # placing here due to not load yara in not related tools
        malduck_rules.rules = File.yara_rules["CAPE"]
        malduck_modules.rules = malduck_rules
        ext = ExtractManager.__new__(ExtractManager)
        ext.configs = {}
        ext.modules = malduck_modules
        tmp_file = tempfile.NamedTemporaryFile(delete=False)
        tmp_file.write(file_data)
        ext.push_file(tmp_file.name)
        tmp_file.close()

        tmp_config = ext.config
        del ext
        if tmp_config:
            for (key, value) in tmp_config[0].items():
                cape_config[cape_name].update({key: [value]})

    if not cape_config[cape_name]:
        return dict()

    return cape_config
Beispiel #26
0
def test_add_metadata(key, value, expected):
    reporter = mwcp.Reporter()
    reporter.add_metadata(key, value)
    assert reporter.metadata == expected
Beispiel #27
0
def main():
    """Run tool."""

    print('')

    # Get command line arguments
    argparser = get_arg_parser()
    args, input_files = argparser.parse_known_args()

    if args.all_tests or not args.parser_name:
        parsers = [None]
    else:
        parsers = [args.parser_name]

    # Configure reporter based on args
    reporter = mwcp.Reporter(disableoutputfiles=True, parserdir=args.parserdir)

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

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

    # Run test cases
    if args.run_tests:
        print("Running test cases. May take a while...")

        # Run tests
        test_results = tester.run_tests(
            parsers,
            list(filter(None, args.field_names.split(","))),
            ignore_field_names=list(
                filter(None, args.exclude_field_names.split(","))))

        # Determine if any test cases failed
        all_passed = True
        if any([not test_result.passed for test_result in test_results]):
            all_passed = False
        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:
            sys.exit(0)
        else:
            sys.exit(1)

    # Delete files from test cases
    elif args.delete:
        removed_files = tester.remove_test_results(args.parser_name,
                                                   input_files)
        for filename in removed_files:
            print(u"Removing results for {} in {}".format(
                filename, tester.get_results_filepath(args.parser_name)))

    # Update previously existing test cases
    elif args.update and args.parser_name:
        print("Updating test cases. May take a while...")
        results_file_path = tester.get_results_filepath(args.parser_name)
        if os.path.isfile(results_file_path):
            input_files = tester.list_test_files(args.parser_name)
        else:
            print(
                "No test case file found for parser '{}'. No update could be made."
                .format(args.parser_name))
            return

        for input_file in input_files:
            metadata = tester.gen_results(parser_name=args.parser_name,
                                          input_file_path=input_file)
            if len(metadata) > 1 and len(reporter.errors) == 0:
                print(u"Updating results for {} in {}".format(
                    input_file, results_file_path))
                tester.update_test_results(results_file_path=results_file_path,
                                           results_data=metadata,
                                           replace=True)
            elif len(metadata) > 1 and len(reporter.errors) > 0:
                print(u"Error occurred for {} in {}, not updating".format(
                    input_file, results_file_path))
                print('\n'.join(reporter.errors))
            else:
                print(u"Empty results for {} in {}, not updating".format(
                    input_file, results_file_path))

    # Add/update test cases for specified input files and specified parser
    elif args.parser_name and not args.delete and input_files:
        results_file_path = tester.get_results_filepath(args.parser_name)
        for input_file in input_files:
            metadata = tester.gen_results(parser_name=args.parser_name,
                                          input_file_path=input_file)
            if len(metadata) > 1 and len(reporter.errors) == 0:
                print(u"Updating results for {} in {}".format(
                    input_file, results_file_path))
                tester.update_test_results(results_file_path=results_file_path,
                                           results_data=metadata,
                                           replace=True)
            elif len(metadata) > 1 and len(reporter.errors) > 0:
                print(u"Error occurred for {} in {}, not updating".format(
                    input_file, results_file_path))
            else:
                print(u"Empty results for {} in {}, not updating".format(
                    input_file, results_file_path))
    else:
        argparser.print_help()
Beispiel #28
0
def test_managed_tempdir(tmpdir):
    reporter = mwcp.Reporter(tempdir=str(tmpdir))
    managed_tempdir = reporter.managed_tempdir
    assert os.path.exists(managed_tempdir)
    assert managed_tempdir.startswith(
        os.path.join(str(tmpdir), 'mwcp-managed_tempdir-'))