# run this script from within IDA with ./tests/data/mimikatz.exe open import sys import logging import os.path import binascii import traceback import pytest try: sys.path.append(os.path.dirname(__file__)) from fixtures import * finally: sys.path.pop() 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 sample with MD5: `%s`" % (wanted)) 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 + FEATURE_PRESENCE_TESTS_IDA: id = make_test_id((sample, scope, feature, expected)) try: check_input_file(get_sample_md5_by_name(sample)) except RuntimeError: print("SKIP %s" % (id)) continue scope = resolve_scope(scope) sample = resolve_sample(sample) try: do_test_feature_presence(get_ida_extractor, sample, scope, feature, expected) except Exception 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") def test_ida_feature_counts(): for (sample, scope, feature, expected) in FEATURE_COUNT_TESTS: id = make_test_id((sample, scope, feature, expected)) try: check_input_file(get_sample_md5_by_name(sample)) except RuntimeError: print("SKIP %s" % (id)) continue scope = resolve_scope(scope) sample = resolve_sample(sample) try: do_test_feature_count(get_ida_extractor, sample, scope, feature, expected) except Exception as e: print("FAIL %s" % (id)) traceback.print_exc() else: print("OK %s" % (id)) 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() test() print("DONE")