From e47635455ef2864b3455e18dd0239f0a83e75045 Mon Sep 17 00:00:00 2001 From: mr-tz Date: Wed, 31 Jul 2024 13:30:30 +0000 Subject: [PATCH] add dynamic vmray feature tests --- tests/fixtures.py | 17 ++++++- tests/test_drakvuf_features.py | 45 +++++++++-------- tests/test_vmray_features.py | 89 ++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 tests/test_vmray_features.py diff --git a/tests/fixtures.py b/tests/fixtures.py index 3ae05f503..ddd6e87ed 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -209,6 +209,13 @@ def get_drakvuf_extractor(path): return DrakvufExtractor.from_report(report) +@lru_cache(maxsize=1) +def get_vmray_extractor(path): + from capa.features.extractors.vmray.extractor import VMRayExtractor + + return VMRayExtractor.from_zipfile(path) + + @lru_cache(maxsize=1) def get_ghidra_extractor(path: Path): import capa.features.extractors.ghidra.extractor @@ -395,7 +402,7 @@ def get_data_path_by_name(name) -> Path: / "v2.2" / "d46900384c78863420fb3e297d0a2f743cd2b6b3f7f82bf64059a168e07aceb7.json.gz" ) - elif name.startswith("93b2d1"): + elif name.startswith("93b2d1-drakvuf"): return ( CD / "data" @@ -403,6 +410,14 @@ def get_data_path_by_name(name) -> Path: / "drakvuf" / "93b2d1840566f45fab674ebc79a9d19c88993bcb645e0357f3cb584d16e7c795.log.gz" ) + elif name.startswith("93b2d1-vmray"): + return ( + CD + / "data" + / "dynamic" + / "vmray" + / "93b2d1840566f45fab674ebc79a9d19c88993bcb645e0357f3cb584d16e7c795_archive.zip" + ) elif name.startswith("ea2876"): return CD / "data" / "ea2876e9175410b6f6719f80ee44b9553960758c7d0f7bed73c0fe9a78d8e669.dll_" elif name.startswith("1038a2"): diff --git a/tests/test_drakvuf_features.py b/tests/test_drakvuf_features.py index 79832fc34..61fe69442 100644 --- a/tests/test_drakvuf_features.py +++ b/tests/test_drakvuf_features.py @@ -15,26 +15,31 @@ DYNAMIC_DRAKVUF_FEATURE_PRESENCE_TESTS = sorted( [ - ("93b2d1", "file", capa.features.common.String("\\Program Files\\WindowsApps\\does_not_exist"), False), + ("93b2d1-drakvuf", "file", capa.features.common.String("\\Program Files\\WindowsApps\\does_not_exist"), False), # file/imports - ("93b2d1", "file", capa.features.file.Import("SetUnhandledExceptionFilter"), True), + ("93b2d1-drakvuf", "file", capa.features.file.Import("SetUnhandledExceptionFilter"), True), # thread/api calls - ("93b2d1", "process=(3564:4852),thread=6592", capa.features.insn.API("LdrLoadDll"), True), - ("93b2d1", "process=(3564:4852),thread=6592", capa.features.insn.API("DoesNotExist"), False), + ("93b2d1-drakvuf", "process=(3564:4852),thread=6592", capa.features.insn.API("LdrLoadDll"), True), + ("93b2d1-drakvuf", "process=(3564:4852),thread=6592", capa.features.insn.API("DoesNotExist"), False), # call/api - ("93b2d1", "process=(3564:4852),thread=6592,call=1", capa.features.insn.API("LdrLoadDll"), True), - ("93b2d1", "process=(3564:4852),thread=6592,call=1", capa.features.insn.API("DoesNotExist"), False), + ("93b2d1-drakvuf", "process=(3564:4852),thread=6592,call=1", capa.features.insn.API("LdrLoadDll"), True), + ("93b2d1-drakvuf", "process=(3564:4852),thread=6592,call=1", capa.features.insn.API("DoesNotExist"), False), # call/string argument ( - "93b2d1", + "93b2d1-drakvuf", "process=(3564:4852),thread=6592,call=1", capa.features.common.String('0x667e2beb40:"api-ms-win-core-fibers-l1-1-1"'), True, ), - ("93b2d1", "process=(3564:4852),thread=6592,call=1", capa.features.common.String("non_existant"), False), + ( + "93b2d1-drakvuf", + "process=(3564:4852),thread=6592,call=1", + capa.features.common.String("non_existant"), + False, + ), # call/number argument - ("93b2d1", "process=(3564:4852),thread=6592,call=1", capa.features.insn.Number(0x801), True), - ("93b2d1", "process=(3564:4852),thread=6592,call=1", capa.features.insn.Number(0x010101010101), False), + ("93b2d1-drakvuf", "process=(3564:4852),thread=6592,call=1", capa.features.insn.Number(0x801), True), + ("93b2d1-drakvuf", "process=(3564:4852),thread=6592,call=1", capa.features.insn.Number(0x010101010101), False), ], # order tests by (file, item) # so that our LRU cache is most effective. @@ -43,26 +48,26 @@ DYNAMIC_DRAKVUF_FEATURE_COUNT_TESTS = sorted( [ - ("93b2d1", "file", capa.features.common.String("\\Program Files\\WindowsApps\\does_not_exist"), False), + ("93b2d1-drakvuf", "file", capa.features.common.String("\\Program Files\\WindowsApps\\does_not_exist"), False), # file/imports - ("93b2d1", "file", capa.features.file.Import("SetUnhandledExceptionFilter"), 1), + ("93b2d1-drakvuf", "file", capa.features.file.Import("SetUnhandledExceptionFilter"), 1), # thread/api calls - ("93b2d1", "process=(3564:4852),thread=6592", capa.features.insn.API("LdrLoadDll"), 9), - ("93b2d1", "process=(3564:4852),thread=6592", capa.features.insn.API("DoesNotExist"), False), + ("93b2d1-drakvuf", "process=(3564:4852),thread=6592", capa.features.insn.API("LdrLoadDll"), 9), + ("93b2d1-drakvuf", "process=(3564:4852),thread=6592", capa.features.insn.API("DoesNotExist"), False), # call/api - ("93b2d1", "process=(3564:4852),thread=6592,call=1", capa.features.insn.API("LdrLoadDll"), 1), - ("93b2d1", "process=(3564:4852),thread=6592,call=1", capa.features.insn.API("DoesNotExist"), 0), + ("93b2d1-drakvuf", "process=(3564:4852),thread=6592,call=1", capa.features.insn.API("LdrLoadDll"), 1), + ("93b2d1-drakvuf", "process=(3564:4852),thread=6592,call=1", capa.features.insn.API("DoesNotExist"), 0), # call/string argument ( - "93b2d1", + "93b2d1-drakvuf", "process=(3564:4852),thread=6592,call=1", capa.features.common.String('0x667e2beb40:"api-ms-win-core-fibers-l1-1-1"'), 1, ), - ("93b2d1", "process=(3564:4852),thread=6592,call=1", capa.features.common.String("non_existant"), 0), + ("93b2d1-drakvuf", "process=(3564:4852),thread=6592,call=1", capa.features.common.String("non_existant"), 0), # call/number argument - ("93b2d1", "process=(3564:4852),thread=6592,call=1", capa.features.insn.Number(0x801), 1), - ("93b2d1", "process=(3564:4852),thread=6592,call=1", capa.features.insn.Number(0x010101010101), 0), + ("93b2d1-drakvuf", "process=(3564:4852),thread=6592,call=1", capa.features.insn.Number(0x801), 1), + ("93b2d1-drakvuf", "process=(3564:4852),thread=6592,call=1", capa.features.insn.Number(0x010101010101), 0), ], # order tests by (file, item) # so that our LRU cache is most effective. diff --git a/tests/test_vmray_features.py b/tests/test_vmray_features.py new file mode 100644 index 000000000..d92a75e49 --- /dev/null +++ b/tests/test_vmray_features.py @@ -0,0 +1,89 @@ +# Copyright (C) 2024 Mandiant, Inc. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: [package root]/LICENSE.txt +# Unless required by applicable law or agreed to in writing, software distributed under the License +# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and limitations under the License. + +import fixtures + +import capa.main +import capa.features.file +import capa.features.insn +import capa.features.common + +DYNAMIC_VMRAY_FEATURE_PRESENCE_TESTS = sorted( + [ + ("93b2d1-vmray", "file", capa.features.common.String("api.%x%x.%s"), True), + ("93b2d1-vmray", "file", capa.features.common.String("\\Program Files\\WindowsApps\\does_not_exist"), False), + # file/imports + ("93b2d1-vmray", "file", capa.features.file.Import("GetAddrInfoW"), True), + # thread/api calls + ("93b2d1-vmray", "process=(2176:0),thread=7", capa.features.insn.API("GetAddrInfoW"), True), + ("93b2d1-vmray", "process=(2176:0),thread=7", capa.features.insn.API("DoesNotExist"), False), + # call/api + ("93b2d1-vmray", "process=(2176:0),thread=7,call=2361", capa.features.insn.API("GetAddrInfoW"), True), + # call/string argument + ( + "93b2d1-vmray", + "process=(2176:0),thread=7,call=10323", + capa.features.common.String("raw.githubusercontent.com"), + True, + ), + # call/number argument + # VirtualAlloc(4096, 4) + ("93b2d1-vmray", "process=(2176:0),thread=7,call=2358", capa.features.insn.Number(4096), True), + ("93b2d1-vmray", "process=(2176:0),thread=7,call=2358", capa.features.insn.Number(4), True), + ], + # order tests by (file, item) + # so that our LRU cache is most effective. + key=lambda t: (t[0], t[1]), +) + +DYNAMIC_VMRAY_FEATURE_COUNT_TESTS = sorted( + [ + # file/imports + ("93b2d1-vmray", "file", capa.features.file.Import("GetAddrInfoW"), 1), + # thread/api calls + ("93b2d1-vmray", "process=(2176:0),thread=7", capa.features.insn.API("free"), 1), + ("93b2d1-vmray", "process=(2176:0),thread=7", capa.features.insn.API("GetAddrInfoW"), 5), + # call/api + ("93b2d1-vmray", "process=(2176:0),thread=7,call=2345", capa.features.insn.API("free"), 1), + ("93b2d1-vmray", "process=(2176:0),thread=7,call=2345", capa.features.insn.API("GetAddrInfoW"), 0), + ("93b2d1-vmray", "process=(2176:0),thread=7,call=2361", capa.features.insn.API("GetAddrInfoW"), 1), + # call/string argument + ( + "93b2d1-vmray", + "process=(2176:0),thread=7,call=10323", + capa.features.common.String("raw.githubusercontent.com"), + 1, + ), + ("93b2d1-vmray", "process=(2176:0),thread=7,call=10323", capa.features.common.String("non_existant"), 0), + # call/number argument + ("93b2d1-vmray", "process=(2176:0),thread=7,call=10315", capa.features.insn.Number(4096), 1), + ("93b2d1-vmray", "process=(2176:0),thread=7,call=10315", capa.features.insn.Number(4), 1), + ("93b2d1-vmray", "process=(2176:0),thread=7,call=10315", capa.features.insn.Number(404), 0), + ], + # order tests by (file, item) + # so that our LRU cache is most effective. + key=lambda t: (t[0], t[1]), +) + + +@fixtures.parametrize( + "sample,scope,feature,expected", + DYNAMIC_VMRAY_FEATURE_PRESENCE_TESTS, + indirect=["sample", "scope"], +) +def test_vmray_features(sample, scope, feature, expected): + fixtures.do_test_feature_presence(fixtures.get_vmray_extractor, sample, scope, feature, expected) + + +@fixtures.parametrize( + "sample,scope,feature,expected", + DYNAMIC_VMRAY_FEATURE_COUNT_TESTS, + indirect=["sample", "scope"], +) +def test_vmray_feature_counts(sample, scope, feature, expected): + fixtures.do_test_feature_count(fixtures.get_vmray_extractor, sample, scope, feature, expected)