mirror of
https://github.com/mandiant/capa.git
synced 2026-06-12 11:01:31 -07:00
committed by
Willi Ballenthin
parent
8e464e6041
commit
8fca21f808
@@ -60,6 +60,7 @@
|
||||
- fix: FeatureRegexRegistryControlSetMatchIncomplete now checks all Regex features instead of returning after the first @williballenthin (SURF-84)
|
||||
- fix: MissingStaticScope and MissingDynamicScope lint checks guard against absent scopes dict to prevent TypeError @williballenthin (SURF-83)
|
||||
- fix: MissingExampleOffset lint now reads scopes.static instead of obsolete scope key @williballenthin (SURF-82)
|
||||
- fix: extend MissingExampleOffset lint to validate dynamic examples using (pid:N,tid:N,call:N) format @williballenthin #3058
|
||||
- fix: invert scope filter in import-to-ida.py so function-scope rules are annotated instead of skipped @williballenthin (SURF-81)
|
||||
- fix: remove dead string literal in test_detect_duplicate_features @williballenthin (SURF-80)
|
||||
- fix: remove duplicate Rule.from_yaml call in test_scope_instruction_description @williballenthin (SURF-79)
|
||||
|
||||
+32
-8
@@ -247,17 +247,41 @@ class MissingExamples(Lint):
|
||||
|
||||
class MissingExampleOffset(Lint):
|
||||
name = "missing example offset"
|
||||
recommendation = "Add offset of example function"
|
||||
recommendation = "Add offset of example (static: hash:0xADDR, dynamic: hash:(pid:N,tid:N,call:N))"
|
||||
|
||||
STATIC_SCOPES_NEEDING_OFFSET = ("function", "basic block")
|
||||
DYNAMIC_SCOPES_NEEDING_OFFSET = ("process", "thread", "call", "span of calls")
|
||||
|
||||
def check_rule(self, ctx: Context, rule: Rule):
|
||||
static_scope = rule.meta.get("scopes", {}).get("static")
|
||||
if static_scope in ("function", "basic block"):
|
||||
examples = rule.meta.get("examples")
|
||||
if isinstance(examples, list):
|
||||
for example in examples:
|
||||
if example and ":" not in example:
|
||||
logger.debug("example: %s", example)
|
||||
scopes = rule.meta.get("scopes", {})
|
||||
static_scope = scopes.get("static")
|
||||
dynamic_scope = scopes.get("dynamic")
|
||||
|
||||
examples = rule.meta.get("examples")
|
||||
if not isinstance(examples, list):
|
||||
return False
|
||||
|
||||
for example in examples:
|
||||
if not example:
|
||||
continue
|
||||
|
||||
example_id, _, offset = example.partition(":")
|
||||
|
||||
sample_path = ctx.samples.get(example_id)
|
||||
is_dynamic_sample = sample_path is not None and "dynamic" in sample_path.parts
|
||||
|
||||
if is_dynamic_sample:
|
||||
if dynamic_scope in self.DYNAMIC_SCOPES_NEEDING_OFFSET:
|
||||
if not offset or not offset.startswith("("):
|
||||
logger.debug("example: %s (missing dynamic offset)", example)
|
||||
return True
|
||||
else:
|
||||
if static_scope in self.STATIC_SCOPES_NEEDING_OFFSET:
|
||||
if not offset:
|
||||
logger.debug("example: %s (missing static offset)", example)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class ExampleFileDNE(Lint):
|
||||
|
||||
+58
-1
@@ -282,7 +282,6 @@ def test_missing_example_offset_uses_scopes():
|
||||
import lint as lint_module
|
||||
|
||||
lint_instance = lint_module.MissingExampleOffset()
|
||||
ctx = lint_module.Context(samples={}, rules=capa.rules.RuleSet([]), is_thorough=False)
|
||||
|
||||
function_scope_rule_missing_offset = capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent("""
|
||||
@@ -298,6 +297,10 @@ def test_missing_example_offset_uses_scopes():
|
||||
- api: CreateFile
|
||||
""")
|
||||
)
|
||||
|
||||
rules = capa.rules.RuleSet([function_scope_rule_missing_offset])
|
||||
ctx = lint_module.Context(samples={}, rules=rules, is_thorough=False)
|
||||
|
||||
assert lint_instance.check_rule(ctx, function_scope_rule_missing_offset) is True
|
||||
|
||||
function_scope_rule_with_offset = capa.rules.Rule.from_yaml(
|
||||
@@ -332,6 +335,60 @@ def test_missing_example_offset_uses_scopes():
|
||||
)
|
||||
assert lint_instance.check_rule(ctx, file_scope_rule_no_offset) is not True
|
||||
|
||||
ctx_with_dynamic = lint_module.Context(
|
||||
samples={"abc123_min_archive.zip": Path("tests/data/dynamic/vmray/abc123_min_archive.zip")},
|
||||
rules=rules,
|
||||
is_thorough=False,
|
||||
)
|
||||
|
||||
dynamic_example_missing_offset = capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule dynamic example missing offset
|
||||
scopes:
|
||||
static: basic block
|
||||
dynamic: call
|
||||
examples:
|
||||
- abc123_min_archive.zip
|
||||
features:
|
||||
- api: CreateFile
|
||||
""")
|
||||
)
|
||||
assert lint_instance.check_rule(ctx_with_dynamic, dynamic_example_missing_offset) is True
|
||||
|
||||
dynamic_example_with_offset = capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule dynamic example with offset
|
||||
scopes:
|
||||
static: basic block
|
||||
dynamic: call
|
||||
examples:
|
||||
- abc123_min_archive.zip:(pid:2932,tid:2928,call:354)
|
||||
features:
|
||||
- api: CreateFile
|
||||
""")
|
||||
)
|
||||
assert lint_instance.check_rule(ctx_with_dynamic, dynamic_example_with_offset) is not True
|
||||
|
||||
dynamic_file_scope_no_offset = capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule dynamic file scope no offset
|
||||
scopes:
|
||||
static: file
|
||||
dynamic: file
|
||||
examples:
|
||||
- abc123_min_archive.zip
|
||||
features:
|
||||
- string: test
|
||||
""")
|
||||
)
|
||||
assert lint_instance.check_rule(ctx_with_dynamic, dynamic_file_scope_no_offset) is not True
|
||||
|
||||
|
||||
def test_feature_regex_registry_control_set_checks_all_features():
|
||||
sys.path.insert(0, str(CD / ".." / "scripts"))
|
||||
|
||||
Reference in New Issue
Block a user