diff --git a/CHANGELOG.md b/CHANGELOG.md index 77bfaa3b2..0d70b501e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - bump minimum Python version to 3.8.1 to satisfy uv #2387 @williballenthin - vmray: collect more process information from flog.xml #2394 @mr-tz @mike-hunhoff - replace tabulate, tqdm, and termcolor with rich #2374 @s-ff +- dynamic: emit complete features for A/W APIs #2409 @mike-hunhoff ### capa explorer IDA Pro plugin diff --git a/capa/features/extractors/cape/call.py b/capa/features/extractors/cape/call.py index 88680b3fa..0bee22fcc 100644 --- a/capa/features/extractors/cape/call.py +++ b/capa/features/extractors/cape/call.py @@ -9,6 +9,7 @@ import logging from typing import Tuple, Iterator +import capa.features.extractors.helpers from capa.helpers import assert_never from capa.features.insn import API, Number from capa.features.common import String, Feature @@ -50,7 +51,8 @@ def extract_call_features(ph: ProcessHandle, th: ThreadHandle, ch: CallHandle) - else: assert_never(value) - yield API(call.api), ch.address + for name in capa.features.extractors.helpers.generate_symbols("", call.api): + yield API(name), ch.address def extract_features(ph: ProcessHandle, th: ThreadHandle, ch: CallHandle) -> Iterator[Tuple[Feature, Address]]: diff --git a/capa/features/extractors/drakvuf/call.py b/capa/features/extractors/drakvuf/call.py index 34e877acc..7d0e2a5ee 100644 --- a/capa/features/extractors/drakvuf/call.py +++ b/capa/features/extractors/drakvuf/call.py @@ -9,6 +9,7 @@ import logging from typing import Tuple, Iterator +import capa.features.extractors.helpers from capa.features.insn import API, Number from capa.features.common import String, Feature from capa.features.address import Address @@ -44,7 +45,8 @@ def extract_call_features(ph: ProcessHandle, th: ThreadHandle, ch: CallHandle) - # but yielding the entire string would be helpful for an analyst looking at the verbose output yield String(arg_value), ch.address - yield API(call.name), ch.address + for name in capa.features.extractors.helpers.generate_symbols("", call.name): + yield API(name), ch.address def extract_features(ph: ProcessHandle, th: ThreadHandle, ch: CallHandle) -> Iterator[Tuple[Feature, Address]]: diff --git a/capa/features/extractors/vmray/call.py b/capa/features/extractors/vmray/call.py index 436b4bebb..6b87d7d89 100644 --- a/capa/features/extractors/vmray/call.py +++ b/capa/features/extractors/vmray/call.py @@ -8,6 +8,7 @@ import logging from typing import Tuple, Iterator +import capa.features.extractors.helpers from capa.features.insn import API, Number from capa.features.common import String, Feature from capa.features.address import Address @@ -41,7 +42,8 @@ def extract_call_features(ph: ProcessHandle, th: ThreadHandle, ch: CallHandle) - for param in call.params_in.params: yield from get_call_param_features(param, ch) - yield API(call.name), ch.address + for name in capa.features.extractors.helpers.generate_symbols("", call.name): + yield API(name), ch.address def extract_features(ph: ProcessHandle, th: ThreadHandle, ch: CallHandle) -> Iterator[Tuple[Feature, Address]]: diff --git a/tests/test_cape_features.py b/tests/test_cape_features.py index d72caa9ab..ade933e73 100644 --- a/tests/test_cape_features.py +++ b/tests/test_cape_features.py @@ -37,6 +37,8 @@ ), ("0000a657", "process=(1180:3052)", capa.features.common.String("nope"), False), # thread/api calls + ("0000a657", "process=(2900:2852),thread=2904", capa.features.insn.API("RegQueryValueExA"), True), + ("0000a657", "process=(2900:2852),thread=2904", capa.features.insn.API("RegQueryValueEx"), True), ("0000a657", "process=(2852:3052),thread=2804", capa.features.insn.API("NtQueryValueKey"), True), ("0000a657", "process=(2852:3052),thread=2804", capa.features.insn.API("GetActiveWindow"), False), # thread/number call argument diff --git a/tests/test_drakvuf_features.py b/tests/test_drakvuf_features.py index 61fe69442..132b02ddc 100644 --- a/tests/test_drakvuf_features.py +++ b/tests/test_drakvuf_features.py @@ -22,6 +22,8 @@ ("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-drakvuf", "process=(3564:4852),thread=4716,call=17", capa.features.insn.API("CreateWindowExW"), True), + ("93b2d1-drakvuf", "process=(3564:4852),thread=4716,call=17", capa.features.insn.API("CreateWindowEx"), True), ("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 diff --git a/tests/test_vmray_features.py b/tests/test_vmray_features.py index 02eb683ec..e0803236d 100644 --- a/tests/test_vmray_features.py +++ b/tests/test_vmray_features.py @@ -19,8 +19,12 @@ ("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), + ("93b2d1-vmray", "file", capa.features.file.Import("GetAddrInfo"), True), # thread/api calls + ("93b2d1-vmray", "process=(2176:0),thread=2180", capa.features.insn.API("LoadLibraryExA"), True), + ("93b2d1-vmray", "process=(2176:0),thread=2180", capa.features.insn.API("LoadLibraryEx"), True), ("93b2d1-vmray", "process=(2176:0),thread=2420", capa.features.insn.API("GetAddrInfoW"), True), + ("93b2d1-vmray", "process=(2176:0),thread=2420", capa.features.insn.API("GetAddrInfo"), True), ("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),