# run this script from within IDA with ./tests/data/mimikatz.exe open import logging import binascii import traceback import collections import pytest from fixtures import * import capa.features import capa.features.file import capa.features.insn import capa.features.basicblock from capa.features import ARCH_X32, ARCH_X64 logger = logging.getLogger("test_ida_features") def check_input_file(wanted): import idautils # some versions (7.4) of IDA return a truncated version of the MD5. # https://github.com/idapython/bin/issues/11 try: found = idautils.GetInputFileMD5()[:31].decode("ascii").lower() except UnicodeDecodeError: # in IDA 7.5 or so, GetInputFileMD5 started returning raw binary # rather than the hex digest found = binascii.hexlify(idautils.GetInputFileMD5()[:15]).decode("ascii").lower() if not wanted.startswith(found): raise RuntimeError("please run the tests against `mimikatz.exe`") def get_ida_extractor(_path): check_input_file("5f66b82558ca92e54e77f216ef4c066c") # have to import import this inline so pytest doesn't bail outside of IDA import capa.features.extractors.ida return capa.features.extractors.ida.IdaFeatureExtractor() @pytest.mark.skip(reason="IDA Pro tests must be run within IDA") def test_ida_features(): for (sample, scope, feature, expected) in FEATURE_PRESENCE_TESTS: # resolve sample # resolve scope pass id = make_test_id((sample, scope, feature, expected)) try: do_test_feature_presence(get_ida_extractor, sample, scope, feature, expected) except AssertionError as e: print("FAIL %s" % (id)) traceback.print_exc() else: print("OK %s" % (id)) @pytest.mark.skip(reason="IDA Pro tests must be run within IDA") @parametrize( "sample,scope,feature,expected", FEATURE_COUNT_TESTS, indirect=["sample", "scope"], ) def test_ida_feature_counts(sample, scope, feature, expected): do_test_feature_count(get_ida_extractor, sample, scope, feature, expected) if __name__ == "__main__": print("-" * 80) # invoke all functions in this module that start with `test_` for name in dir(sys.modules[__name__]): if not name.startswith("test_"): continue test = getattr(sys.modules[__name__], name) logger.debug("invoking test: %s", name) sys.stderr.flush() try: test() except AssertionError as e: print("FAIL %s" % (name)) traceback.print_exc() else: print("OK %s" % (name))