fix: implement extract_function_loop in dnfile extractor

The stub always raised NotImplementedError and was not registered in
FUNCTION_HANDLERS, so loop detection was silently skipped for all .NET
samples. Detects backward branches (target offset < instruction offset)
as loops, matching the approach used by other extractors.
This commit is contained in:
Willi Ballenthin
2026-04-22 20:02:54 +03:00
committed by Willi Ballenthin
parent f9df8f0a5c
commit 69a1ba862c
3 changed files with 69 additions and 12 deletions
+1
View File
@@ -50,6 +50,7 @@
- fix: correct wrong dict key in VMRay _compute_monitor_threads assertion (used thread_id instead of process_id) @williballenthin
fix: replace assert with isinstance guard in get_callee for invalid MethodSpec tokens @williballenthin
- fix: replace assert with isinstance guard in get_callee for invalid MethodSpec tokens @williballenthin
- fix: implement extract_function_loop in dnfile extractor to detect backward branches as loops @williballenthin (SURF-55)
- fix: remove dead find_process function and helpers.py from cape extractor @williballenthin (SURF-54)
- fix: remove dead interface_extract_* stub functions from viv basicblock, function, and insn extractors @williballenthin (SURF-53)
- fix: remove unused import of capa.features.extractors.strings from binexport2 intel insn.py @williballenthin (SURF-52)
+26 -2
View File
@@ -18,6 +18,8 @@ from __future__ import annotations
import logging
from typing import Iterator
from dncil.cil.opcode import FlowControl
from capa.features.common import Feature, Characteristic
from capa.features.address import Address
from capa.features.extractors.base_extractor import FunctionHandle
@@ -45,7 +47,24 @@ def extract_recursive_call(fh: FunctionHandle) -> Iterator[tuple[Characteristic,
def extract_function_loop(fh: FunctionHandle) -> Iterator[tuple[Characteristic, Address]]:
"""extract loop indicators from a function"""
raise NotImplementedError()
for insn in fh.inner.instructions:
if insn.opcode.flow_control not in (
FlowControl.Branch,
FlowControl.Cond_Branch,
):
continue
if isinstance(insn.operand, list):
targets = insn.operand
elif isinstance(insn.operand, int):
targets = [insn.operand]
else:
continue
for target in targets:
if target < insn.offset:
yield Characteristic("loop"), fh.address
return
def extract_features(fh: FunctionHandle) -> Iterator[tuple[Feature, Address]]:
@@ -54,4 +73,9 @@ def extract_features(fh: FunctionHandle) -> Iterator[tuple[Feature, Address]]:
yield feature, addr
FUNCTION_HANDLERS = (extract_function_calls_to, extract_function_calls_from, extract_recursive_call)
FUNCTION_HANDLERS = (
extract_function_calls_to,
extract_function_calls_from,
extract_recursive_call,
extract_function_loop,
)
+42 -10
View File
@@ -1185,19 +1185,46 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
("_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", "token=0x600001D", capa.features.common.Characteristic("loop"), True),
("_1c444", "token=0x6000001", capa.features.common.Characteristic("loop"), 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
(
"_692f",
"token=0x6000004",
capa.features.insn.API("System.Linq.Enumerable::First"),
True,
), # generic method
(
"_692f",
"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
(
"_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
(
"_1c444",
"function=0x1F59, bb=0x1F59, insn=0x1F5B",
@@ -1236,7 +1263,8 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
"_1c444",
"token=0x6000081",
capa.features.insn.Property(
"System.Diagnostics.ProcessStartInfo::UseShellExecute", access=FeatureAccess.WRITE
"System.Diagnostics.ProcessStartInfo::UseShellExecute",
access=FeatureAccess.WRITE,
), # MemberRef property access
True,
),
@@ -1244,7 +1272,8 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
"_1c444",
"token=0x6000081",
capa.features.insn.Property(
"System.Diagnostics.ProcessStartInfo::WorkingDirectory", access=FeatureAccess.WRITE
"System.Diagnostics.ProcessStartInfo::WorkingDirectory",
access=FeatureAccess.WRITE,
), # MemberRef property access
True,
),
@@ -1252,7 +1281,8 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
"_1c444",
"token=0x6000081",
capa.features.insn.Property(
"System.Diagnostics.ProcessStartInfo::FileName", access=FeatureAccess.WRITE
"System.Diagnostics.ProcessStartInfo::FileName",
access=FeatureAccess.WRITE,
), # MemberRef property access
True,
),
@@ -1316,7 +1346,8 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
"_692f",
"token=0x6000006",
capa.features.insn.Property(
"System.Management.Automation.PowerShell::Streams", access=FeatureAccess.READ
"System.Management.Automation.PowerShell::Streams",
access=FeatureAccess.READ,
), # MemberRef property access
False,
),
@@ -1365,7 +1396,8 @@ FEATURE_PRESENCE_TESTS_DOTNET = sorted(
"_039a6",
"token=0x6000023",
capa.features.insn.Property(
"System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Task", access=FeatureAccess.READ
"System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Task",
access=FeatureAccess.READ,
), # MemberRef method
False,
),