diff --git a/tests/fixtures.py b/tests/fixtures.py index 0dd6ea59..5b2f4efd 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -256,6 +256,8 @@ def get_data_path_by_name(name) -> Path: return CD / "data" / "499c2a85f6e8142c3f48d4251c9c7cd6.raw32" elif name.startswith("9324d"): return CD / "data" / "9324d1a8ae37a36ae560c37448c9705a.exe_" + elif name.startswith("395eb"): + return CD / "data" / "395eb0ddd99d2c9e37b6d0b73485ee9c.exe_" elif name.startswith("a1982"): return CD / "data" / "a198216798ca38f280dc413f8c57f2c2.exe_" elif name.startswith("a933a"): @@ -1102,6 +1104,11 @@ def z9324d_extractor(): return get_extractor(get_data_path_by_name("9324d...")) +@pytest.fixture +def z395eb_extractor(): + return get_extractor(get_data_path_by_name("395eb...")) + + @pytest.fixture def pma12_04_extractor(): return get_extractor(get_data_path_by_name("pma12-04")) diff --git a/tests/test_main.py b/tests/test_main.py index 278bc729..c6deb173 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -303,6 +303,30 @@ def test_byte_matching(z9324d_extractor): assert "byte match test" in capabilities +def test_com_feature_matching(z395eb_extractor): + rules = capa.rules.RuleSet( + [ + capa.rules.Rule.from_yaml( + textwrap.dedent( + """ + rule: + meta: + name: initialize IWebBrowser2 + scope: basic block + features: + - and: + - api: ole32.CoCreateInstance + - com/class: InternetExplorer #bytes: 01 DF 02 00 00 00 00 00 C0 00 00 00 00 00 00 46 = CLSID_InternetExplorer + - com/interface: IWebBrowser2 #bytes: 61 16 0C D3 AF CD D0 11 8A 3E 00 C0 4F C9 E2 6E = IID_IWebBrowser2 + """ + ) + ) + ] + ) + capabilities, meta = capa.main.find_capabilities(rules, z395eb_extractor) + assert "initialize IWebBrowser2" in capabilities + + def test_count_bb(z9324d_extractor): rules = capa.rules.RuleSet( [ diff --git a/tests/test_rules.py b/tests/test_rules.py index 024a40d3..f91a3214 100644 --- a/tests/test_rules.py +++ b/tests/test_rules.py @@ -1003,3 +1003,73 @@ def test_property_access_symbol(): ) is True ) + + +def test_translate_com_features(): + r = capa.rules.Rule.from_yaml( + textwrap.dedent( + """ + rule: + meta: + name: test rule + features: + - com/class: WICPngDecoder + # 389ea17b-5078-4cde-b6ef-25c15175c751 WICPngDecoder + # e018945b-aa86-4008-9bd4-6777a1e40c11 WICPngDecoder + """ + ) + ) + com_name = "WICPngDecoder" + com_features = [ + capa.features.common.Bytes(b"{\xa1\x9e8xP\xdeL\xb6\xef%\xc1Qu\xc7Q", f"{com_name} as bytes"), + capa.features.common.StringFactory("389ea17b-5078-4cde-b6ef-25c15175c751", f"{com_name} as guid string"), + capa.features.common.Bytes(b"[\x94\x18\xe0\x86\xaa\x08@\x9b\xd4gw\xa1\xe4\x0c\x11", f"{com_name} as bytes"), + capa.features.common.StringFactory("e018945b-aa86-4008-9bd4-6777a1e40c11", f"{com_name} as guid string"), + ] + for child in r.statement.get_children(): + assert child in com_features + + +def test_invalid_com_features(): + # test for unknown COM class + with pytest.raises(capa.rules.InvalidRule): + _ = capa.rules.Rule.from_yaml( + textwrap.dedent( + """ + rule: + meta: + name: test rule + features: + - com/class: invalid_com + """ + ) + ) + + # test for unknown COM interface + with pytest.raises(capa.rules.InvalidRule): + _ = capa.rules.Rule.from_yaml( + textwrap.dedent( + """ + rule: + meta: + name: test rule + features: + - com/interface: invalid_com + """ + ) + ) + + # test for invalid COM type + # valid_com_types = "class", "interface" + with pytest.raises(capa.rules.InvalidRule): + _ = capa.rules.Rule.from_yaml( + textwrap.dedent( + """ + rule: + meta: + name: test rule + features: + - com/invalid_COM_type: WICPngDecoder + """ + ) + )