mirror of
https://github.com/mandiant/capa.git
synced 2025-12-22 23:26:21 -08:00
@@ -14,9 +14,23 @@ import capa.features.basicblock
|
||||
logger = logging.getLogger("test_ida_features")
|
||||
|
||||
|
||||
def check_input_file():
|
||||
import idautils
|
||||
|
||||
wanted = "5f66b82558ca92e54e77f216ef4c066c"
|
||||
# some versions of IDA return a truncated version of the MD5.
|
||||
# https://github.com/idapython/bin/issues/11
|
||||
found = idautils.GetInputFileMD5().rstrip(b"\x00").decode("ascii").lower()
|
||||
if not wanted.startswith(found):
|
||||
raise RuntimeError("please run the tests against `mimikatz.exe`")
|
||||
|
||||
|
||||
def get_extractor():
|
||||
check_input_file()
|
||||
|
||||
# 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()
|
||||
|
||||
|
||||
@@ -57,6 +71,7 @@ def extract_basic_block_features(f, bb):
|
||||
def test_api_features():
|
||||
# have to import import this inline so pytest doesn't bail outside of IDA
|
||||
import idaapi
|
||||
|
||||
f = idaapi.get_func(0x403BAC)
|
||||
features = extract_function_features(f)
|
||||
assert capa.features.insn.API("advapi32.CryptAcquireContextW") in features
|
||||
@@ -74,6 +89,7 @@ def test_api_features():
|
||||
@pytest.mark.skip(reason="IDA Pro tests must be run within IDA")
|
||||
def test_string_features():
|
||||
import idaapi
|
||||
|
||||
f = idaapi.get_func(0x40105D)
|
||||
features = extract_function_features(f)
|
||||
assert capa.features.String("SCardControl") in features
|
||||
@@ -86,9 +102,10 @@ def test_string_features():
|
||||
@pytest.mark.skip(reason="IDA Pro tests must be run within IDA")
|
||||
def test_byte_features():
|
||||
import idaapi
|
||||
|
||||
f = idaapi.get_func(0x40105D)
|
||||
features = extract_function_features(f)
|
||||
wanted = capa.features.Bytes("SCardControl".encode('utf-16le'))
|
||||
wanted = capa.features.Bytes("SCardControl".encode("utf-16le"))
|
||||
# use `==` rather than `is` because the result is not `True` but a truthy value.
|
||||
assert wanted.evaluate(features) == True
|
||||
|
||||
@@ -96,6 +113,7 @@ def test_byte_features():
|
||||
@pytest.mark.skip(reason="IDA Pro tests must be run within IDA")
|
||||
def test_number_features():
|
||||
import idaapi
|
||||
|
||||
f = idaapi.get_func(0x40105D)
|
||||
features = extract_function_features(f)
|
||||
assert capa.features.insn.Number(0xFF) in features
|
||||
@@ -108,6 +126,7 @@ def test_number_features():
|
||||
@pytest.mark.skip(reason="IDA Pro tests must be run within IDA")
|
||||
def test_offset_features():
|
||||
import idaapi
|
||||
|
||||
f = idaapi.get_func(0x40105D)
|
||||
features = extract_function_features(f)
|
||||
assert capa.features.insn.Offset(0x0) in features
|
||||
@@ -129,6 +148,7 @@ def test_offset_features():
|
||||
@pytest.mark.skip(reason="IDA Pro tests must be run within IDA")
|
||||
def test_nzxor_features():
|
||||
import idaapi
|
||||
|
||||
f = idaapi.get_func(0x410DFC)
|
||||
features = extract_function_features(f)
|
||||
assert capa.features.Characteristic("nzxor") in features # 0x0410F0B
|
||||
@@ -137,6 +157,7 @@ def test_nzxor_features():
|
||||
@pytest.mark.skip(reason="IDA Pro tests must be run within IDA")
|
||||
def test_mnemonic_features():
|
||||
import idaapi
|
||||
|
||||
f = idaapi.get_func(0x40105D)
|
||||
features = extract_function_features(f)
|
||||
assert capa.features.insn.Mnemonic("push") in features
|
||||
@@ -158,6 +179,7 @@ def test_file_section_name_features():
|
||||
@pytest.mark.skip(reason="IDA Pro tests must be run within IDA")
|
||||
def test_tight_loop_features():
|
||||
import idaapi
|
||||
|
||||
extractor = get_extractor()
|
||||
f = idaapi.get_func(0x402EC4)
|
||||
for bb in extractor.get_basic_blocks(f):
|
||||
@@ -171,6 +193,7 @@ def test_tight_loop_features():
|
||||
@pytest.mark.skip(reason="IDA Pro tests must be run within IDA")
|
||||
def test_tight_loop_bb_features():
|
||||
import idaapi
|
||||
|
||||
extractor = get_extractor()
|
||||
f = idaapi.get_func(0x402EC4)
|
||||
for bb in extractor.get_basic_blocks(f):
|
||||
@@ -195,6 +218,7 @@ def test_file_import_name_features():
|
||||
@pytest.mark.skip(reason="IDA Pro tests must be run within IDA")
|
||||
def test_stackstring_features():
|
||||
import idaapi
|
||||
|
||||
f = idaapi.get_func(0x4556E5)
|
||||
features = extract_function_features(f)
|
||||
assert capa.features.Characteristic("stack string") in features
|
||||
@@ -203,6 +227,7 @@ def test_stackstring_features():
|
||||
@pytest.mark.skip(reason="IDA Pro tests must be run within IDA")
|
||||
def test_switch_features():
|
||||
import idaapi
|
||||
|
||||
f = idaapi.get_func(0x409411)
|
||||
features = extract_function_features(f)
|
||||
assert capa.features.Characteristic("switch") in features
|
||||
@@ -215,15 +240,22 @@ def test_switch_features():
|
||||
@pytest.mark.skip(reason="IDA Pro tests must be run within IDA")
|
||||
def test_function_calls_to():
|
||||
import idaapi
|
||||
|
||||
# this function is used in a function pointer
|
||||
f = idaapi.get_func(0x4011FB)
|
||||
features = extract_function_features(f)
|
||||
assert capa.features.Characteristic("calls to") in features
|
||||
assert capa.features.Characteristic("calls to") not in features
|
||||
|
||||
# __FindPESection is called once
|
||||
f = idaapi.get_func(0x470360)
|
||||
features = extract_function_features(f)
|
||||
assert len(features[capa.features.Characteristic("calls to")]) == 1
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="IDA Pro tests must be run within IDA")
|
||||
def test_function_calls_from():
|
||||
import idaapi
|
||||
|
||||
f = idaapi.get_func(0x4011FB)
|
||||
features = extract_function_features(f)
|
||||
assert capa.features.Characteristic("calls from") in features
|
||||
@@ -233,6 +265,7 @@ def test_function_calls_from():
|
||||
@pytest.mark.skip(reason="IDA Pro tests must be run within IDA")
|
||||
def test_basic_block_count():
|
||||
import idaapi
|
||||
|
||||
f = idaapi.get_func(0x4011FB)
|
||||
features = extract_function_features(f)
|
||||
assert len(features[capa.features.basicblock.BasicBlock()]) == 15
|
||||
@@ -243,11 +276,11 @@ if __name__ == "__main__":
|
||||
|
||||
# invoke all functions in this module that start with `parse_`
|
||||
for name in dir(sys.modules[__name__]):
|
||||
if not name.startswith('test_'):
|
||||
if not name.startswith("test_"):
|
||||
continue
|
||||
|
||||
test = getattr(sys.modules[__name__], name)
|
||||
logger.debug('invoking test: %s', name)
|
||||
logger.debug("invoking test: %s", name)
|
||||
sys.stderr.flush()
|
||||
try:
|
||||
test()
|
||||
@@ -255,4 +288,4 @@ if __name__ == "__main__":
|
||||
print("FAIL %s" % (name))
|
||||
traceback.print_exc()
|
||||
else:
|
||||
print("OK %s" % (name))
|
||||
print("OK %s" % (name))
|
||||
|
||||
Reference in New Issue
Block a user