mirror of
https://github.com/mandiant/capa.git
synced 2026-03-12 21:23:12 -07:00
style: auto-format with black and isort
This commit is contained in:
@@ -20,6 +20,7 @@ Proto files generated via protobuf v24.4:
|
||||
from BinExport2 at 6916731d5f6693c4a4f0a052501fd3bd92cfd08b
|
||||
https://github.com/google/binexport/blob/6916731/binexport2.proto
|
||||
"""
|
||||
|
||||
import io
|
||||
import hashlib
|
||||
import logging
|
||||
|
||||
@@ -84,16 +84,14 @@ def extract_insn_number_features(
|
||||
yield OperandOffset(i, value), ih.address
|
||||
|
||||
|
||||
OFFSET_PATTERNS = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
OFFSET_PATTERNS = BinExport2InstructionPatternMatcher.from_str("""
|
||||
ldr|ldrb|ldrh|ldrsb|ldrsh|ldrex|ldrd|str|strb|strh|strex|strd reg, [reg(not-stack), #int] ; capture #int
|
||||
ldr|ldrb|ldrh|ldrsb|ldrsh|ldrex|ldrd|str|strb|strh|strex|strd reg, [reg(not-stack), #int]! ; capture #int
|
||||
ldr|ldrb|ldrh|ldrsb|ldrsh|ldrex|ldrd|str|strb|strh|strex|strd reg, [reg(not-stack)], #int ; capture #int
|
||||
ldp|ldpd|stp|stpd reg, reg, [reg(not-stack), #int] ; capture #int
|
||||
ldp|ldpd|stp|stpd reg, reg, [reg(not-stack), #int]! ; capture #int
|
||||
ldp|ldpd|stp|stpd reg, reg, [reg(not-stack)], #int ; capture #int
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
def extract_insn_offset_features(
|
||||
@@ -117,12 +115,10 @@ def extract_insn_offset_features(
|
||||
yield OperandOffset(match.operand_index, value), ih.address
|
||||
|
||||
|
||||
NZXOR_PATTERNS = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
NZXOR_PATTERNS = BinExport2InstructionPatternMatcher.from_str("""
|
||||
eor reg, reg, reg
|
||||
eor reg, reg, #int
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
def extract_insn_nzxor_characteristic_features(
|
||||
@@ -144,11 +140,9 @@ def extract_insn_nzxor_characteristic_features(
|
||||
yield Characteristic("nzxor"), ih.address
|
||||
|
||||
|
||||
INDIRECT_CALL_PATTERNS = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
INDIRECT_CALL_PATTERNS = BinExport2InstructionPatternMatcher.from_str("""
|
||||
blx|bx|blr reg
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
def extract_function_indirect_call_characteristic_features(
|
||||
|
||||
@@ -34,17 +34,14 @@ from capa.features.extractors.binexport2.arch.intel.helpers import SECURITY_COOK
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
IGNORE_NUMBER_PATTERNS = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
IGNORE_NUMBER_PATTERNS = BinExport2InstructionPatternMatcher.from_str("""
|
||||
ret #int
|
||||
retn #int
|
||||
add reg(stack), #int
|
||||
sub reg(stack), #int
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
NUMBER_PATTERNS = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
NUMBER_PATTERNS = BinExport2InstructionPatternMatcher.from_str("""
|
||||
push #int0 ; capture #int0
|
||||
|
||||
# its a little tedious to enumerate all the address forms
|
||||
@@ -64,8 +61,7 @@ NUMBER_PATTERNS = BinExport2InstructionPatternMatcher.from_str(
|
||||
# imagine reg is zero'd out, then this is like `mov reg, #int`
|
||||
# which is not uncommon.
|
||||
lea reg, [reg + #int] ; capture #int
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
def extract_insn_number_features(
|
||||
@@ -100,8 +96,7 @@ def extract_insn_number_features(
|
||||
yield OperandOffset(match.operand_index, value), ih.address
|
||||
|
||||
|
||||
OFFSET_PATTERNS = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
OFFSET_PATTERNS = BinExport2InstructionPatternMatcher.from_str("""
|
||||
mov|movzx|movsb|cmp [reg + reg * #int + #int0], #int ; capture #int0
|
||||
mov|movzx|movsb|cmp [reg * #int + #int0], #int ; capture #int0
|
||||
mov|movzx|movsb|cmp [reg + reg + #int0], #int ; capture #int0
|
||||
@@ -114,18 +109,15 @@ OFFSET_PATTERNS = BinExport2InstructionPatternMatcher.from_str(
|
||||
mov|movzx|movsb|cmp|lea reg, [reg * #int + #int0] ; capture #int0
|
||||
mov|movzx|movsb|cmp|lea reg, [reg + reg + #int0] ; capture #int0
|
||||
mov|movzx|movsb|cmp|lea reg, [reg(not-stack) + #int0] ; capture #int0
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
# these are patterns that access offset 0 from some pointer
|
||||
# (pointer is not the stack pointer).
|
||||
OFFSET_ZERO_PATTERNS = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
OFFSET_ZERO_PATTERNS = BinExport2InstructionPatternMatcher.from_str("""
|
||||
mov|movzx|movsb [reg(not-stack)], reg
|
||||
mov|movzx|movsb [reg(not-stack)], #int
|
||||
lea reg, [reg(not-stack)]
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
def extract_insn_offset_features(
|
||||
@@ -189,12 +181,10 @@ def is_security_cookie(
|
||||
return False
|
||||
|
||||
|
||||
NZXOR_PATTERNS = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
NZXOR_PATTERNS = BinExport2InstructionPatternMatcher.from_str("""
|
||||
xor|xorpd|xorps|pxor reg, reg
|
||||
xor|xorpd|xorps|pxor reg, #int
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
def extract_insn_nzxor_characteristic_features(
|
||||
@@ -228,8 +218,7 @@ def extract_insn_nzxor_characteristic_features(
|
||||
yield Characteristic("nzxor"), ih.address
|
||||
|
||||
|
||||
INDIRECT_CALL_PATTERNS = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
INDIRECT_CALL_PATTERNS = BinExport2InstructionPatternMatcher.from_str("""
|
||||
call|jmp reg0
|
||||
call|jmp [reg + reg * #int + #int]
|
||||
call|jmp [reg + reg * #int]
|
||||
@@ -237,8 +226,7 @@ INDIRECT_CALL_PATTERNS = BinExport2InstructionPatternMatcher.from_str(
|
||||
call|jmp [reg + reg + #int]
|
||||
call|jmp [reg + #int]
|
||||
call|jmp [reg]
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
def extract_function_indirect_call_characteristic_features(
|
||||
|
||||
@@ -56,7 +56,7 @@ def get_previous_instructions(vw: VivWorkspace, va: int) -> list[int]:
|
||||
if ploc is not None:
|
||||
# from vivisect.const:
|
||||
# location: (L_VA, L_SIZE, L_LTYPE, L_TINFO)
|
||||
(pva, _, ptype, pinfo) = ploc
|
||||
pva, _, ptype, pinfo = ploc
|
||||
|
||||
if ptype == LOC_OP and not (pinfo & IF_NOFALL):
|
||||
ret.append(pva)
|
||||
|
||||
@@ -176,7 +176,7 @@ def extract_insn_api_features(fh: FunctionHandle, bb, ih: InsnHandle) -> Iterato
|
||||
|
||||
elif isinstance(insn.opers[0], envi.archs.i386.disasm.i386RegOper):
|
||||
try:
|
||||
(_, target) = resolve_indirect_call(f.vw, insn.va, insn=insn)
|
||||
_, target = resolve_indirect_call(f.vw, insn.va, insn=insn)
|
||||
except NotFoundError:
|
||||
# not able to resolve the indirect call, sorry
|
||||
return
|
||||
|
||||
@@ -945,8 +945,7 @@ def main(argv: Optional[list[str]] = None):
|
||||
argv = sys.argv[1:]
|
||||
|
||||
desc = "The FLARE team's open-source tool to identify capabilities in executable files."
|
||||
epilog = textwrap.dedent(
|
||||
"""
|
||||
epilog = textwrap.dedent("""
|
||||
By default, capa uses a default set of embedded rules.
|
||||
You can see the rule set here:
|
||||
https://github.com/mandiant/capa-rules
|
||||
@@ -973,8 +972,7 @@ def main(argv: Optional[list[str]] = None):
|
||||
|
||||
filter rules by meta fields, e.g. rule name or namespace
|
||||
capa -t "create TCP socket" suspicious.exe
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=desc, epilog=epilog, formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
|
||||
@@ -31,6 +31,7 @@ $ protoc.exe --python_out=. --mypy_out=. <path_to_proto> (e.g. capa/render/proto
|
||||
|
||||
Alternatively, --pyi_out=. can be used to generate a Python Interface file that supports development
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from typing import Any, Union
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ usage:
|
||||
parallelism factor
|
||||
--no-mp disable subprocesses
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
import logging
|
||||
|
||||
@@ -28,6 +28,7 @@ Requires:
|
||||
- sarif_om 1.0.4
|
||||
- jschema_to_python 1.2.3
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
import logging
|
||||
|
||||
@@ -32,6 +32,7 @@ Example:
|
||||
│00000070│ 39 31 37 36 61 64 36 38 ┊ 32 66 66 64 64 36 35 66 │9176ad68┊2ffdd65f│
|
||||
│00000080│ 30 61 36 36 39 12 28 61 ┊ 34 62 33 35 64 65 37 31 │0a669•(a┊4b35de71│
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
@@ -18,6 +18,7 @@ detect-elf-os
|
||||
|
||||
Attempt to detect the underlying OS that the given ELF file targets.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
@@ -36,6 +36,7 @@ Check the log window for any errors, and/or the summary of changes.
|
||||
|
||||
Derived from: https://github.com/mandiant/capa/blob/master/scripts/import-to-ida.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
@@ -54,6 +54,7 @@ Example::
|
||||
0x44cb60: ?
|
||||
0x44cba0: __guard_icall_checks_enforced
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"""
|
||||
Extract files relevant to capa analysis from VMRay Analysis Archive and create a new ZIP file.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import zipfile
|
||||
|
||||
@@ -43,6 +43,7 @@ example:
|
||||
|
||||
^^^ --label or git hash
|
||||
"""
|
||||
|
||||
import sys
|
||||
import timeit
|
||||
import logging
|
||||
|
||||
@@ -34,6 +34,7 @@ Example:
|
||||
│00000080│ 30 61 36 36 39 12 28 61 ┊ 34 62 33 35 64 65 37 31 │0a669•(a┊4b35de71│
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
@@ -37,6 +37,7 @@ Example:
|
||||
────┴────────────────────────────────────────────────────
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
@@ -46,6 +46,7 @@ Example:
|
||||
2022-01-24 22:35:39,839 [INFO] Starting extraction...
|
||||
2022-01-24 22:35:42,632 [INFO] Writing results to linter-data.json
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
@@ -54,6 +54,7 @@ Example::
|
||||
- connect TCP socket
|
||||
...
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
@@ -70,6 +70,7 @@ Example::
|
||||
insn: 0x10001027: mnemonic(shl)
|
||||
...
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
@@ -458,9 +458,7 @@ def test_pattern_parsing():
|
||||
capture="#int",
|
||||
)
|
||||
|
||||
assert (
|
||||
BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
assert BinExport2InstructionPatternMatcher.from_str("""
|
||||
# comment
|
||||
br reg
|
||||
br reg(not-stack)
|
||||
@@ -481,10 +479,7 @@ def test_pattern_parsing():
|
||||
call [reg * #int + #int]
|
||||
call [reg + reg + #int]
|
||||
call [reg + #int]
|
||||
"""
|
||||
).queries
|
||||
is not None
|
||||
)
|
||||
""").queries is not None
|
||||
|
||||
|
||||
def match_address(extractor: BinExport2FeatureExtractor, queries: BinExport2InstructionPatternMatcher, address: int):
|
||||
@@ -507,8 +502,7 @@ def match_address_with_be2(
|
||||
|
||||
|
||||
def test_pattern_matching():
|
||||
queries = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
queries = BinExport2InstructionPatternMatcher.from_str("""
|
||||
br reg(stack) ; capture reg
|
||||
br reg(not-stack) ; capture reg
|
||||
mov reg0, reg1 ; capture reg0
|
||||
@@ -522,8 +516,7 @@ def test_pattern_matching():
|
||||
ldp|stp reg, reg, [reg, #int]! ; capture #int
|
||||
ldp|stp reg, reg, [reg], #int ; capture #int
|
||||
ldrb reg0, [reg1(not-stack), reg2] ; capture reg2
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
# 0x210184: ldrb w2, [x0, x1]
|
||||
# query: ldrb reg0, [reg1(not-stack), reg2] ; capture reg2"
|
||||
@@ -550,11 +543,9 @@ BE2_EXTRACTOR_687 = fixtures.get_binexport_extractor(
|
||||
|
||||
|
||||
def test_pattern_matching_exclamation():
|
||||
queries = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
queries = BinExport2InstructionPatternMatcher.from_str("""
|
||||
stp reg, reg, [reg, #int]! ; capture #int
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
# note this captures the sp
|
||||
# 0x107918: stp x20, x19, [sp,0xFFFFFFFFFFFFFFE0]!
|
||||
@@ -564,11 +555,9 @@ def test_pattern_matching_exclamation():
|
||||
|
||||
|
||||
def test_pattern_matching_stack():
|
||||
queries = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
queries = BinExport2InstructionPatternMatcher.from_str("""
|
||||
stp reg, reg, [reg(stack), #int]! ; capture #int
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
# note this does capture the sp
|
||||
# compare this with the test above (exclamation)
|
||||
@@ -579,11 +568,9 @@ def test_pattern_matching_stack():
|
||||
|
||||
|
||||
def test_pattern_matching_not_stack():
|
||||
queries = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
queries = BinExport2InstructionPatternMatcher.from_str("""
|
||||
stp reg, reg, [reg(not-stack), #int]! ; capture #int
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
# note this does not capture the sp
|
||||
# compare this with the test above (exclamation)
|
||||
@@ -597,11 +584,9 @@ BE2_EXTRACTOR_MIMI = fixtures.get_binexport_extractor(CD / "data" / "binexport2"
|
||||
|
||||
|
||||
def test_pattern_matching_x86():
|
||||
queries = BinExport2InstructionPatternMatcher.from_str(
|
||||
"""
|
||||
queries = BinExport2InstructionPatternMatcher.from_str("""
|
||||
cmp|lea reg, [reg(not-stack) + #int0] ; capture #int0
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
# 0x4018c0: LEA ECX, [EBX+0x2]
|
||||
# query: cmp|lea reg, [reg(not-stack) + #int0] ; capture #int0
|
||||
|
||||
@@ -23,9 +23,7 @@ def test_match_across_scopes_file_function(z9324d_extractor):
|
||||
rules = capa.rules.RuleSet(
|
||||
[
|
||||
# this rule should match on a function (0x4073F0)
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: install service
|
||||
@@ -39,13 +37,9 @@ def test_match_across_scopes_file_function(z9324d_extractor):
|
||||
- api: advapi32.OpenSCManagerA
|
||||
- api: advapi32.CreateServiceA
|
||||
- api: advapi32.StartServiceA
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
# this rule should match on a file feature
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: .text section
|
||||
@@ -56,15 +50,11 @@ def test_match_across_scopes_file_function(z9324d_extractor):
|
||||
- 9324d1a8ae37a36ae560c37448c9705a
|
||||
features:
|
||||
- section: .text
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
# this rule should match on earlier rule matches:
|
||||
# - install service, with function scope
|
||||
# - .text section, with file scope
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: .text section and install service
|
||||
@@ -77,9 +67,7 @@ def test_match_across_scopes_file_function(z9324d_extractor):
|
||||
- and:
|
||||
- match: install service
|
||||
- match: .text section
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
]
|
||||
)
|
||||
capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor)
|
||||
@@ -92,9 +80,7 @@ def test_match_across_scopes(z9324d_extractor):
|
||||
rules = capa.rules.RuleSet(
|
||||
[
|
||||
# this rule should match on a basic block (including at least 0x403685)
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: tight loop
|
||||
@@ -105,14 +91,10 @@ def test_match_across_scopes(z9324d_extractor):
|
||||
- 9324d1a8ae37a36ae560c37448c9705a:0x403685
|
||||
features:
|
||||
- characteristic: tight loop
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
# this rule should match on a function (0x403660)
|
||||
# based on API, as well as prior basic block rule match
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: kill thread loop
|
||||
@@ -126,13 +108,9 @@ def test_match_across_scopes(z9324d_extractor):
|
||||
- api: kernel32.TerminateThread
|
||||
- api: kernel32.CloseHandle
|
||||
- match: tight loop
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
# this rule should match on a file feature and a prior function rule match
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: kill thread program
|
||||
@@ -145,9 +123,7 @@ def test_match_across_scopes(z9324d_extractor):
|
||||
- and:
|
||||
- section: .text
|
||||
- match: kill thread loop
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
]
|
||||
)
|
||||
capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor)
|
||||
@@ -157,11 +133,7 @@ def test_match_across_scopes(z9324d_extractor):
|
||||
|
||||
|
||||
def test_subscope_bb_rules(z9324d_extractor):
|
||||
rules = capa.rules.RuleSet(
|
||||
[
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
rules = capa.rules.RuleSet([capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -172,22 +144,14 @@ def test_subscope_bb_rules(z9324d_extractor):
|
||||
- and:
|
||||
- basic block:
|
||||
- characteristic: tight loop
|
||||
"""
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
"""))])
|
||||
# tight loop at 0x403685
|
||||
capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor)
|
||||
assert "test rule" in capabilities.matches
|
||||
|
||||
|
||||
def test_match_specific_functions(z9324d_extractor):
|
||||
rules = capa.rules.RuleSet(
|
||||
[
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
rules = capa.rules.RuleSet([capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: receive data
|
||||
@@ -199,11 +163,7 @@ def test_match_specific_functions(z9324d_extractor):
|
||||
features:
|
||||
- or:
|
||||
- api: recv
|
||||
"""
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
"""))])
|
||||
extractor = FunctionFilter(z9324d_extractor, {0x4019C0})
|
||||
capabilities = capa.capabilities.common.find_capabilities(rules, extractor)
|
||||
matches = capabilities.matches["receive data"]
|
||||
@@ -214,11 +174,7 @@ def test_match_specific_functions(z9324d_extractor):
|
||||
|
||||
|
||||
def test_byte_matching(z9324d_extractor):
|
||||
rules = capa.rules.RuleSet(
|
||||
[
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
rules = capa.rules.RuleSet([capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: byte match test
|
||||
@@ -228,21 +184,13 @@ def test_byte_matching(z9324d_extractor):
|
||||
features:
|
||||
- and:
|
||||
- bytes: ED 24 9E F4 52 A9 07 47 55 8E E1 AB 30 8E 23 61
|
||||
"""
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
"""))])
|
||||
capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor)
|
||||
assert "byte match test" in capabilities.matches
|
||||
|
||||
|
||||
def test_com_feature_matching(z395eb_extractor):
|
||||
rules = capa.rules.RuleSet(
|
||||
[
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
rules = capa.rules.RuleSet([capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: initialize IWebBrowser2
|
||||
@@ -254,21 +202,13 @@ def test_com_feature_matching(z395eb_extractor):
|
||||
- api: ole32.CoCreateInstance
|
||||
- com/class: InternetExplorer #bytes: 01 DF 02 00 00 00 00 00 C0 00 00 00 00 00 00 46 = CLSID_InternetExplorer
|
||||
- com/interface: IWebBrowser2 #bytes: 61 16 0C D3 AF CD D0 11 8A 3E 00 C0 4F C9 E2 6E = IID_IWebBrowser2
|
||||
"""
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
"""))])
|
||||
capabilities = capa.main.find_capabilities(rules, z395eb_extractor)
|
||||
assert "initialize IWebBrowser2" in capabilities.matches
|
||||
|
||||
|
||||
def test_count_bb(z9324d_extractor):
|
||||
rules = capa.rules.RuleSet(
|
||||
[
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
rules = capa.rules.RuleSet([capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: count bb
|
||||
@@ -279,22 +219,14 @@ def test_count_bb(z9324d_extractor):
|
||||
features:
|
||||
- and:
|
||||
- count(basic blocks): 1 or more
|
||||
"""
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
"""))])
|
||||
capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor)
|
||||
assert "count bb" in capabilities.matches
|
||||
|
||||
|
||||
def test_instruction_scope(z9324d_extractor):
|
||||
# .text:004071A4 68 E8 03 00 00 push 3E8h
|
||||
rules = capa.rules.RuleSet(
|
||||
[
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
rules = capa.rules.RuleSet([capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: push 1000
|
||||
@@ -306,11 +238,7 @@ def test_instruction_scope(z9324d_extractor):
|
||||
- and:
|
||||
- mnemonic: push
|
||||
- number: 1000
|
||||
"""
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
"""))])
|
||||
capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor)
|
||||
assert "push 1000" in capabilities.matches
|
||||
assert 0x4071A4 in {result[0] for result in capabilities.matches["push 1000"]}
|
||||
@@ -320,11 +248,7 @@ def test_instruction_subscope(z9324d_extractor):
|
||||
# .text:00406F60 sub_406F60 proc near
|
||||
# [...]
|
||||
# .text:004071A4 68 E8 03 00 00 push 3E8h
|
||||
rules = capa.rules.RuleSet(
|
||||
[
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
rules = capa.rules.RuleSet([capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: push 1000 on i386
|
||||
@@ -338,11 +262,7 @@ def test_instruction_subscope(z9324d_extractor):
|
||||
- instruction:
|
||||
- mnemonic: push
|
||||
- number: 1000
|
||||
"""
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
"""))])
|
||||
capabilities = capa.capabilities.common.find_capabilities(rules, z9324d_extractor)
|
||||
assert "push 1000 on i386" in capabilities.matches
|
||||
assert 0x406F60 in {result[0] for result in capabilities.matches["push 1000 on i386"]}
|
||||
|
||||
@@ -81,8 +81,7 @@ def test_cape_extractor(version: str, filename: str, exception: Type[BaseExcepti
|
||||
|
||||
|
||||
def test_cape_model_argument():
|
||||
call = Call.model_validate_json(
|
||||
"""
|
||||
call = Call.model_validate_json("""
|
||||
{
|
||||
"timestamp": "2023-10-20 12:30:14,015",
|
||||
"thread_id": "2380",
|
||||
@@ -105,7 +104,6 @@ def test_cape_model_argument():
|
||||
"repeated": 19,
|
||||
"id": 0
|
||||
}
|
||||
"""
|
||||
)
|
||||
""")
|
||||
assert call.arguments[0].value == 30
|
||||
assert call.arguments[1].value == 0x30
|
||||
|
||||
@@ -18,8 +18,7 @@ from capa.features.extractors.drakvuf.models import SystemCall
|
||||
|
||||
|
||||
def test_syscall_argument_construction():
|
||||
call_dictionary = json.loads(
|
||||
r"""
|
||||
call_dictionary = json.loads(r"""
|
||||
{
|
||||
"Plugin": "syscall",
|
||||
"TimeStamp": "1716999134.581449",
|
||||
@@ -43,8 +42,7 @@ def test_syscall_argument_construction():
|
||||
"Timeout": "0xfffff506a02846d8",
|
||||
"Alertable": "0x0"
|
||||
}
|
||||
"""
|
||||
)
|
||||
""")
|
||||
call = SystemCall(**call_dictionary)
|
||||
assert len(call.arguments) == call.nargs
|
||||
assert call.arguments["IoCompletionHandle"] == "0xffffffff80001ac0"
|
||||
|
||||
@@ -83,8 +83,7 @@ def get_call_ids(matches) -> Iterator[int]:
|
||||
def test_dynamic_call_scope():
|
||||
extractor = get_0000a657_thread3064()
|
||||
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -93,8 +92,7 @@ def test_dynamic_call_scope():
|
||||
dynamic: call
|
||||
features:
|
||||
- api: GetSystemTimeAsFileTime
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
ruleset = capa.rules.RuleSet([r])
|
||||
@@ -116,8 +114,7 @@ def test_dynamic_call_scope():
|
||||
def test_dynamic_span_scope():
|
||||
extractor = get_0000a657_thread3064()
|
||||
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -131,8 +128,7 @@ def test_dynamic_span_scope():
|
||||
- api: LdrGetDllHandle
|
||||
- api: LdrGetProcedureAddress
|
||||
- count(api(LdrGetDllHandle)): 2
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
ruleset = capa.rules.RuleSet([r])
|
||||
@@ -158,8 +154,7 @@ def test_dynamic_span_scope():
|
||||
def test_dynamic_span_scope_length():
|
||||
extractor = get_0000a657_thread3064()
|
||||
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -170,8 +165,7 @@ def test_dynamic_span_scope_length():
|
||||
- and:
|
||||
- api: GetSystemTimeAsFileTime
|
||||
- api: RtlAddVectoredExceptionHandler
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
ruleset = capa.rules.RuleSet([r])
|
||||
@@ -196,8 +190,7 @@ def test_dynamic_span_scope_length():
|
||||
def test_dynamic_span_call_subscope():
|
||||
extractor = get_0000a657_thread3064()
|
||||
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -210,8 +203,7 @@ def test_dynamic_span_call_subscope():
|
||||
- and:
|
||||
- api: LdrGetProcedureAddress
|
||||
- string: AddVectoredExceptionHandler
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
ruleset = capa.rules.RuleSet([r])
|
||||
@@ -234,8 +226,7 @@ def test_dynamic_span_call_subscope():
|
||||
def test_dynamic_span_scope_span_subscope():
|
||||
extractor = get_0000a657_thread3064()
|
||||
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -256,8 +247,7 @@ def test_dynamic_span_scope_span_subscope():
|
||||
- api: LdrGetDllHandle
|
||||
- api: LdrGetProcedureAddress
|
||||
- string: RemoveVectoredExceptionHandler
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
ruleset = capa.rules.RuleSet([r])
|
||||
@@ -269,8 +259,7 @@ def test_dynamic_span_scope_span_subscope():
|
||||
|
||||
# show that you can't use thread subscope in span rules.
|
||||
def test_dynamic_span_scope_thread_subscope():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -281,8 +270,7 @@ def test_dynamic_span_scope_thread_subscope():
|
||||
- and:
|
||||
- thread:
|
||||
- string: "foo"
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
with pytest.raises(capa.rules.InvalidRule):
|
||||
capa.rules.Rule.from_yaml(rule)
|
||||
@@ -300,8 +288,7 @@ def test_dynamic_span_scope_thread_subscope():
|
||||
def test_dynamic_span_example():
|
||||
extractor = get_0000a657_thread3064()
|
||||
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -319,8 +306,7 @@ def test_dynamic_span_example():
|
||||
- api: LdrGetProcedureAddress
|
||||
- string: "AddVectoredExceptionHandler"
|
||||
- api: RtlAddVectoredExceptionHandler
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
ruleset = capa.rules.RuleSet([r])
|
||||
@@ -345,8 +331,7 @@ def test_dynamic_span_example():
|
||||
def test_dynamic_span_multiple_spans_overlapping_single_event():
|
||||
extractor = get_0000a657_thread3064()
|
||||
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -359,8 +344,7 @@ def test_dynamic_span_multiple_spans_overlapping_single_event():
|
||||
- and:
|
||||
- api: LdrGetProcedureAddress
|
||||
- string: "AddVectoredExceptionHandler"
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
ruleset = capa.rules.RuleSet([r])
|
||||
@@ -386,9 +370,7 @@ def test_dynamic_span_scope_match_statements():
|
||||
|
||||
ruleset = capa.rules.RuleSet(
|
||||
[
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: resolve add VEH
|
||||
@@ -401,12 +383,8 @@ def test_dynamic_span_scope_match_statements():
|
||||
- api: LdrGetDllHandle
|
||||
- api: LdrGetProcedureAddress
|
||||
- string: AddVectoredExceptionHandler
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: resolve remove VEH
|
||||
@@ -419,12 +397,8 @@ def test_dynamic_span_scope_match_statements():
|
||||
- api: LdrGetDllHandle
|
||||
- api: LdrGetProcedureAddress
|
||||
- string: RemoveVectoredExceptionHandler
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: resolve add and remove VEH
|
||||
@@ -435,12 +409,8 @@ def test_dynamic_span_scope_match_statements():
|
||||
- and:
|
||||
- match: resolve add VEH
|
||||
- match: resolve remove VEH
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: has VEH runtime linking
|
||||
@@ -450,9 +420,7 @@ def test_dynamic_span_scope_match_statements():
|
||||
features:
|
||||
- and:
|
||||
- match: linking/runtime-linking/veh
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ import textwrap
|
||||
|
||||
import capa.rules
|
||||
|
||||
EXPECTED = textwrap.dedent(
|
||||
"""\
|
||||
EXPECTED = textwrap.dedent("""\
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -34,13 +33,11 @@ EXPECTED = textwrap.dedent(
|
||||
- and:
|
||||
- number: 1
|
||||
- number: 2
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
def test_rule_reformat_top_level_elements():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
features:
|
||||
- and:
|
||||
@@ -56,15 +53,13 @@ def test_rule_reformat_top_level_elements():
|
||||
examples:
|
||||
- foo1234
|
||||
- bar5678
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
assert capa.rules.Rule.from_yaml(rule).to_yaml() == EXPECTED
|
||||
|
||||
|
||||
def test_rule_reformat_indentation():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -80,15 +75,13 @@ def test_rule_reformat_indentation():
|
||||
- and:
|
||||
- number: 1
|
||||
- number: 2
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
assert capa.rules.Rule.from_yaml(rule).to_yaml() == EXPECTED
|
||||
|
||||
|
||||
def test_rule_reformat_order():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
authors:
|
||||
@@ -104,8 +97,7 @@ def test_rule_reformat_order():
|
||||
- and:
|
||||
- number: 1
|
||||
- number: 2
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
assert capa.rules.Rule.from_yaml(rule).to_yaml() == EXPECTED
|
||||
|
||||
@@ -113,8 +105,7 @@ def test_rule_reformat_order():
|
||||
def test_rule_reformat_meta_update():
|
||||
# test updating the rule content after parsing
|
||||
|
||||
src = textwrap.dedent(
|
||||
"""
|
||||
src = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
authors:
|
||||
@@ -130,8 +121,7 @@ def test_rule_reformat_meta_update():
|
||||
- and:
|
||||
- number: 1
|
||||
- number: 2
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
rule = capa.rules.Rule.from_yaml(src)
|
||||
rule.name = "test rule"
|
||||
@@ -141,8 +131,7 @@ def test_rule_reformat_meta_update():
|
||||
def test_rule_reformat_string_description():
|
||||
# the `description` should be aligned with the preceding feature name.
|
||||
# see #263
|
||||
src = textwrap.dedent(
|
||||
"""
|
||||
src = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -155,8 +144,7 @@ def test_rule_reformat_string_description():
|
||||
- and:
|
||||
- string: foo
|
||||
description: bar
|
||||
"""
|
||||
).lstrip()
|
||||
""").lstrip()
|
||||
|
||||
rule = capa.rules.Rule.from_yaml(src)
|
||||
assert rule.to_yaml() == src
|
||||
|
||||
@@ -108,9 +108,7 @@ def test_null_feature_extractor():
|
||||
|
||||
rules = capa.rules.RuleSet(
|
||||
[
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: create file
|
||||
@@ -120,9 +118,7 @@ def test_null_feature_extractor():
|
||||
features:
|
||||
- and:
|
||||
- api: CreateFile
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
]
|
||||
)
|
||||
capabilities = capa.main.find_capabilities(rules, EXTRACTOR)
|
||||
|
||||
@@ -88,9 +88,7 @@ def test_null_feature_extractor():
|
||||
|
||||
rules = capa.rules.RuleSet(
|
||||
[
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: xor loop
|
||||
@@ -102,9 +100,7 @@ def test_null_feature_extractor():
|
||||
- characteristic: tight loop
|
||||
- mnemonic: xor
|
||||
- characteristic: nzxor
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
]
|
||||
)
|
||||
capabilities = capa.main.find_capabilities(rules, EXTRACTOR)
|
||||
|
||||
@@ -38,8 +38,7 @@ def test_main(z9324d_extractor):
|
||||
|
||||
def test_main_single_rule(z9324d_extractor, tmpdir):
|
||||
# tests a single rule can be loaded successfully
|
||||
RULE_CONTENT = textwrap.dedent(
|
||||
"""
|
||||
RULE_CONTENT = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -50,8 +49,7 @@ def test_main_single_rule(z9324d_extractor, tmpdir):
|
||||
- test
|
||||
features:
|
||||
- string: test
|
||||
"""
|
||||
)
|
||||
""")
|
||||
path = z9324d_extractor.path
|
||||
rule_file = tmpdir.mkdir("capa").join("rule.yml")
|
||||
rule_file.write(RULE_CONTENT)
|
||||
@@ -100,9 +98,7 @@ def test_main_shellcode(z499c2_extractor):
|
||||
def test_ruleset():
|
||||
rules = capa.rules.RuleSet(
|
||||
[
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: file rule
|
||||
@@ -111,12 +107,8 @@ def test_ruleset():
|
||||
dynamic: process
|
||||
features:
|
||||
- characteristic: embedded pe
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: function rule
|
||||
@@ -125,12 +117,8 @@ def test_ruleset():
|
||||
dynamic: process
|
||||
features:
|
||||
- characteristic: tight loop
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: basic block rule
|
||||
@@ -139,12 +127,8 @@ def test_ruleset():
|
||||
dynamic: process
|
||||
features:
|
||||
- characteristic: nzxor
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: process rule
|
||||
@@ -153,12 +137,8 @@ def test_ruleset():
|
||||
dynamic: process
|
||||
features:
|
||||
- string: "explorer.exe"
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: thread rule
|
||||
@@ -167,12 +147,8 @@ def test_ruleset():
|
||||
dynamic: thread
|
||||
features:
|
||||
- api: RegDeleteKey
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test call subscope
|
||||
@@ -184,12 +160,8 @@ def test_ruleset():
|
||||
- string: "explorer.exe"
|
||||
- call:
|
||||
- api: HttpOpenRequestW
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -207,9 +179,7 @@ def test_ruleset():
|
||||
- number: 6 = IPPROTO_TCP
|
||||
- number: 1 = SOCK_STREAM
|
||||
- number: 2 = AF_INET
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
]
|
||||
)
|
||||
assert len(rules.file_rules) == 2
|
||||
@@ -322,9 +292,7 @@ def test_main_cape1(tmp_path):
|
||||
# https://github.com/mandiant/capa/pull/1696
|
||||
rules = tmp_path / "rules"
|
||||
rules.mkdir()
|
||||
(rules / "create-or-open-registry-key.yml").write_text(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
(rules / "create-or-open-registry-key.yml").write_text(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: create or open registry key
|
||||
@@ -354,9 +322,7 @@ def test_main_cape1(tmp_path):
|
||||
- api: SHRegOpenUSKey
|
||||
- api: SHRegCreateUSKey
|
||||
- api: RtlCreateRegistryKey
|
||||
"""
|
||||
)
|
||||
)
|
||||
"""))
|
||||
|
||||
assert capa.main.main([str(path), "-r", str(rules)]) == 0
|
||||
assert capa.main.main([str(path), "-q", "-r", str(rules)]) == 0
|
||||
|
||||
@@ -46,8 +46,7 @@ def match(rules, features, va, scope=Scope.FUNCTION):
|
||||
|
||||
|
||||
def test_match_simple():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -57,8 +56,7 @@ def test_match_simple():
|
||||
namespace: testns1/testns2
|
||||
features:
|
||||
- number: 100
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
|
||||
features, matches = match([r], {capa.features.insn.Number(100): {1, 2}}, 0x0)
|
||||
@@ -69,8 +67,7 @@ def test_match_simple():
|
||||
|
||||
|
||||
def test_match_range_exact():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -79,8 +76,7 @@ def test_match_range_exact():
|
||||
dynamic: process
|
||||
features:
|
||||
- count(number(100)): 2
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
|
||||
# just enough matches
|
||||
@@ -97,8 +93,7 @@ def test_match_range_exact():
|
||||
|
||||
|
||||
def test_match_range_range():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -107,8 +102,7 @@ def test_match_range_range():
|
||||
dynamic: process
|
||||
features:
|
||||
- count(number(100)): (2, 3)
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
|
||||
# just enough matches
|
||||
@@ -129,8 +123,7 @@ def test_match_range_range():
|
||||
|
||||
|
||||
def test_match_range_exact_zero():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -146,8 +139,7 @@ def test_match_range_exact_zero():
|
||||
# so we have this additional trivial feature.
|
||||
- mnemonic: mov
|
||||
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
|
||||
# feature isn't indexed - good.
|
||||
@@ -165,8 +157,7 @@ def test_match_range_exact_zero():
|
||||
|
||||
|
||||
def test_match_range_with_zero():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -181,8 +172,7 @@ def test_match_range_with_zero():
|
||||
# since we don't support top level NOT statements.
|
||||
# so we have this additional trivial feature.
|
||||
- mnemonic: mov
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
|
||||
# ok
|
||||
@@ -200,8 +190,7 @@ def test_match_range_with_zero():
|
||||
|
||||
def test_match_adds_matched_rule_feature():
|
||||
"""show that using `match` adds a feature for matched rules."""
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -210,8 +199,7 @@ def test_match_adds_matched_rule_feature():
|
||||
dynamic: process
|
||||
features:
|
||||
- number: 100
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
features, _ = match([r], {capa.features.insn.Number(100): {1}}, 0x0)
|
||||
assert capa.features.common.MatchedRule("test rule") in features
|
||||
@@ -220,9 +208,7 @@ def test_match_adds_matched_rule_feature():
|
||||
def test_match_matched_rules():
|
||||
"""show that using `match` adds a feature for matched rules."""
|
||||
rules = [
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule1
|
||||
@@ -231,12 +217,8 @@ def test_match_matched_rules():
|
||||
dynamic: process
|
||||
features:
|
||||
- number: 100
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule2
|
||||
@@ -245,9 +227,7 @@ def test_match_matched_rules():
|
||||
dynamic: process
|
||||
features:
|
||||
- match: test rule1
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
]
|
||||
|
||||
features, _ = match(
|
||||
@@ -271,9 +251,7 @@ def test_match_matched_rules():
|
||||
|
||||
def test_match_namespace():
|
||||
rules = [
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: CreateFile API
|
||||
@@ -283,12 +261,8 @@ def test_match_namespace():
|
||||
namespace: file/create/CreateFile
|
||||
features:
|
||||
- api: CreateFile
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: WriteFile API
|
||||
@@ -298,12 +272,8 @@ def test_match_namespace():
|
||||
namespace: file/write
|
||||
features:
|
||||
- api: WriteFile
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: file-create
|
||||
@@ -312,12 +282,8 @@ def test_match_namespace():
|
||||
dynamic: process
|
||||
features:
|
||||
- match: file/create
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: filesystem-any
|
||||
@@ -326,9 +292,7 @@ def test_match_namespace():
|
||||
dynamic: process
|
||||
features:
|
||||
- match: file
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
]
|
||||
|
||||
features, matches = match(
|
||||
@@ -355,9 +319,7 @@ def test_match_namespace():
|
||||
|
||||
def test_match_substring():
|
||||
rules = [
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -367,9 +329,7 @@ def test_match_substring():
|
||||
features:
|
||||
- and:
|
||||
- substring: abc
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
]
|
||||
features, _ = match(
|
||||
capa.rules.topologically_order_rules(rules),
|
||||
@@ -409,9 +369,7 @@ def test_match_substring():
|
||||
|
||||
def test_match_regex():
|
||||
rules = [
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -421,12 +379,8 @@ def test_match_regex():
|
||||
features:
|
||||
- and:
|
||||
- string: /.*bbbb.*/
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: rule with implied wildcards
|
||||
@@ -436,12 +390,8 @@ def test_match_regex():
|
||||
features:
|
||||
- and:
|
||||
- string: /bbbb/
|
||||
"""
|
||||
)
|
||||
),
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
""")),
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: rule with anchor
|
||||
@@ -451,9 +401,7 @@ def test_match_regex():
|
||||
features:
|
||||
- and:
|
||||
- string: /^bbbb/
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
]
|
||||
features, _ = match(
|
||||
capa.rules.topologically_order_rules(rules),
|
||||
@@ -488,9 +436,7 @@ def test_match_regex():
|
||||
|
||||
def test_match_regex_ignorecase():
|
||||
rules = [
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -500,9 +446,7 @@ def test_match_regex_ignorecase():
|
||||
features:
|
||||
- and:
|
||||
- string: /.*bbbb.*/i
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
]
|
||||
features, _ = match(
|
||||
capa.rules.topologically_order_rules(rules),
|
||||
@@ -514,9 +458,7 @@ def test_match_regex_ignorecase():
|
||||
|
||||
def test_match_regex_complex():
|
||||
rules = [
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
r"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent(r"""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -526,9 +468,7 @@ def test_match_regex_complex():
|
||||
features:
|
||||
- or:
|
||||
- string: /.*HARDWARE\\Key\\key with spaces\\.*/i
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
]
|
||||
features, _ = match(
|
||||
capa.rules.topologically_order_rules(rules),
|
||||
@@ -540,9 +480,7 @@ def test_match_regex_complex():
|
||||
|
||||
def test_match_regex_values_always_string():
|
||||
rules = [
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -553,9 +491,7 @@ def test_match_regex_values_always_string():
|
||||
- or:
|
||||
- string: /123/
|
||||
- string: /0x123/
|
||||
"""
|
||||
)
|
||||
),
|
||||
""")),
|
||||
]
|
||||
features, _ = match(
|
||||
capa.rules.topologically_order_rules(rules),
|
||||
@@ -587,8 +523,7 @@ def test_regex_get_value_str(pattern):
|
||||
|
||||
@pytest.mark.xfail(reason="can't have top level NOT")
|
||||
def test_match_only_not():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -599,8 +534,7 @@ def test_match_only_not():
|
||||
features:
|
||||
- not:
|
||||
- number: 99
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
|
||||
_, matches = match([r], {capa.features.insn.Number(100): {1, 2}}, 0x0)
|
||||
@@ -608,8 +542,7 @@ def test_match_only_not():
|
||||
|
||||
|
||||
def test_match_not():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -622,8 +555,7 @@ def test_match_not():
|
||||
- mnemonic: mov
|
||||
- not:
|
||||
- number: 99
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
|
||||
_, matches = match([r], {capa.features.insn.Number(100): {1, 2}, capa.features.insn.Mnemonic("mov"): {1, 2}}, 0x0)
|
||||
@@ -632,8 +564,7 @@ def test_match_not():
|
||||
|
||||
@pytest.mark.xfail(reason="can't have nested NOT")
|
||||
def test_match_not_not():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -645,8 +576,7 @@ def test_match_not_not():
|
||||
- not:
|
||||
- not:
|
||||
- number: 100
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
|
||||
_, matches = match([r], {capa.features.insn.Number(100): {1, 2}}, 0x0)
|
||||
@@ -654,8 +584,7 @@ def test_match_not_not():
|
||||
|
||||
|
||||
def test_match_operand_number():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -665,8 +594,7 @@ def test_match_operand_number():
|
||||
features:
|
||||
- and:
|
||||
- operand[0].number: 0x10
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
|
||||
assert capa.features.insn.OperandNumber(0, 0x10) in {capa.features.insn.OperandNumber(0, 0x10)}
|
||||
@@ -684,8 +612,7 @@ def test_match_operand_number():
|
||||
|
||||
|
||||
def test_match_operand_offset():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -695,8 +622,7 @@ def test_match_operand_offset():
|
||||
features:
|
||||
- and:
|
||||
- operand[0].offset: 0x10
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
|
||||
assert capa.features.insn.OperandOffset(0, 0x10) in {capa.features.insn.OperandOffset(0, 0x10)}
|
||||
@@ -714,8 +640,7 @@ def test_match_operand_offset():
|
||||
|
||||
|
||||
def test_match_property_access():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -725,8 +650,7 @@ def test_match_property_access():
|
||||
features:
|
||||
- and:
|
||||
- property/read: System.IO.FileInfo::Length
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
|
||||
assert capa.features.insn.Property("System.IO.FileInfo::Length", capa.features.common.FeatureAccess.READ) in {
|
||||
@@ -758,8 +682,7 @@ def test_match_property_access():
|
||||
|
||||
|
||||
def test_match_os_any():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -777,8 +700,7 @@ def test_match_os_any():
|
||||
- and:
|
||||
- os: any
|
||||
- string: "Goodbye world"
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
|
||||
_, matches = match(
|
||||
@@ -812,8 +734,7 @@ def test_match_os_any():
|
||||
|
||||
# this test demonstrates the behavior of unstable features that may change before the next major release.
|
||||
def test_index_features_and_unstable():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -824,8 +745,7 @@ def test_index_features_and_unstable():
|
||||
- and:
|
||||
- mnemonic: mov
|
||||
- api: CreateFileW
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
rr = capa.rules.RuleSet([r])
|
||||
index: capa.rules.RuleSet._RuleFeatureIndex = rr._feature_indexes_by_scopes[capa.rules.Scope.FUNCTION]
|
||||
@@ -841,8 +761,7 @@ def test_index_features_and_unstable():
|
||||
|
||||
# this test demonstrates the behavior of unstable features that may change before the next major release.
|
||||
def test_index_features_or_unstable():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -853,8 +772,7 @@ def test_index_features_or_unstable():
|
||||
- or:
|
||||
- mnemonic: mov
|
||||
- api: CreateFileW
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
rr = capa.rules.RuleSet([r])
|
||||
index: capa.rules.RuleSet._RuleFeatureIndex = rr._feature_indexes_by_scopes[capa.rules.Scope.FUNCTION]
|
||||
@@ -871,8 +789,7 @@ def test_index_features_or_unstable():
|
||||
|
||||
# this test demonstrates the behavior of unstable features that may change before the next major release.
|
||||
def test_index_features_nested_unstable():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -885,8 +802,7 @@ def test_index_features_nested_unstable():
|
||||
- or:
|
||||
- api: CreateFileW
|
||||
- string: foo
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
rr = capa.rules.RuleSet([r])
|
||||
index: capa.rules.RuleSet._RuleFeatureIndex = rr._feature_indexes_by_scopes[capa.rules.Scope.FUNCTION]
|
||||
|
||||
@@ -25,8 +25,7 @@ from capa.features.common import Arch, Substring
|
||||
|
||||
|
||||
def test_optimizer_order():
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -44,8 +43,7 @@ def test_optimizer_order():
|
||||
- or:
|
||||
- number: 1
|
||||
- offset: 4
|
||||
"""
|
||||
)
|
||||
""")
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
|
||||
# before optimization
|
||||
|
||||
@@ -56,8 +56,7 @@ def test_render_meta_attack():
|
||||
subtechnique = "Windows Service"
|
||||
canonical = "{:s}::{:s}::{:s} [{:s}]".format(tactic, technique, subtechnique, id)
|
||||
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -70,10 +69,7 @@ def test_render_meta_attack():
|
||||
- {:s}
|
||||
features:
|
||||
- number: 1
|
||||
""".format(
|
||||
canonical
|
||||
)
|
||||
)
|
||||
""".format(canonical))
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
rule_meta = capa.render.result_document.RuleMetadata.from_capa(r)
|
||||
attack = rule_meta.attack[0]
|
||||
@@ -94,8 +90,7 @@ def test_render_meta_mbc():
|
||||
method = "Heavens Gate"
|
||||
canonical = "{:s}::{:s}::{:s} [{:s}]".format(objective, behavior, method, id)
|
||||
|
||||
rule = textwrap.dedent(
|
||||
"""
|
||||
rule = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -108,10 +103,7 @@ def test_render_meta_mbc():
|
||||
- {:s}
|
||||
features:
|
||||
- number: 1
|
||||
""".format(
|
||||
canonical
|
||||
)
|
||||
)
|
||||
""".format(canonical))
|
||||
r = capa.rules.Rule.from_yaml(rule)
|
||||
rule_meta = capa.render.result_document.RuleMetadata.from_capa(r)
|
||||
mbc = rule_meta.mbc[0]
|
||||
@@ -129,8 +121,7 @@ def test_render_meta_maec():
|
||||
malware_category = "downloader"
|
||||
analysis_conclusion = "malicious"
|
||||
|
||||
rule_yaml = textwrap.dedent(
|
||||
"""
|
||||
rule_yaml = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -144,10 +135,7 @@ def test_render_meta_maec():
|
||||
maec/analysis-conclusion: {:s}
|
||||
features:
|
||||
- number: 1
|
||||
""".format(
|
||||
malware_family, malware_category, analysis_conclusion
|
||||
)
|
||||
)
|
||||
""".format(malware_family, malware_category, analysis_conclusion))
|
||||
rule = capa.rules.Rule.from_yaml(rule_yaml)
|
||||
rm = capa.render.result_document.RuleMatches(
|
||||
meta=capa.render.result_document.RuleMetadata.from_capa(rule),
|
||||
@@ -220,8 +208,7 @@ def test_render_vverbose_feature(feature, expected):
|
||||
|
||||
layout = capa.render.result_document.StaticLayout(functions=())
|
||||
|
||||
src = textwrap.dedent(
|
||||
"""
|
||||
src = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -237,8 +224,7 @@ def test_render_vverbose_feature(feature, expected):
|
||||
- and:
|
||||
- number: 1
|
||||
- number: 2
|
||||
"""
|
||||
)
|
||||
""")
|
||||
rule = capa.rules.Rule.from_yaml(src)
|
||||
|
||||
rm = capa.render.result_document.RuleMatches(
|
||||
|
||||
@@ -22,9 +22,7 @@ import capa.rules
|
||||
import capa.helpers
|
||||
import capa.rules.cache
|
||||
|
||||
R1 = capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
R1 = capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -40,13 +38,9 @@ R1 = capa.rules.Rule.from_yaml(
|
||||
- and:
|
||||
- number: 1
|
||||
- number: 2
|
||||
"""
|
||||
)
|
||||
)
|
||||
"""))
|
||||
|
||||
R2 = capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
R2 = capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule 2
|
||||
@@ -62,9 +56,7 @@ R2 = capa.rules.Rule.from_yaml(
|
||||
- and:
|
||||
- number: 3
|
||||
- number: 4
|
||||
"""
|
||||
)
|
||||
)
|
||||
"""))
|
||||
|
||||
|
||||
def test_ruleset_cache_ids():
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,9 +21,7 @@ import capa.rules
|
||||
|
||||
|
||||
def test_rule_scope_instruction():
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -35,14 +33,10 @@ def test_rule_scope_instruction():
|
||||
- mnemonic: mov
|
||||
- arch: i386
|
||||
- os: windows
|
||||
"""
|
||||
)
|
||||
)
|
||||
"""))
|
||||
|
||||
with pytest.raises(capa.rules.InvalidRule):
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -51,17 +45,11 @@ def test_rule_scope_instruction():
|
||||
dynamic: unsupported
|
||||
features:
|
||||
- characteristic: embedded pe
|
||||
"""
|
||||
)
|
||||
)
|
||||
"""))
|
||||
|
||||
|
||||
def test_rule_subscope_instruction():
|
||||
rules = capa.rules.RuleSet(
|
||||
[
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
rules = capa.rules.RuleSet([capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -75,11 +63,7 @@ def test_rule_subscope_instruction():
|
||||
- mnemonic: mov
|
||||
- arch: i386
|
||||
- os: windows
|
||||
"""
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
"""))])
|
||||
# the function rule scope will have one rules:
|
||||
# - `test rule`
|
||||
assert len(rules.function_rules) == 1
|
||||
@@ -90,9 +74,7 @@ def test_rule_subscope_instruction():
|
||||
|
||||
|
||||
def test_scope_instruction_implied_and():
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -105,15 +87,11 @@ def test_scope_instruction_implied_and():
|
||||
- mnemonic: mov
|
||||
- arch: i386
|
||||
- os: windows
|
||||
"""
|
||||
)
|
||||
)
|
||||
"""))
|
||||
|
||||
|
||||
def test_scope_instruction_description():
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -127,13 +105,9 @@ def test_scope_instruction_description():
|
||||
- mnemonic: mov
|
||||
- arch: i386
|
||||
- os: windows
|
||||
"""
|
||||
)
|
||||
)
|
||||
"""))
|
||||
|
||||
capa.rules.Rule.from_yaml(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
capa.rules.Rule.from_yaml(textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: test rule
|
||||
@@ -147,6 +121,4 @@ def test_scope_instruction_description():
|
||||
- mnemonic: mov
|
||||
- arch: i386
|
||||
- os: windows
|
||||
"""
|
||||
)
|
||||
)
|
||||
"""))
|
||||
|
||||
@@ -142,8 +142,7 @@ def test_proto_conversion(tmp_path):
|
||||
|
||||
|
||||
def test_detect_duplicate_features(tmpdir):
|
||||
TEST_RULE_0 = textwrap.dedent(
|
||||
"""
|
||||
TEST_RULE_0 = textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: Test Rule 0
|
||||
@@ -155,12 +154,10 @@ def test_detect_duplicate_features(tmpdir):
|
||||
- number: 1
|
||||
- not:
|
||||
- string: process
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
TEST_RULESET = {
|
||||
"rule_1": textwrap.dedent(
|
||||
"""
|
||||
"rule_1": textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: Test Rule 1
|
||||
@@ -179,10 +176,8 @@ def test_detect_duplicate_features(tmpdir):
|
||||
- count(mnemonic(xor)): 5
|
||||
- not:
|
||||
- os: linux
|
||||
"""
|
||||
),
|
||||
"rule_2": textwrap.dedent(
|
||||
"""
|
||||
"""),
|
||||
"rule_2": textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: Test Rule 2
|
||||
@@ -196,10 +191,8 @@ def test_detect_duplicate_features(tmpdir):
|
||||
- and:
|
||||
- api: CreateFile
|
||||
- mnemonic: xor
|
||||
"""
|
||||
),
|
||||
"rule_3": textwrap.dedent(
|
||||
"""
|
||||
"""),
|
||||
"rule_3": textwrap.dedent("""
|
||||
rule:
|
||||
meta:
|
||||
name: Test Rule 3
|
||||
@@ -214,8 +207,7 @@ def test_detect_duplicate_features(tmpdir):
|
||||
- and:
|
||||
- api: bind
|
||||
- number: 2
|
||||
"""
|
||||
),
|
||||
"""),
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
@@ -26,11 +26,9 @@ from capa.features.extractors.vmray.models import (
|
||||
|
||||
|
||||
def test_vmray_model_param():
|
||||
param_str = textwrap.dedent(
|
||||
"""
|
||||
param_str = textwrap.dedent("""
|
||||
<param name="addrlen" type="signed_32bit" value="16"/>
|
||||
"""
|
||||
)
|
||||
""")
|
||||
param: Param = Param.model_validate(xml_to_dict(param_str)["param"])
|
||||
|
||||
assert param.value is not None
|
||||
@@ -38,13 +36,11 @@ def test_vmray_model_param():
|
||||
|
||||
|
||||
def test_vmray_model_param_deref():
|
||||
param_str = textwrap.dedent(
|
||||
"""
|
||||
param_str = textwrap.dedent("""
|
||||
<param name="buf" type="ptr" value="0xaaaaaaaa">
|
||||
<deref type="str" value="Hello world"/>
|
||||
</param>
|
||||
"""
|
||||
)
|
||||
""")
|
||||
param: Param = Param.model_validate(xml_to_dict(param_str)["param"])
|
||||
|
||||
assert param.deref is not None
|
||||
@@ -52,8 +48,7 @@ def test_vmray_model_param_deref():
|
||||
|
||||
|
||||
def test_vmray_model_function_call():
|
||||
function_call_str = textwrap.dedent(
|
||||
"""
|
||||
function_call_str = textwrap.dedent("""
|
||||
<fncall fncall_id="18" process_id="1" thread_id="1" name="sys_time">
|
||||
<in>
|
||||
<param name="tloc" type="unknown" value="0x0"/>
|
||||
@@ -62,8 +57,7 @@ def test_vmray_model_function_call():
|
||||
<param name="ret_val" type="unknown" value="0xaaaaaaaa"/>
|
||||
</out>
|
||||
</fncall>
|
||||
"""
|
||||
)
|
||||
""")
|
||||
function_call: FunctionCall = FunctionCall.model_validate(xml_to_dict(function_call_str)["fncall"])
|
||||
|
||||
assert function_call.fncall_id == 18
|
||||
@@ -81,22 +75,19 @@ def test_vmray_model_function_call():
|
||||
|
||||
|
||||
def test_vmray_model_analysis_metadata():
|
||||
analysis_metadata: AnalysisMetadata = AnalysisMetadata.model_validate_json(
|
||||
"""
|
||||
analysis_metadata: AnalysisMetadata = AnalysisMetadata.model_validate_json("""
|
||||
{
|
||||
"sample_type": "Linux ELF Executable (x86-64)",
|
||||
"submission_filename": "abcd1234"
|
||||
}
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
assert analysis_metadata.sample_type == "Linux ELF Executable (x86-64)"
|
||||
assert analysis_metadata.submission_filename == "abcd1234"
|
||||
|
||||
|
||||
def test_vmray_model_elffile():
|
||||
elffile: ElfFile = ElfFile.model_validate_json(
|
||||
"""
|
||||
elffile: ElfFile = ElfFile.model_validate_json("""
|
||||
{
|
||||
"sections": [
|
||||
{
|
||||
@@ -107,16 +98,14 @@ def test_vmray_model_elffile():
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
assert elffile.sections[0].header.sh_name == "abcd1234"
|
||||
assert elffile.sections[0].header.sh_addr == 2863311530
|
||||
|
||||
|
||||
def test_vmray_model_pefile():
|
||||
pefile: PEFile = PEFile.model_validate_json(
|
||||
"""
|
||||
pefile: PEFile = PEFile.model_validate_json("""
|
||||
{
|
||||
"basic_info": {
|
||||
"image_base": 2863311530
|
||||
@@ -150,8 +139,7 @@ def test_vmray_model_pefile():
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
assert pefile.basic_info.image_base == 2863311530
|
||||
|
||||
|
||||
Reference in New Issue
Block a user