tests: add dynamic fixture discovery

closes #1743
This commit is contained in:
Willi Ballenthin
2026-03-29 23:54:25 +02:00
parent 36c4aa1131
commit 9126cb91c6
6 changed files with 208 additions and 293 deletions

View File

@@ -70,6 +70,7 @@
- binja: add mypy config for top-level binaryninja module to fix mypy issues @devs6186 #2399
- ci: deprecate macos-13 runner and use Python v3.13 for testing @mike-hunhoff #2777
- ci: pin pip-audit action SHAs and update to v1.1.0 @kami922 #1131
- tests: use dynamic test sample discovery @williballenthin #1743
### Raw diffs
- [capa v9.3.1...master](https://github.com/mandiant/capa/compare/v9.3.1...master)

View File

@@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import hashlib
import logging
import contextlib
import collections
@@ -416,164 +418,71 @@ def extract_instruction_features(extractor, fh, bbh, ih) -> dict[Feature, set[Ad
return features
# note: to reduce the testing time it's recommended to reuse already existing test samples, if possible
# index from various identifiers to the path to a test fixture file
# supported index facets include:
# - file name (foo.exe)
# - file base name (foo)
# - pma01-01.exe_
# - md5 prefix (5, 8, all characters)
# - sha256 prefix (5, 8, all characters)
# - parent/file prefix (vmray/12345...)
# - file prefix (12345..., like <hash>.json.gz)
fixture_index: dict[str, Path] = {}
for base, _dirs, files in os.walk(CD):
for file in files:
path = Path(os.path.join(base, file))
# full name, like: hello_world.exe
fixture_index[path.name.lower()] = path
# basename, like: hello_world
fixture_index[path.stem.lower()] = path
if "Practical Malware Analysis Lab " in path.name:
# like: pma01-01.exe_
fixture_index[path.name.replace("Practical Malware Analysis Lab ", "pma")] = path
m = hashlib.md5()
m.update(path.read_bytes())
fixture_index[m.hexdigest()] = path
fixture_index[m.hexdigest()[:5]] = path
s = hashlib.sha256()
s.update(path.read_bytes())
fixture_index[s.hexdigest()] = path
fixture_index[s.hexdigest()[:5]] = path
if path.name.lower()[:8] not in fixture_index:
# like: 0000a657
# to handle cases like data/dynamic/cape/v2.2/0000a657....json.gz
fixture_index[path.name.lower()[:8]] = path
# like: drakvuf/12345
fixture_index[f"{path.parent.name}/{path.name.lower()[:5]}"] = path
def get_data_path_by_name(name) -> Path:
if name == "mimikatz":
return CD / "data" / "mimikatz.exe_"
elif name == "kernel32":
return CD / "data" / "kernel32.dll_"
elif name == "kernel32-64":
return CD / "data" / "kernel32-64.dll_"
elif name == "pma01-01":
return CD / "data" / "Practical Malware Analysis Lab 01-01.dll_"
elif name == "pma01-01-rd":
return CD / "data" / "rd" / "Practical Malware Analysis Lab 01-01.dll_.json"
elif name == "pma12-04":
return CD / "data" / "Practical Malware Analysis Lab 12-04.exe_"
elif name == "pma16-01":
return CD / "data" / "Practical Malware Analysis Lab 16-01.exe_"
elif name == "pma16-01_binja_db":
return CD / "data" / "Practical Malware Analysis Lab 16-01.exe_.bndb"
elif name == "pma21-01":
return CD / "data" / "Practical Malware Analysis Lab 21-01.exe_"
elif name == "al-khaser x86":
return CD / "data" / "al-khaser_x86.exe_"
elif name == "al-khaser x64":
return CD / "data" / "al-khaser_x64.exe_"
elif name.startswith("39c05"):
return CD / "data" / "39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.dll_"
elif name.startswith("499c2"):
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"):
return CD / "data" / "a933a1a402775cfa94b6bee0963f4b46.dll_"
elif name.startswith("bfb9b"):
return CD / "data" / "bfb9b5391a13d0afd787e87ab90f14f5.dll_"
elif name.startswith("c9188"):
return CD / "data" / "c91887d861d9bd4a5872249b641bc9f9.exe_"
elif name.startswith("64d9f"):
return CD / "data" / "64d9f7d96b99467f36e22fada623c3bb.dll_"
elif name.startswith("82bf6"):
return CD / "data" / "82BF6347ACF15E5D883715DC289D8A2B.exe_"
elif name.startswith("pingtaest"):
return CD / "data" / "ping_täst.exe_"
elif name.startswith("77329"):
return CD / "data" / "773290480d5445f11d3dc1b800728966.exe_"
elif name.startswith("3b13b"):
return CD / "data" / "3b13b6f1d7cd14dc4a097a12e2e505c0a4cff495262261e2bfc991df238b9b04.dll_"
elif name == "7351f.elf":
return CD / "data" / "7351f8a40c5450557b24622417fc478d.elf_"
elif name.startswith("79abd"):
return CD / "data" / "79abd17391adc6251ecdc58d13d76baf.dll_"
elif name.startswith("946a9"):
return CD / "data" / "946a99f36a46d335dec080d9a4371940.dll_"
elif name.startswith("2f7f5f"):
return CD / "data" / "2f7f5fb5de175e770d7eae87666f9831.elf_"
elif name.startswith("b9f5b"):
return CD / "data" / "b9f5bd514485fb06da39beff051b9fdc.exe_"
elif name.startswith("mixed-mode-64"):
return DNFILE_TESTFILES / "mixed-mode" / "ModuleCode" / "bin" / "ModuleCode_amd64.exe"
elif name.startswith("hello-world"):
return DNFILE_TESTFILES / "hello-world" / "hello-world.exe"
elif name.startswith("_1c444"):
return DOTNET_DIR / "1c444ebeba24dcba8628b7dfe5fec7c6.exe_"
elif name.startswith("_387f15"):
return DOTNET_DIR / "387f15043f0198fd3a637b0758c2b6dde9ead795c3ed70803426fc355731b173.dll_"
elif name.startswith("_692f"):
return DOTNET_DIR / "692f7fd6d198e804d6af98eb9e390d61.exe_"
elif name.startswith("_0953c"):
return CD / "data" / "0953cc3b77ed2974b09e3a00708f88de931d681e2d0cb64afbaf714610beabe6.exe_"
elif name.startswith("_039a6"):
return CD / "data" / "039a6336d0802a2255669e6867a5679c7eb83313dbc61fb1c7232147379bd304.exe_"
elif name.startswith("b5f052"):
return CD / "data" / "b5f0524e69b3a3cf636c7ac366ca57bf5e3a8fdc8a9f01caf196c611a7918a87.elf_"
elif name.startswith("bf7a9c"):
return CD / "data" / "bf7a9c8bdfa6d47e01ad2b056264acc3fd90cf43fe0ed8deec93ab46b47d76cb.elf_"
elif name.startswith("294b8d"):
return CD / "data" / "294b8db1f2702b60fb2e42fdc50c2cee6a5046112da9a5703a548a4fa50477bc.elf_"
elif name.startswith("2bf18d"):
return CD / "data" / "2bf18d0403677378adad9001b1243211.elf_"
elif name.startswith("0000a657"):
return (
CD
/ "data"
/ "dynamic"
/ "cape"
/ "v2.2"
/ "0000a65749f5902c4d82ffa701198038f0b4870b00a27cfca109f8f933476d82.json.gz"
)
elif name.startswith("d46900"):
return (
CD
/ "data"
/ "dynamic"
/ "cape"
/ "v2.2"
/ "d46900384c78863420fb3e297d0a2f743cd2b6b3f7f82bf64059a168e07aceb7.json.gz"
)
elif name.startswith("93b2d1-drakvuf"):
return (
CD
/ "data"
/ "dynamic"
/ "drakvuf"
/ "93b2d1840566f45fab674ebc79a9d19c88993bcb645e0357f3cb584d16e7c795.log.gz"
)
elif name.startswith("93b2d1-vmray"):
return (
CD
/ "data"
/ "dynamic"
/ "vmray"
/ "93b2d1840566f45fab674ebc79a9d19c88993bcb645e0357f3cb584d16e7c795_min_archive.zip"
)
elif name.startswith("2f8a79-vmray"):
return (
CD
/ "data"
/ "dynamic"
/ "vmray"
/ "2f8a79b12a7a989ac7e5f6ec65050036588a92e65aeb6841e08dc228ff0e21b4_min_archive.zip"
)
elif name.startswith("eb1287-vmray"):
return (
CD
/ "data"
/ "dynamic"
/ "vmray"
/ "eb12873c0ce3e9ea109c2a447956cbd10ca2c3e86936e526b2c6e28764999f21_min_archive.zip"
)
elif name.startswith("ea2876"):
return CD / "data" / "ea2876e9175410b6f6719f80ee44b9553960758c7d0f7bed73c0fe9a78d8e669.dll_"
elif name.startswith("1038a2"):
return CD / "data" / "1038a23daad86042c66bfe6c9d052d27048de9653bde5750dc0f240c792d9ac8.elf_"
elif name.startswith("3da7c"):
return CD / "data" / "3da7c2c70a2d93ac4643f20339d5c7d61388bddd77a4a5fd732311efad78e535.elf_"
elif name.startswith("nested_typedef"):
return CD / "data" / "dotnet" / "dd9098ff91717f4906afe9dafdfa2f52.exe_"
elif name.startswith("nested_typeref"):
return CD / "data" / "dotnet" / "2c7d60f77812607dec5085973ff76cea.dll_"
elif name.startswith("687e79.ghidra.be2"):
return (
CD
/ "data"
/ "binexport2"
/ "687e79cde5b0ced75ac229465835054931f9ec438816f2827a8be5f3bd474929.elf_.ghidra.BinExport"
)
elif name.startswith("d1e650.ghidra.be2"):
return (
CD
/ "data"
/ "binexport2"
/ "d1e6506964edbfffb08c0dd32e1486b11fbced7a4bd870ffe79f110298f0efb8.elf_.ghidra.BinExport"
)
else:
raise ValueError(f"unexpected sample fixture: {name}")
try:
return fixture_index[name.rstrip(".")]
except KeyError:
if name.startswith("mixed-mode-64"):
return DNFILE_TESTFILES / "mixed-mode" / "ModuleCode" / "bin" / "ModuleCode_amd64.exe"
elif name.startswith("687e79.ghidra.be2"):
return (
CD
/ "data"
/ "binexport2"
/ "687e79cde5b0ced75ac229465835054931f9ec438816f2827a8be5f3bd474929.elf_.ghidra.BinExport"
)
elif name.startswith("d1e650.ghidra.be2"):
return (
CD
/ "data"
/ "binexport2"
/ "d1e6506964edbfffb08c0dd32e1486b11fbced7a4bd870ffe79f110298f0efb8.elf_.ghidra.BinExport"
)
else:
raise ValueError(f"unexpected sample fixture: {name}")
def resolve_sample(sample):
@@ -798,7 +707,7 @@ def parametrize(params, values, **kwargs):
FEATURE_PRESENCE_TESTS = sorted(
[
# file/characteristic("embedded pe")
("pma12-04", "file", capa.features.common.Characteristic("embedded pe"), True),
("pma12-04.exe_", "file", capa.features.common.Characteristic("embedded pe"), True),
# file/string
("mimikatz", "file", capa.features.common.String("SCardControl"), True),
("mimikatz", "file", capa.features.common.String("SCardTransmit"), True),
@@ -814,7 +723,7 @@ FEATURE_PRESENCE_TESTS = sorted(
("kernel32", "file", capa.features.file.Export("lstrlenW"), True),
("kernel32", "file", capa.features.file.Export("nope"), False),
# forwarded export
("ea2876", "file", capa.features.file.Export("vresion.GetFileVersionInfoA"), True),
("ea287...", "file", capa.features.file.Export("vresion.GetFileVersionInfoA"), True),
# file/imports
("mimikatz", "file", capa.features.file.Import("advapi32.CryptSetHashParam"), True),
("mimikatz", "file", capa.features.file.Import("CryptSetHashParam"), True),
@@ -868,7 +777,7 @@ FEATURE_PRESENCE_TESTS = sorted(
("mimikatz", "function=0x40105D", capa.features.insn.Offset(0x4), True),
("mimikatz", "function=0x40105D", capa.features.insn.Offset(0xC), True),
# insn/offset, issue #276
("64d9f", "function=0x10001510,bb=0x100015B0", capa.features.insn.Offset(0x4000), True),
("64d9f...", "function=0x10001510,bb=0x100015B0", capa.features.insn.Offset(0x4000), True),
# insn/offset: stack references
("mimikatz", "function=0x40105D", capa.features.insn.Offset(0x8), False),
("mimikatz", "function=0x40105D", capa.features.insn.Offset(0x10), False),
@@ -924,32 +833,32 @@ FEATURE_PRESENCE_TESTS = sorted(
# insn/api: x64 thunk
("kernel32-64", "function=0x1800202B0", capa.features.insn.API("RtlCaptureContext"), True),
# insn/api: x64 nested thunk
("al-khaser x64", "function=0x14004B4F0", capa.features.insn.API("__vcrt_GetModuleHandle"), True),
("al-khaser_x64", "function=0x14004B4F0", capa.features.insn.API("__vcrt_GetModuleHandle"), True),
# insn/api: call via jmp
("mimikatz", "function=0x40B3C6", capa.features.insn.API("LocalFree"), True),
("c91887...", "function=0x40156F", capa.features.insn.API("CloseClipboard"), True),
("c9188...", "function=0x40156F", capa.features.insn.API("CloseClipboard"), True),
# insn/api: resolve indirect calls
# not extracting dll anymore
("c91887...", "function=0x401A77", capa.features.insn.API("kernel32.CreatePipe"), False),
("c91887...", "function=0x401A77", capa.features.insn.API("kernel32.SetHandleInformation"), False),
("c91887...", "function=0x401A77", capa.features.insn.API("kernel32.CloseHandle"), False),
("c91887...", "function=0x401A77", capa.features.insn.API("kernel32.WriteFile"), False),
("c91887...", "function=0x401A77", capa.features.insn.API("CreatePipe"), True),
("c91887...", "function=0x401A77", capa.features.insn.API("SetHandleInformation"), True),
("c91887...", "function=0x401A77", capa.features.insn.API("CloseHandle"), True),
("c91887...", "function=0x401A77", capa.features.insn.API("WriteFile"), True),
("c9188...", "function=0x401A77", capa.features.insn.API("kernel32.CreatePipe"), False),
("c9188...", "function=0x401A77", capa.features.insn.API("kernel32.SetHandleInformation"), False),
("c9188...", "function=0x401A77", capa.features.insn.API("kernel32.CloseHandle"), False),
("c9188...", "function=0x401A77", capa.features.insn.API("kernel32.WriteFile"), False),
("c9188...", "function=0x401A77", capa.features.insn.API("CreatePipe"), True),
("c9188...", "function=0x401A77", capa.features.insn.API("SetHandleInformation"), True),
("c9188...", "function=0x401A77", capa.features.insn.API("CloseHandle"), True),
("c9188...", "function=0x401A77", capa.features.insn.API("WriteFile"), True),
# insn/string
("mimikatz", "function=0x40105D", capa.features.common.String("SCardControl"), True),
("mimikatz", "function=0x40105D", capa.features.common.String("SCardTransmit"), True),
("mimikatz", "function=0x40105D", capa.features.common.String("ACR > "), True),
("mimikatz", "function=0x40105D", capa.features.common.String("nope"), False),
("773290...", "function=0x140001140", capa.features.common.String(r"%s:\\OfficePackagesForWDAG"), True),
("77329...", "function=0x140001140", capa.features.common.String(r"%s:\\OfficePackagesForWDAG"), True),
# overlapping string, see #1271
("294b8d...", "function=0x404970,bb=0x404970,insn=0x40499F", capa.features.common.String("\r\n\x00:ht"), False),
("294b8...", "function=0x404970,bb=0x404970,insn=0x40499F", capa.features.common.String("\r\n\x00:ht"), False),
# insn/regex
("pma16-01", "function=0x4021B0", capa.features.common.Regex("HTTP/1.0"), True),
("pma16-01", "function=0x402F40", capa.features.common.Regex("www.practicalmalwareanalysis.com"), True),
("pma16-01", "function=0x402F40", capa.features.common.Substring("practicalmalwareanalysis.com"), True),
("pma16-01.exe_", "function=0x4021B0", capa.features.common.Regex("HTTP/1.0"), True),
("pma16-01.exe_", "function=0x402F40", capa.features.common.Regex("www.practicalmalwareanalysis.com"), True),
("pma16-01.exe_", "function=0x402F40", capa.features.common.Substring("practicalmalwareanalysis.com"), True),
# insn/string, pointer to string
("mimikatz", "function=0x44EDEF", capa.features.common.String("INPUTEVENT"), True),
# insn/string, direct memory reference
@@ -1000,39 +909,39 @@ FEATURE_PRESENCE_TESTS = sorted(
# function/characteristic(calls to)
("mimikatz", "function=0x40105D", capa.features.common.Characteristic("calls to"), True),
# function/characteristic(forwarded export)
("ea2876", "file", capa.features.common.Characteristic("forwarded export"), True),
("ea287", "file", capa.features.common.Characteristic("forwarded export"), True),
# before this we used ambiguous (0x4556E5, False), which has a data reference / indirect recursive call, see #386
("mimikatz", "function=0x456BB9", capa.features.common.Characteristic("calls to"), False),
# file/function-name
("pma16-01", "file", capa.features.file.FunctionName("__aulldiv"), True),
("pma16-01.exe_", "file", capa.features.file.FunctionName("__aulldiv"), True),
# os & format & arch
("pma16-01", "file", OS(OS_WINDOWS), True),
("pma16-01", "file", OS(OS_LINUX), False),
("pma16-01.exe_", "file", OS(OS_WINDOWS), True),
("pma16-01.exe_", "file", OS(OS_LINUX), False),
("mimikatz", "file", OS(OS_WINDOWS), True),
("pma16-01", "function=0x401100", OS(OS_WINDOWS), True),
("pma16-01", "function=0x401100,bb=0x401130", OS(OS_WINDOWS), True),
("pma16-01.exe_", "function=0x401100", OS(OS_WINDOWS), True),
("pma16-01.exe_", "function=0x401100,bb=0x401130", OS(OS_WINDOWS), True),
("mimikatz", "function=0x40105D", OS(OS_WINDOWS), True),
("pma16-01", "file", Arch(ARCH_I386), True),
("pma16-01", "file", Arch(ARCH_AMD64), False),
("pma16-01.exe_", "file", Arch(ARCH_I386), True),
("pma16-01.exe_", "file", Arch(ARCH_AMD64), False),
("mimikatz", "file", Arch(ARCH_I386), True),
("pma16-01", "function=0x401100", Arch(ARCH_I386), True),
("pma16-01", "function=0x401100,bb=0x401130", Arch(ARCH_I386), True),
("pma16-01.exe_", "function=0x401100", Arch(ARCH_I386), True),
("pma16-01.exe_", "function=0x401100,bb=0x401130", Arch(ARCH_I386), True),
("mimikatz", "function=0x40105D", Arch(ARCH_I386), True),
("pma16-01", "file", Format(FORMAT_PE), True),
("pma16-01", "file", Format(FORMAT_ELF), False),
("pma16-01.exe_", "file", Format(FORMAT_PE), True),
("pma16-01.exe_", "file", Format(FORMAT_ELF), False),
("mimikatz", "file", Format(FORMAT_PE), True),
# format is also a global feature
("pma16-01", "function=0x401100", Format(FORMAT_PE), True),
("pma16-01.exe_", "function=0x401100", Format(FORMAT_PE), True),
("mimikatz", "function=0x456BB9", Format(FORMAT_PE), True),
# elf support
("7351f.elf", "file", OS(OS_LINUX), True),
("7351f.elf", "file", OS(OS_WINDOWS), False),
("7351f.elf", "file", Format(FORMAT_ELF), True),
("7351f.elf", "file", Format(FORMAT_PE), False),
("7351f.elf", "file", Arch(ARCH_I386), False),
("7351f.elf", "file", Arch(ARCH_AMD64), True),
("7351f.elf", "function=0x408753", capa.features.common.String("/dev/null"), True),
("7351f.elf", "function=0x408753,bb=0x408781", capa.features.insn.API("open"), True),
("7351f...", "file", OS(OS_LINUX), True),
("7351f...", "file", OS(OS_WINDOWS), False),
("7351f...", "file", Format(FORMAT_ELF), True),
("7351f...", "file", Format(FORMAT_PE), False),
("7351f...", "file", Arch(ARCH_I386), False),
("7351f...", "file", Arch(ARCH_AMD64), True),
("7351f...", "function=0x408753", capa.features.common.String("/dev/null"), True),
("7351f...", "function=0x408753,bb=0x408781", capa.features.insn.API("open"), True),
("79abd...", "function=0x10002385,bb=0x10002385", capa.features.common.Characteristic("call $+5"), True),
("946a9...", "function=0x10001510,bb=0x100015c0", capa.features.common.Characteristic("call $+5"), True),
],
@@ -1106,68 +1015,73 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
("hello-world", "function=0x250, bb=0x250, insn=0x257", capa.features.common.Namespace("System"), True),
("hello-world", "function=0x250", capa.features.insn.API("System.Console::WriteLine"), True),
("hello-world", "file", capa.features.file.Import("System.Console::WriteLine"), True),
("_1c444", "file", capa.features.common.String(r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"), True),
("_1c444", "file", capa.features.common.String("get_IsAlive"), True),
("_1c444", "file", capa.features.file.Import("gdi32.CreateCompatibleBitmap"), True),
("_1c444", "file", capa.features.file.Import("CreateCompatibleBitmap"), True),
("_1c444", "file", capa.features.file.Import("gdi32::CreateCompatibleBitmap"), False),
("_1c444", "function=0x1F68", capa.features.insn.API("GetWindowDC"), True),
("1c444...", "file", capa.features.common.String(r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"), True),
("1c444...", "file", capa.features.common.String("get_IsAlive"), True),
("1c444...", "file", capa.features.file.Import("gdi32.CreateCompatibleBitmap"), True),
("1c444...", "file", capa.features.file.Import("CreateCompatibleBitmap"), True),
("1c444...", "file", capa.features.file.Import("gdi32::CreateCompatibleBitmap"), False),
("1c444...", "function=0x1F68", capa.features.insn.API("GetWindowDC"), True),
# not extracting dll anymore
("_1c444", "function=0x1F68", capa.features.insn.API("user32.GetWindowDC"), False),
("_1c444", "function=0x1F68", capa.features.insn.Number(0xCC0020), True),
("_1c444", "token=0x600001D", capa.features.common.Characteristic("calls to"), True),
("_1c444", "token=0x6000018", capa.features.common.Characteristic("calls to"), False),
("_1c444", "token=0x600001D", capa.features.common.Characteristic("calls from"), True),
("_1c444", "token=0x600000F", capa.features.common.Characteristic("calls from"), False),
("_1c444", "function=0x1F68", capa.features.insn.Number(0x0), True),
("_1c444", "function=0x1F68", capa.features.insn.Number(0x1), False),
("_692f", "token=0x6000004", capa.features.insn.API("System.Linq.Enumerable::First"), True), # generic method
("1c444...", "function=0x1F68", capa.features.insn.API("user32.GetWindowDC"), False),
("1c444...", "function=0x1F68", capa.features.insn.Number(0xCC0020), True),
("1c444...", "token=0x600001D", capa.features.common.Characteristic("calls to"), True),
("1c444...", "token=0x6000018", capa.features.common.Characteristic("calls to"), False),
("1c444...", "token=0x600001D", capa.features.common.Characteristic("calls from"), True),
("1c444...", "token=0x600000F", capa.features.common.Characteristic("calls from"), False),
("1c444...", "function=0x1F68", capa.features.insn.Number(0x0), True),
("1c444...", "function=0x1F68", capa.features.insn.Number(0x1), False),
(
"_692f",
"692f7...",
"token=0x6000004",
capa.features.insn.API("System.Linq.Enumerable::First"),
True,
), # generic method
(
"692f7...",
"token=0x6000004",
capa.features.insn.Property("System.Linq.Enumerable::First"),
False,
), # generic method
("_692f", "token=0x6000004", capa.features.common.Namespace("System.Linq"), True), # generic method
("_692f", "token=0x6000004", capa.features.common.Class("System.Linq.Enumerable"), True), # generic method
("_1c444", "token=0x6000020", capa.features.common.Namespace("Reqss"), True), # ldftn
("_1c444", "token=0x6000020", capa.features.common.Class("Reqss.Reqss"), True), # ldftn
("692f7...", "token=0x6000004", capa.features.common.Namespace("System.Linq"), True), # generic method
("692f7...", "token=0x6000004", capa.features.common.Class("System.Linq.Enumerable"), True), # generic method
("1c444...", "token=0x6000020", capa.features.common.Namespace("Reqss"), True), # ldftn
("1c444...", "token=0x6000020", capa.features.common.Class("Reqss.Reqss"), True), # ldftn
(
"_1c444",
"1c444...",
"function=0x1F59, bb=0x1F59, insn=0x1F5B",
capa.features.common.Characteristic("unmanaged call"),
True,
),
("_1c444", "function=0x2544", capa.features.common.Characteristic("unmanaged call"), False),
("1c444...", "function=0x2544", capa.features.common.Characteristic("unmanaged call"), False),
# same as above but using token instead of function
("_1c444", "token=0x6000088", capa.features.common.Characteristic("unmanaged call"), False),
("1c444...", "token=0x6000088", capa.features.common.Characteristic("unmanaged call"), False),
(
"_1c444",
"1c444...",
"function=0x1F68, bb=0x1F68, insn=0x1FF9",
capa.features.insn.API("System.Drawing.Image::FromHbitmap"),
True,
),
("_1c444", "function=0x1F68, bb=0x1F68, insn=0x1FF9", capa.features.insn.API("FromHbitmap"), False),
("1c444...", "function=0x1F68, bb=0x1F68, insn=0x1FF9", capa.features.insn.API("FromHbitmap"), False),
(
"_1c444",
"1c444...",
"token=0x600002B",
capa.features.insn.Property("System.IO.FileInfo::Length", access=FeatureAccess.READ),
True,
), # MemberRef property access
(
"_1c444",
"1c444...",
"token=0x600002B",
capa.features.insn.Property("System.IO.FileInfo::Length"),
True,
), # MemberRef property access
(
"_1c444",
"1c444...",
"token=0x6000081",
capa.features.insn.API("System.Diagnostics.Process::Start"),
True,
), # MemberRef property access
(
"_1c444",
"1c444...",
"token=0x6000081",
capa.features.insn.Property(
"System.Diagnostics.ProcessStartInfo::UseShellExecute", access=FeatureAccess.WRITE
@@ -1175,7 +1089,7 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
True,
),
(
"_1c444",
"1c444...",
"token=0x6000081",
capa.features.insn.Property(
"System.Diagnostics.ProcessStartInfo::WorkingDirectory", access=FeatureAccess.WRITE
@@ -1183,7 +1097,7 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
True,
),
(
"_1c444",
"1c444...",
"token=0x6000081",
capa.features.insn.Property(
"System.Diagnostics.ProcessStartInfo::FileName", access=FeatureAccess.WRITE
@@ -1191,7 +1105,7 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
True,
),
(
"_1c444",
"1c444...",
"token=0x6000087",
capa.features.insn.Property(
"Sockets.MySocket::reConnectionDelay", access=FeatureAccess.WRITE
@@ -1199,7 +1113,7 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
True,
),
(
"_1c444",
"1c444...",
"token=0x600008A",
capa.features.insn.Property(
"Sockets.MySocket::isConnected", access=FeatureAccess.WRITE
@@ -1207,19 +1121,19 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
True,
),
(
"_1c444",
"1c444...",
"token=0x600008A",
capa.features.common.Class("Sockets.MySocket"), # Field property access
True,
),
(
"_1c444",
"1c444...",
"token=0x600008A",
capa.features.common.Namespace("Sockets"), # Field property access
True,
),
(
"_1c444",
"1c444...",
"token=0x600008A",
capa.features.insn.Property(
"Sockets.MySocket::onConnected", access=FeatureAccess.READ
@@ -1227,7 +1141,7 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
True,
),
(
"_0953c",
"0953c...",
"token=0x6000004",
capa.features.insn.Property(
"System.Diagnostics.Debugger::IsAttached", access=FeatureAccess.READ
@@ -1235,19 +1149,19 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
True,
),
(
"_0953c",
"0953c...",
"token=0x6000004",
capa.features.common.Class("System.Diagnostics.Debugger"), # MemberRef property access
True,
),
(
"_0953c",
"0953c...",
"token=0x6000004",
capa.features.common.Namespace("System.Diagnostics"), # MemberRef property access
True,
),
(
"_692f",
"692f7...",
"token=0x6000006",
capa.features.insn.Property(
"System.Management.Automation.PowerShell::Streams", access=FeatureAccess.READ
@@ -1255,7 +1169,7 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
False,
),
(
"_387f15",
"387f1...",
"token=0x600009E",
capa.features.insn.Property(
"Modulo.IqQzcRDvSTulAhyLtZHqyeYGgaXGbuLwhxUKXYmhtnOmgpnPJDTSIPhYPpnE::geoplugin_countryCode",
@@ -1264,7 +1178,7 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
True,
),
(
"_387f15",
"387f1...",
"token=0x600009E",
capa.features.common.Class(
"Modulo.IqQzcRDvSTulAhyLtZHqyeYGgaXGbuLwhxUKXYmhtnOmgpnPJDTSIPhYPpnE"
@@ -1272,31 +1186,31 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
True,
),
(
"_387f15",
"387f1...",
"token=0x600009E",
capa.features.common.Namespace("Modulo"), # MethodDef property access
True,
),
(
"_039a6",
"039a6...",
"token=0x6000007",
capa.features.insn.API("System.Reflection.Assembly::Load"),
True,
),
(
"_039a6",
"039a6...",
"token=0x600001D",
capa.features.insn.Property("StagelessHollow.Arac::Marka", access=FeatureAccess.READ), # MethodDef method
True,
),
(
"_039a6",
"039a6...",
"token=0x600001C",
capa.features.insn.Property("StagelessHollow.Arac::Marka", access=FeatureAccess.READ), # MethodDef method
False,
),
(
"_039a6",
"039a6...",
"token=0x6000023",
capa.features.insn.Property(
"System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Task", access=FeatureAccess.READ
@@ -1304,109 +1218,109 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
False,
),
(
"nested_typedef",
"dd909...",
"file",
capa.features.common.Class("mynamespace.myclass_outer0"),
True,
),
(
"nested_typedef",
"dd909...",
"file",
capa.features.common.Class("mynamespace.myclass_outer1"),
True,
),
(
"nested_typedef",
"dd909...",
"file",
capa.features.common.Class("mynamespace.myclass_outer0/myclass_inner0_0"),
True,
),
(
"nested_typedef",
"dd909...",
"file",
capa.features.common.Class("mynamespace.myclass_outer0/myclass_inner0_1"),
True,
),
(
"nested_typedef",
"dd909...",
"file",
capa.features.common.Class("mynamespace.myclass_outer1/myclass_inner1_0"),
True,
),
(
"nested_typedef",
"dd909...",
"file",
capa.features.common.Class("mynamespace.myclass_outer1/myclass_inner1_1"),
True,
),
(
"nested_typedef",
"dd909...",
"file",
capa.features.common.Class("mynamespace.myclass_outer1/myclass_inner1_0/myclass_inner_inner"),
True,
),
(
"nested_typedef",
"dd909...",
"file",
capa.features.common.Class("myclass_inner_inner"),
False,
),
(
"nested_typedef",
"dd909...",
"file",
capa.features.common.Class("myclass_inner1_0"),
False,
),
(
"nested_typedef",
"dd909...",
"file",
capa.features.common.Class("myclass_inner1_1"),
False,
),
(
"nested_typedef",
"dd909...",
"file",
capa.features.common.Class("myclass_inner0_0"),
False,
),
(
"nested_typedef",
"dd909...",
"file",
capa.features.common.Class("myclass_inner0_1"),
False,
),
(
"nested_typeref",
"2c7d6...",
"file",
capa.features.file.Import("Android.OS.Build/VERSION::SdkInt"),
True,
),
(
"nested_typeref",
"2c7d6...",
"file",
capa.features.file.Import("Android.Media.Image/Plane::Buffer"),
True,
),
(
"nested_typeref",
"2c7d6...",
"file",
capa.features.file.Import("Android.Provider.Telephony/Sent/Sent::ContentUri"),
True,
),
(
"nested_typeref",
"2c7d6...",
"file",
capa.features.file.Import("Android.OS.Build::SdkInt"),
False,
),
(
"nested_typeref",
"2c7d6...",
"file",
capa.features.file.Import("Plane::Buffer"),
False,
),
(
"nested_typeref",
"2c7d6...",
"file",
capa.features.file.Import("Sent::ContentUri"),
False,
@@ -1426,33 +1340,33 @@ FEATURE_PRESENCE_TESTS_IDA = [
FEATURE_BINJA_DATABASE_TESTS = sorted(
[
# insn/regex
("pma16-01_binja_db", "function=0x4021B0", capa.features.common.Regex("HTTP/1.0"), True),
("pma16-01.exe_.bndb", "function=0x4021B0", capa.features.common.Regex("HTTP/1.0"), True),
(
"pma16-01_binja_db",
"pma16-01.exe_.bndb",
"function=0x402F40",
capa.features.common.Regex("www.practicalmalwareanalysis.com"),
True,
),
(
"pma16-01_binja_db",
"pma16-01.exe_.bndb",
"function=0x402F40",
capa.features.common.Substring("practicalmalwareanalysis.com"),
True,
),
("pma16-01_binja_db", "file", capa.features.file.FunctionName("__aulldiv"), True),
("pma16-01.exe_.bndb", "file", capa.features.file.FunctionName("__aulldiv"), True),
# os & format & arch
("pma16-01_binja_db", "file", OS(OS_WINDOWS), True),
("pma16-01_binja_db", "file", OS(OS_LINUX), False),
("pma16-01_binja_db", "function=0x404356", OS(OS_WINDOWS), True),
("pma16-01_binja_db", "function=0x404356,bb=0x4043B9", OS(OS_WINDOWS), True),
("pma16-01_binja_db", "file", Arch(ARCH_I386), True),
("pma16-01_binja_db", "file", Arch(ARCH_AMD64), False),
("pma16-01_binja_db", "function=0x404356", Arch(ARCH_I386), True),
("pma16-01_binja_db", "function=0x404356,bb=0x4043B9", Arch(ARCH_I386), True),
("pma16-01_binja_db", "file", Format(FORMAT_PE), True),
("pma16-01_binja_db", "file", Format(FORMAT_ELF), False),
("pma16-01.exe_.bndb", "file", OS(OS_WINDOWS), True),
("pma16-01.exe_.bndb", "file", OS(OS_LINUX), False),
("pma16-01.exe_.bndb", "function=0x404356", OS(OS_WINDOWS), True),
("pma16-01.exe_.bndb", "function=0x404356,bb=0x4043B9", OS(OS_WINDOWS), True),
("pma16-01.exe_.bndb", "file", Arch(ARCH_I386), True),
("pma16-01.exe_.bndb", "file", Arch(ARCH_AMD64), False),
("pma16-01.exe_.bndb", "function=0x404356", Arch(ARCH_I386), True),
("pma16-01.exe_.bndb", "function=0x404356,bb=0x4043B9", Arch(ARCH_I386), True),
("pma16-01.exe_.bndb", "file", Format(FORMAT_PE), True),
("pma16-01.exe_.bndb", "file", Format(FORMAT_ELF), False),
# format is also a global feature
("pma16-01_binja_db", "function=0x404356", Format(FORMAT_PE), True),
("pma16-01.exe_.bndb", "function=0x404356", Format(FORMAT_PE), True),
],
# order tests by (file, item)
# so that our LRU cache is most effective.
@@ -1470,8 +1384,8 @@ FEATURE_COUNT_TESTS = [
FEATURE_COUNT_TESTS_DOTNET = [
("_1c444", "token=0x600001D", capa.features.common.Characteristic("calls to"), 1),
("_1c444", "token=0x600001D", capa.features.common.Characteristic("calls from"), 9),
("1c444...", "token=0x600001D", capa.features.common.Characteristic("calls to"), 1),
("1c444...", "token=0x600001D", capa.features.common.Characteristic("calls from"), 9),
]
@@ -1539,12 +1453,12 @@ def z395eb_extractor():
@pytest.fixture
def pma12_04_extractor():
return get_extractor(get_data_path_by_name("pma12-04"))
return get_extractor(get_data_path_by_name("pma12-04.exe_"))
@pytest.fixture
def pma16_01_extractor():
return get_extractor(get_data_path_by_name("pma16-01"))
return get_extractor(get_data_path_by_name("pma16-01.exe_"))
@pytest.fixture
@@ -1554,7 +1468,7 @@ def bfb9b_extractor():
@pytest.fixture
def pma21_01_extractor():
return get_extractor(get_data_path_by_name("pma21-01"))
return get_extractor(get_data_path_by_name("pma21-01.exe_"))
@pytest.fixture
@@ -1574,12 +1488,12 @@ def z499c2_extractor():
@pytest.fixture
def al_khaser_x86_extractor():
return get_extractor(get_data_path_by_name("al-khaser x86"))
return get_extractor(get_data_path_by_name("al-khaser_x86"))
@pytest.fixture
def pingtaest_extractor():
return get_extractor(get_data_path_by_name("pingtaest"))
return get_extractor(get_data_path_by_name("ping_täst"))
@pytest.fixture
@@ -1599,22 +1513,22 @@ def hello_world_dotnetfile_extractor():
@pytest.fixture
def _1c444_dotnetfile_extractor():
return get_dnfile_extractor(get_data_path_by_name("_1c444"))
return get_dnfile_extractor(get_data_path_by_name("1c444..."))
@pytest.fixture
def _692f_dotnetfile_extractor():
return get_dnfile_extractor(get_data_path_by_name("_692f"))
return get_dnfile_extractor(get_data_path_by_name("692f7..."))
@pytest.fixture
def _0953c_dotnetfile_extractor():
return get_dnfile_extractor(get_data_path_by_name("_0953c"))
return get_dnfile_extractor(get_data_path_by_name("0953c..."))
@pytest.fixture
def _039a6_dotnetfile_extractor():
return get_dnfile_extractor(get_data_path_by_name("_039a6"))
return get_dnfile_extractor(get_data_path_by_name("039a6..."))
def get_result_doc(path: Path):

View File

@@ -158,7 +158,7 @@ def test_freeze_bytes_roundtrip():
def test_freeze_load_sample(tmpdir):
o = tmpdir.mkdir("capa").join("test.frz")
extractor = fixtures.get_cape_extractor(fixtures.get_data_path_by_name("d46900"))
extractor = fixtures.get_cape_extractor(fixtures.get_data_path_by_name("d4690..."))
Path(o.strpath).write_bytes(capa.features.freeze.dump(extractor))

View File

@@ -227,7 +227,7 @@ def test_not_render_rules_also_matched(z9324d_extractor, capsys):
def test_json_meta(capsys):
path = str(fixtures.get_data_path_by_name("pma01-01"))
path = str(fixtures.get_data_path_by_name("pma01-01.dll_"))
assert capa.main.main([path, "-j"]) == 0
std = capsys.readouterr()
std_json = json.loads(std.out)
@@ -271,7 +271,7 @@ def test_main_dotnet4(_039a6_dotnetfile_extractor):
def test_main_rd():
path = str(fixtures.get_data_path_by_name("pma01-01-rd"))
path = str(fixtures.get_data_path_by_name("pma01-01.dll_.json"))
assert capa.main.main([path, "-vv"]) == 0
assert capa.main.main([path, "-v"]) == 0
assert capa.main.main([path, "-j"]) == 0

View File

@@ -279,12 +279,12 @@ def test_round_trip(request, rd_file):
def test_json_to_rdoc():
path = fixtures.get_data_path_by_name("pma01-01-rd")
path = fixtures.get_data_path_by_name("pma01-01.dll_.json")
assert isinstance(rdoc.ResultDocument.from_file(path), rdoc.ResultDocument)
def test_rdoc_to_capa():
path = fixtures.get_data_path_by_name("pma01-01-rd")
path = fixtures.get_data_path_by_name("pma01-01.dll_.json")
rd = rdoc.ResultDocument.from_file(path)

View File

@@ -35,7 +35,7 @@ DYNAMIC_VMRAY_FEATURE_PRESENCE_TESTS = sorted(
("93b2d1-vmray", "process=(2176:0),thread=2420", capa.features.insn.API("DoesNotExist"), False),
# call/api
("93b2d1-vmray", "process=(2176:0),thread=2420,call=2361", capa.features.insn.API("GetAddrInfoW"), True),
("eb1287-vmray", "process=(4968:0),thread=5992,call=10981", capa.features.insn.API("CreateMutexW"), True),
("vmray/eb1287...", "process=(4968:0),thread=5992,call=10981", capa.features.insn.API("CreateMutexW"), True),
# call/string argument
(
"93b2d1-vmray",