This commit is contained in:
William Ballenthin
2020-07-01 12:33:13 -06:00
parent f55c1ac604
commit d23ef48bb6
8 changed files with 1234 additions and 783 deletions

View File

@@ -9,70 +9,74 @@ import viv_utils
CD = os.path.dirname(__file__)
Sample = collections.namedtuple('Sample', ['vw', 'path'])
Sample = collections.namedtuple("Sample", ["vw", "path"])
@pytest.fixture
def mimikatz():
path = os.path.join(CD, 'data', 'mimikatz.exe_')
path = os.path.join(CD, "data", "mimikatz.exe_")
return Sample(viv_utils.getWorkspace(path), path)
@pytest.fixture
def sample_a933a1a402775cfa94b6bee0963f4b46():
path = os.path.join(CD, 'data', 'a933a1a402775cfa94b6bee0963f4b46.dll_')
path = os.path.join(CD, "data", "a933a1a402775cfa94b6bee0963f4b46.dll_")
return Sample(viv_utils.getWorkspace(path), path)
@pytest.fixture
def kernel32():
path = os.path.join(CD, 'data', 'kernel32.dll_')
path = os.path.join(CD, "data", "kernel32.dll_")
return Sample(viv_utils.getWorkspace(path), path)
@pytest.fixture
def sample_a198216798ca38f280dc413f8c57f2c2():
path = os.path.join(CD, 'data', 'a198216798ca38f280dc413f8c57f2c2.exe_')
path = os.path.join(CD, "data", "a198216798ca38f280dc413f8c57f2c2.exe_")
return Sample(viv_utils.getWorkspace(path), path)
@pytest.fixture
def sample_9324d1a8ae37a36ae560c37448c9705a():
path = os.path.join(CD, 'data', '9324d1a8ae37a36ae560c37448c9705a.exe_')
path = os.path.join(CD, "data", "9324d1a8ae37a36ae560c37448c9705a.exe_")
return Sample(viv_utils.getWorkspace(path), path)
@pytest.fixture
def pma_lab_12_04():
path = os.path.join(CD, 'data', 'Practical Malware Analysis Lab 12-04.exe_')
path = os.path.join(CD, "data", "Practical Malware Analysis Lab 12-04.exe_")
return Sample(viv_utils.getWorkspace(path), path)
@pytest.fixture
def sample_bfb9b5391a13d0afd787e87ab90f14f5():
path = os.path.join(CD, 'data', 'bfb9b5391a13d0afd787e87ab90f14f5.dll_')
path = os.path.join(CD, "data", "bfb9b5391a13d0afd787e87ab90f14f5.dll_")
return Sample(viv_utils.getWorkspace(path), path)
@pytest.fixture
def sample_lab21_01():
path = os.path.join(CD, 'data', 'Practical Malware Analysis Lab 21-01.exe_')
path = os.path.join(CD, "data", "Practical Malware Analysis Lab 21-01.exe_")
return Sample(viv_utils.getWorkspace(path), path)
@pytest.fixture
def sample_c91887d861d9bd4a5872249b641bc9f9():
path = os.path.join(CD, 'data', 'c91887d861d9bd4a5872249b641bc9f9.exe_')
path = os.path.join(CD, "data", "c91887d861d9bd4a5872249b641bc9f9.exe_")
return Sample(viv_utils.getWorkspace(path), path)
@pytest.fixture
def sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41():
path = os.path.join(CD, 'data', '39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.dll_')
path = os.path.join(
CD,
"data",
"39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.dll_",
)
return Sample(viv_utils.getWorkspace(path), path)
@pytest.fixture
def sample_499c2a85f6e8142c3f48d4251c9c7cd6_raw32():
path = os.path.join(CD, 'data', '499c2a85f6e8142c3f48d4251c9c7cd6.raw32')
path = os.path.join(CD, "data", "499c2a85f6e8142c3f48d4251c9c7cd6.raw32")
return Sample(viv_utils.getShellcodeWorkspace(path), path)

View File

@@ -41,25 +41,46 @@ def test_some():
assert Some(1, Number(1)).evaluate({Number(0): {1}}) == False
assert Some(2, Number(1), Number(2), Number(3)).evaluate({Number(0): {1}}) == False
assert Some(2, Number(1), Number(2), Number(3)).evaluate({Number(0): {1}, Number(1): {1}}) == False
assert Some(2, Number(1), Number(2), Number(3)).evaluate({Number(0): {1}, Number(1): {1}, Number(2): {1}}) == True
assert Some(2, Number(1), Number(2), Number(3)).evaluate(
{Number(0): {1}, Number(1): {1}, Number(2): {1}, Number(3): {1}}) == True
assert Some(2, Number(1), Number(2), Number(3)).evaluate(
{Number(0): {1}, Number(1): {1}, Number(2): {1}, Number(3): {1}, Number(4): {1}}) == True
assert (
Some(2, Number(1), Number(2), Number(3)).evaluate(
{Number(0): {1}, Number(1): {1}}
)
== False
)
assert (
Some(2, Number(1), Number(2), Number(3)).evaluate(
{Number(0): {1}, Number(1): {1}, Number(2): {1}}
)
== True
)
assert (
Some(2, Number(1), Number(2), Number(3)).evaluate(
{Number(0): {1}, Number(1): {1}, Number(2): {1}, Number(3): {1}}
)
== True
)
assert (
Some(2, Number(1), Number(2), Number(3)).evaluate(
{
Number(0): {1},
Number(1): {1},
Number(2): {1},
Number(3): {1},
Number(4): {1},
}
)
== True
)
def test_complex():
assert True == Or(
And(Number(1), Number(2)),
Or(Number(3),
Some(2, Number(4), Number(5), Number(6)))
Or(Number(3), Some(2, Number(4), Number(5), Number(6))),
).evaluate({Number(5): {1}, Number(6): {1}, Number(7): {1}, Number(8): {1}})
assert False == Or(
And(Number(1), Number(2)),
Or(Number(3),
Some(2, Number(4), Number(5)))
And(Number(1), Number(2)), Or(Number(3), Some(2, Number(4), Number(5)))
).evaluate({Number(5): {1}, Number(6): {1}, Number(7): {1}, Number(8): {1}})
@@ -97,176 +118,252 @@ def test_range():
def test_match_adds_matched_rule_feature():
'''show that using `match` adds a feature for matched rules.'''
rule = textwrap.dedent('''
"""show that using `match` adds a feature for matched rules."""
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- number: 100
''')
"""
)
r = capa.rules.Rule.from_yaml(rule)
features, matches = capa.engine.match([r], {capa.features.insn.Number(100): {1}}, 0x0)
assert capa.features.MatchedRule('test rule') in features
features, matches = capa.engine.match(
[r], {capa.features.insn.Number(100): {1}}, 0x0
)
assert capa.features.MatchedRule("test rule") in features
def test_match_matched_rules():
'''show that using `match` adds a feature for matched rules.'''
"""show that using `match` adds a feature for matched rules."""
rules = [
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule1
features:
- number: 100
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule2
features:
- match: test rule1
''')),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule1
features:
- number: 100
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule2
features:
- match: test rule1
"""
)
),
]
features, matches = capa.engine.match(capa.engine.topologically_order_rules(rules),
{capa.features.insn.Number(100): {1}}, 0x0)
assert capa.features.MatchedRule('test rule1') in features
assert capa.features.MatchedRule('test rule2') in features
features, matches = capa.engine.match(
capa.engine.topologically_order_rules(rules),
{capa.features.insn.Number(100): {1}},
0x0,
)
assert capa.features.MatchedRule("test rule1") in features
assert capa.features.MatchedRule("test rule2") in features
# the ordering of the rules must not matter,
# the engine should match rules in an appropriate order.
features, matches = capa.engine.match(capa.engine.topologically_order_rules(reversed(rules)),
{capa.features.insn.Number(100): {1}}, 0x0)
assert capa.features.MatchedRule('test rule1') in features
assert capa.features.MatchedRule('test rule2') in features
features, matches = capa.engine.match(
capa.engine.topologically_order_rules(reversed(rules)),
{capa.features.insn.Number(100): {1}},
0x0,
)
assert capa.features.MatchedRule("test rule1") in features
assert capa.features.MatchedRule("test rule2") in features
def test_regex():
rules = [
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
features:
- and:
- string: /.*bbbb.*/
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: rule with implied wildcards
features:
- and:
- string: /bbbb/
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: rule with anchor
features:
- and:
- string: /^bbbb/
''')),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- and:
- string: /.*bbbb.*/
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule with implied wildcards
features:
- and:
- string: /bbbb/
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule with anchor
features:
- and:
- string: /^bbbb/
"""
)
),
]
features, matches = capa.engine.match(capa.engine.topologically_order_rules(rules),
{capa.features.insn.Number(100): {1}}, 0x0)
assert capa.features.MatchedRule('test rule') not in features
features, matches = capa.engine.match(
capa.engine.topologically_order_rules(rules),
{capa.features.insn.Number(100): {1}},
0x0,
)
assert capa.features.MatchedRule("test rule") not in features
features, matches = capa.engine.match(capa.engine.topologically_order_rules(rules),
{capa.features.String('aaaa'): {1}}, 0x0)
assert capa.features.MatchedRule('test rule') not in features
features, matches = capa.engine.match(
capa.engine.topologically_order_rules(rules),
{capa.features.String("aaaa"): {1}},
0x0,
)
assert capa.features.MatchedRule("test rule") not in features
features, matches = capa.engine.match(capa.engine.topologically_order_rules(rules),
{capa.features.String('aBBBBa'): {1}}, 0x0)
assert capa.features.MatchedRule('test rule') not in features
features, matches = capa.engine.match(
capa.engine.topologically_order_rules(rules),
{capa.features.String("aBBBBa"): {1}},
0x0,
)
assert capa.features.MatchedRule("test rule") not in features
features, matches = capa.engine.match(capa.engine.topologically_order_rules(rules),
{capa.features.String('abbbba'): {1}}, 0x0)
assert capa.features.MatchedRule('test rule') in features
assert capa.features.MatchedRule('rule with implied wildcards') in features
assert capa.features.MatchedRule('rule with anchor') not in features
features, matches = capa.engine.match(
capa.engine.topologically_order_rules(rules),
{capa.features.String("abbbba"): {1}},
0x0,
)
assert capa.features.MatchedRule("test rule") in features
assert capa.features.MatchedRule("rule with implied wildcards") in features
assert capa.features.MatchedRule("rule with anchor") not in features
def test_regex_ignorecase():
rules = [
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
features:
- and:
- string: /.*bbbb.*/i
''')),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- and:
- string: /.*bbbb.*/i
"""
)
),
]
features, matches = capa.engine.match(capa.engine.topologically_order_rules(rules),
{capa.features.String('aBBBBa'): {1}}, 0x0)
assert capa.features.MatchedRule('test rule') in features
features, matches = capa.engine.match(
capa.engine.topologically_order_rules(rules),
{capa.features.String("aBBBBa"): {1}},
0x0,
)
assert capa.features.MatchedRule("test rule") in features
def test_regex_complex():
rules = [
capa.rules.Rule.from_yaml(textwrap.dedent(r'''
rule:
meta:
name: test rule
features:
- or:
- string: /.*HARDWARE\\Key\\key with spaces\\.*/i
''')),
capa.rules.Rule.from_yaml(
textwrap.dedent(
r"""
rule:
meta:
name: test rule
features:
- or:
- string: /.*HARDWARE\\Key\\key with spaces\\.*/i
"""
)
),
]
features, matches = capa.engine.match(capa.engine.topologically_order_rules(rules),
{capa.features.String(r'Hardware\Key\key with spaces\some value'): {1}}, 0x0)
assert capa.features.MatchedRule('test rule') in features
features, matches = capa.engine.match(
capa.engine.topologically_order_rules(rules),
{capa.features.String(r"Hardware\Key\key with spaces\some value"): {1}},
0x0,
)
assert capa.features.MatchedRule("test rule") in features
def test_match_namespace():
rules = [
capa.rules.Rule.from_yaml(textwrap.dedent('''
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: CreateFile API
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
namespace: file/write
features:
- api: WriteFile
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: file-create
features:
- match: file/create
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: filesystem-any
features:
- match: file
''')),
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: file-create
features:
- match: file/create
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: filesystem-any
features:
- match: file
"""
)
),
]
features, matches = capa.engine.match(capa.engine.topologically_order_rules(rules),
{capa.features.insn.API('CreateFile'): {1}},
0x0)
assert 'CreateFile API' in matches
assert 'file-create' in matches
assert 'filesystem-any' in matches
assert capa.features.MatchedRule('file') in features
assert capa.features.MatchedRule('file/create') in features
assert capa.features.MatchedRule('file/create/CreateFile') in features
features, matches = capa.engine.match(
capa.engine.topologically_order_rules(rules),
{capa.features.insn.API("CreateFile"): {1}},
0x0,
)
assert "CreateFile API" in matches
assert "file-create" in matches
assert "filesystem-any" in matches
assert capa.features.MatchedRule("file") in features
assert capa.features.MatchedRule("file/create") in features
assert capa.features.MatchedRule("file/create/CreateFile") in features
features, matches = capa.engine.match(capa.engine.topologically_order_rules(rules),
{capa.features.insn.API('WriteFile'): {1}},
0x0)
assert 'WriteFile API' in matches
assert 'file-create' not in matches
assert 'filesystem-any' in matches
features, matches = capa.engine.match(
capa.engine.topologically_order_rules(rules),
{capa.features.insn.API("WriteFile"): {1}},
0x0,
)
assert "WriteFile API" in matches
assert "file-create" not in matches
assert "filesystem-any" in matches

View File

@@ -2,24 +2,27 @@ import textwrap
import capa.rules
EXPECTED = textwrap.dedent('''\
rule:
meta:
name: test rule
author: user@domain.com
scope: function
examples:
- foo1234
- bar5678
features:
- and:
- number: 1
- number: 2
''')
EXPECTED = textwrap.dedent(
"""\
rule:
meta:
name: test rule
author: user@domain.com
scope: function
examples:
- foo1234
- bar5678
features:
- and:
- number: 1
- number: 2
"""
)
def test_rule_reformat_top_level_elements():
rule = textwrap.dedent('''\
rule = textwrap.dedent(
"""
rule:
features:
- and:
@@ -31,13 +34,16 @@ def test_rule_reformat_top_level_elements():
scope: function
examples:
- foo1234
- bar5678''')
- 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
@@ -49,45 +55,52 @@ def test_rule_reformat_indentation():
features:
- and:
- number: 1
- number: 2''')
- number: 2
"""
)
assert capa.rules.Rule.from_yaml(rule).to_yaml() == EXPECTED
def test_rule_reformat_order():
rule = textwrap.dedent('''\
rule:
meta:
author: user@domain.com
examples:
- foo1234
- bar5678
scope: function
name: test rule
features:
- and:
- number: 1
- number: 2''')
rule = textwrap.dedent(
"""
rule:
meta:
author: user@domain.com
examples:
- foo1234
- bar5678
scope: function
name: test rule
features:
- and:
- number: 1
- number: 2
"""
)
assert capa.rules.Rule.from_yaml(rule).to_yaml() == EXPECTED
def test_rule_reformat_meta_update():
rule = textwrap.dedent('''\
rule:
meta:
author: user@domain.com
examples:
- foo1234
- bar5678
scope: function
name: AAAA
features:
- and:
- number: 1
- number: 2''')
rule = textwrap.dedent(
"""
rule:
meta:
author: user@domain.com
examples:
- foo1234
- bar5678
scope: function
name: AAAA
features:
- and:
- number: 1
- number: 2
"""
)
rule = capa.rules.Rule.from_yaml(rule)
rule.name = "test rule"
assert rule.to_yaml() == EXPECTED

View File

@@ -10,38 +10,44 @@ import capa.features.freeze
from fixtures import *
EXTRACTOR = capa.features.extractors.NullFeatureExtractor({
'file features': [
(0x402345, capa.features.Characteristic('embedded pe', True)),
],
'functions': {
0x401000: {
'features': [
(0x401000, capa.features.Characteristic('switch', True)),
],
'basic blocks': {
0x401000: {
'features': [
(0x401000, capa.features.Characteristic('tight loop', True)),
],
'instructions': {
0x401000: {
'features': [
(0x401000, capa.features.insn.Mnemonic('xor')),
(0x401000, capa.features.Characteristic('nzxor', True)),
],
EXTRACTOR = capa.features.extractors.NullFeatureExtractor(
{
"file features": [
(0x402345, capa.features.Characteristic("embedded pe", True)),
],
"functions": {
0x401000: {
"features": [(0x401000, capa.features.Characteristic("switch", True)),],
"basic blocks": {
0x401000: {
"features": [
(
0x401000,
capa.features.Characteristic("tight loop", True),
),
],
"instructions": {
0x401000: {
"features": [
(0x401000, capa.features.insn.Mnemonic("xor")),
(
0x401000,
capa.features.Characteristic("nzxor", True),
),
],
},
0x401002: {
"features": [
(0x401002, capa.features.insn.Mnemonic("mov")),
]
},
},
0x401002: {
'features': [
(0x401002, capa.features.insn.Mnemonic('mov')),
]
}
}
},
},
}
},
},
}
})
)
def test_null_feature_extractor():
@@ -49,29 +55,35 @@ def test_null_feature_extractor():
assert list(EXTRACTOR.get_basic_blocks(0x401000)) == [0x401000]
assert list(EXTRACTOR.get_instructions(0x401000, 0x0401000)) == [0x401000, 0x401002]
rules = capa.rules.RuleSet([
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: xor loop
scope: basic block
features:
- and:
- characteristic(tight loop): true
- mnemonic: xor
- characteristic(nzxor): true
''')),
])
rules = capa.rules.RuleSet(
[
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: xor loop
scope: basic block
features:
- and:
- characteristic(tight loop): true
- mnemonic: xor
- characteristic(nzxor): true
"""
)
),
]
)
capabilities = capa.main.find_capabilities(rules, EXTRACTOR)
assert 'xor loop' in capabilities
assert "xor loop" in capabilities
def compare_extractors(a, b):
'''
"""
args:
a (capa.features.extractors.NullFeatureExtractor)
b (capa.features.extractors.NullFeatureExtractor)
'''
"""
# TODO: ordering of these things probably doesn't work yet
@@ -79,44 +91,62 @@ def compare_extractors(a, b):
assert list(a.get_functions()) == list(b.get_functions())
for f in a.get_functions():
assert list(a.get_basic_blocks(f)) == list(b.get_basic_blocks(f))
assert list(a.extract_function_features(f)) == list(b.extract_function_features(f))
assert list(a.extract_function_features(f)) == list(
b.extract_function_features(f)
)
for bb in a.get_basic_blocks(f):
assert list(a.get_instructions(f, bb)) == list(b.get_instructions(f, bb))
assert list(a.extract_basic_block_features(f, bb)) == list(b.extract_basic_block_features(f, bb))
assert list(a.extract_basic_block_features(f, bb)) == list(
b.extract_basic_block_features(f, bb)
)
for insn in a.get_instructions(f, bb):
assert list(a.extract_insn_features(f, bb, insn)) == list(b.extract_insn_features(f, bb, insn))
assert list(a.extract_insn_features(f, bb, insn)) == list(
b.extract_insn_features(f, bb, insn)
)
def compare_extractors_viv_null(viv_ext, null_ext):
'''
"""
almost identical to compare_extractors but adds casts to ints since the VivisectFeatureExtractor returns objects
and NullFeatureExtractor returns ints
args:
viv_ext (capa.features.extractors.viv.VivisectFeatureExtractor)
null_ext (capa.features.extractors.NullFeatureExtractor)
'''
"""
# TODO: ordering of these things probably doesn't work yet
assert list(viv_ext.extract_file_features()) == list(null_ext.extract_file_features())
assert list(viv_ext.extract_file_features()) == list(
null_ext.extract_file_features()
)
assert to_int(list(viv_ext.get_functions())) == list(null_ext.get_functions())
for f in viv_ext.get_functions():
assert to_int(list(viv_ext.get_basic_blocks(f))) == list(null_ext.get_basic_blocks(to_int(f)))
assert list(viv_ext.extract_function_features(f)) == list(null_ext.extract_function_features(to_int(f)))
assert to_int(list(viv_ext.get_basic_blocks(f))) == list(
null_ext.get_basic_blocks(to_int(f))
)
assert list(viv_ext.extract_function_features(f)) == list(
null_ext.extract_function_features(to_int(f))
)
for bb in viv_ext.get_basic_blocks(f):
assert to_int(list(viv_ext.get_instructions(f, bb))) == list(null_ext.get_instructions(to_int(f), to_int(bb)))
assert list(viv_ext.extract_basic_block_features(f, bb)) == list(null_ext.extract_basic_block_features(to_int(f), to_int(bb)))
assert to_int(list(viv_ext.get_instructions(f, bb))) == list(
null_ext.get_instructions(to_int(f), to_int(bb))
)
assert list(viv_ext.extract_basic_block_features(f, bb)) == list(
null_ext.extract_basic_block_features(to_int(f), to_int(bb))
)
for insn in viv_ext.get_instructions(f, bb):
assert list(viv_ext.extract_insn_features(f, bb, insn)) == list(null_ext.extract_insn_features(to_int(f), to_int(bb), to_int(insn)))
assert list(viv_ext.extract_insn_features(f, bb, insn)) == list(
null_ext.extract_insn_features(to_int(f), to_int(bb), to_int(insn))
)
def to_int(o):
'''helper to get int value of extractor items'''
"""helper to get int value of extractor items"""
if isinstance(o, list):
return map(lambda x: capa.helpers.oint(x), o)
else:
@@ -144,30 +174,37 @@ def roundtrip_feature(feature):
def test_serialize_features():
roundtrip_feature(capa.features.insn.API('advapi32.CryptAcquireContextW'))
roundtrip_feature(capa.features.String('SCardControl'))
roundtrip_feature(capa.features.insn.API("advapi32.CryptAcquireContextW"))
roundtrip_feature(capa.features.String("SCardControl"))
roundtrip_feature(capa.features.insn.Number(0xFF))
roundtrip_feature(capa.features.insn.Offset(0x0))
roundtrip_feature(capa.features.insn.Mnemonic('push'))
roundtrip_feature(capa.features.file.Section('.rsrc'))
roundtrip_feature(capa.features.Characteristic('tight loop', True))
roundtrip_feature(capa.features.insn.Mnemonic("push"))
roundtrip_feature(capa.features.file.Section(".rsrc"))
roundtrip_feature(capa.features.Characteristic("tight loop", True))
roundtrip_feature(capa.features.basicblock.BasicBlock())
roundtrip_feature(capa.features.file.Export('BaseThreadInitThunk'))
roundtrip_feature(capa.features.file.Import('kernel32.IsWow64Process'))
roundtrip_feature(capa.features.file.Import('#11'))
roundtrip_feature(capa.features.file.Export("BaseThreadInitThunk"))
roundtrip_feature(capa.features.file.Import("kernel32.IsWow64Process"))
roundtrip_feature(capa.features.file.Import("#11"))
def test_freeze_sample(tmpdir, sample_9324d1a8ae37a36ae560c37448c9705a):
# tmpdir fixture handles cleanup
o = tmpdir.mkdir('capa').join('test.frz').strpath
assert capa.features.freeze.main([sample_9324d1a8ae37a36ae560c37448c9705a.path, o, '-v']) == 0
o = tmpdir.mkdir("capa").join("test.frz").strpath
assert (
capa.features.freeze.main(
[sample_9324d1a8ae37a36ae560c37448c9705a.path, o, "-v"]
)
== 0
)
def test_freeze_load_sample(tmpdir, sample_9324d1a8ae37a36ae560c37448c9705a):
o = tmpdir.mkdir('capa').join('test.frz')
viv_extractor = capa.features.extractors.viv.VivisectFeatureExtractor(sample_9324d1a8ae37a36ae560c37448c9705a.vw,
sample_9324d1a8ae37a36ae560c37448c9705a.path)
with open(o.strpath, 'wb') as f:
o = tmpdir.mkdir("capa").join("test.frz")
viv_extractor = capa.features.extractors.viv.VivisectFeatureExtractor(
sample_9324d1a8ae37a36ae560c37448c9705a.vw,
sample_9324d1a8ae37a36ae560c37448c9705a.path,
)
with open(o.strpath, "wb") as f:
f.write(capa.features.freeze.dump(viv_extractor))
null_extractor = capa.features.freeze.load(o.open('rb').read())
null_extractor = capa.features.freeze.load(o.open("rb").read())
compare_extractors_viv_null(viv_extractor, null_extractor)

View File

@@ -6,10 +6,10 @@ from capa.features.extractors import helpers
def test_all_zeros():
# Python 2: <str>
# Python 3: <bytes>
a = b'\x00\x00\x00\x00'
b = codecs.decode('00000000', 'hex')
c = b'\x01\x00\x00\x00'
d = codecs.decode('01000000', 'hex')
a = b"\x00\x00\x00\x00"
b = codecs.decode("00000000", "hex")
c = b"\x01\x00\x00\x00"
d = codecs.decode("01000000", "hex")
assert helpers.all_zeros(a) is True
assert helpers.all_zeros(b) is True
assert helpers.all_zeros(c) is False

View File

@@ -12,192 +12,274 @@ from fixtures import *
def test_main(sample_9324d1a8ae37a36ae560c37448c9705a):
# tests rules can be loaded successfully
assert capa.main.main([sample_9324d1a8ae37a36ae560c37448c9705a.path, '-v']) == 0
assert capa.main.main([sample_9324d1a8ae37a36ae560c37448c9705a.path, "-v"]) == 0
def test_main_single_rule(sample_9324d1a8ae37a36ae560c37448c9705a, tmpdir):
# tests a single rule can be loaded successfully
RULE_CONTENT = textwrap.dedent('''
rule:
meta:
name: test rule
scope: file
features:
- string: test
''')
rule_file = tmpdir.mkdir('capa').join('rule.yml')
RULE_CONTENT = textwrap.dedent(
"""
rule:
meta:
name: test rule
scope: file
features:
- string: test
"""
)
rule_file = tmpdir.mkdir("capa").join("rule.yml")
rule_file.write(RULE_CONTENT)
assert capa.main.main([sample_9324d1a8ae37a36ae560c37448c9705a.path, '-v', '-r', rule_file.strpath]) == 0
assert (
capa.main.main(
[
sample_9324d1a8ae37a36ae560c37448c9705a.path,
"-v",
"-r",
rule_file.strpath,
]
)
== 0
)
def test_main_shellcode(sample_499c2a85f6e8142c3f48d4251c9c7cd6_raw32):
assert capa.main.main([sample_499c2a85f6e8142c3f48d4251c9c7cd6_raw32.path, '-v', '-f', 'sc32']) == 0
assert (
capa.main.main(
[sample_499c2a85f6e8142c3f48d4251c9c7cd6_raw32.path, "-v", "-f", "sc32"]
)
== 0
)
def test_ruleset():
rules = capa.rules.RuleSet([
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: file rule
scope: file
features:
- characteristic(embedded pe): y
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: function rule
scope: function
features:
- characteristic(switch): y
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: basic block rule
scope: basic block
features:
- characteristic(nzxor): y
''')),
])
rules = capa.rules.RuleSet(
[
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: file rule
scope: file
features:
- characteristic(embedded pe): y
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: function rule
scope: function
features:
- characteristic(switch): y
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: basic block rule
scope: basic block
features:
- characteristic(nzxor): y
"""
)
),
]
)
assert len(rules.file_rules) == 1
assert len(rules.function_rules) == 1
assert len(rules.basic_block_rules) == 1
def test_match_across_scopes_file_function(sample_9324d1a8ae37a36ae560c37448c9705a):
rules = capa.rules.RuleSet([
# this rule should match on a function (0x4073F0)
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: install service
scope: function
examples:
- 9324d1a8ae37a36ae560c37448c9705a:0x4073F0
features:
- and:
- api: advapi32.OpenSCManagerA
- api: advapi32.CreateServiceA
- api: advapi32.StartServiceA
''')),
# this rule should match on a file feature
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: .text section
scope: file
examples:
- 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('''
rule:
meta:
name: .text section and install service
scope: file
examples:
- 9324d1a8ae37a36ae560c37448c9705a
features:
- and:
- match: install service
- match: .text section
''')),
])
extractor = capa.features.extractors.viv.VivisectFeatureExtractor(sample_9324d1a8ae37a36ae560c37448c9705a.vw, sample_9324d1a8ae37a36ae560c37448c9705a.path)
rules = capa.rules.RuleSet(
[
# this rule should match on a function (0x4073F0)
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: install service
scope: function
examples:
- 9324d1a8ae37a36ae560c37448c9705a:0x4073F0
features:
- and:
- api: advapi32.OpenSCManagerA
- api: advapi32.CreateServiceA
- api: advapi32.StartServiceA
"""
)
),
# this rule should match on a file feature
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: .text section
scope: file
examples:
- 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(
"""
rule:
meta:
name: .text section and install service
scope: file
examples:
- 9324d1a8ae37a36ae560c37448c9705a
features:
- and:
- match: install service
- match: .text section
"""
)
),
]
)
extractor = capa.features.extractors.viv.VivisectFeatureExtractor(
sample_9324d1a8ae37a36ae560c37448c9705a.vw,
sample_9324d1a8ae37a36ae560c37448c9705a.path,
)
capabilities = capa.main.find_capabilities(rules, extractor)
assert 'install service' in capabilities
assert '.text section' in capabilities
assert '.text section and install service' in capabilities
assert "install service" in capabilities
assert ".text section" in capabilities
assert ".text section and install service" in capabilities
def test_match_across_scopes(sample_9324d1a8ae37a36ae560c37448c9705a):
rules = capa.rules.RuleSet([
# this rule should match on a basic block (including at least 0x403685)
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: tight loop
scope: basic block
examples:
- 9324d1a8ae37a36ae560c37448c9705a:0x403685
features:
- characteristic(tight loop): true
''')),
# 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('''
rule:
meta:
name: kill thread loop
scope: function
examples:
- 9324d1a8ae37a36ae560c37448c9705a:0x403660
features:
- and:
- 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('''
rule:
meta:
name: kill thread program
scope: file
examples:
- 9324d1a8ae37a36ae560c37448c9705a
features:
- and:
- section: .text
- match: kill thread loop
''')),
])
extractor = capa.features.extractors.viv.VivisectFeatureExtractor(sample_9324d1a8ae37a36ae560c37448c9705a.vw, sample_9324d1a8ae37a36ae560c37448c9705a.path)
rules = capa.rules.RuleSet(
[
# this rule should match on a basic block (including at least 0x403685)
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: tight loop
scope: basic block
examples:
- 9324d1a8ae37a36ae560c37448c9705a:0x403685
features:
- characteristic(tight loop): true
"""
)
),
# 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(
"""
rule:
meta:
name: kill thread loop
scope: function
examples:
- 9324d1a8ae37a36ae560c37448c9705a:0x403660
features:
- and:
- 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(
"""
rule:
meta:
name: kill thread program
scope: file
examples:
- 9324d1a8ae37a36ae560c37448c9705a
features:
- and:
- section: .text
- match: kill thread loop
"""
)
),
]
)
extractor = capa.features.extractors.viv.VivisectFeatureExtractor(
sample_9324d1a8ae37a36ae560c37448c9705a.vw,
sample_9324d1a8ae37a36ae560c37448c9705a.path,
)
capabilities = capa.main.find_capabilities(rules, extractor)
assert 'tight loop' in capabilities
assert 'kill thread loop' in capabilities
assert 'kill thread program' in capabilities
assert "tight loop" in capabilities
assert "kill thread loop" in capabilities
assert "kill thread program" in capabilities
def test_subscope_bb_rules(sample_9324d1a8ae37a36ae560c37448c9705a):
rules = capa.rules.RuleSet([
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
scope: function
features:
- and:
- basic block:
- characteristic(tight loop): true
'''))
])
rules = capa.rules.RuleSet(
[
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
scope: function
features:
- and:
- basic block:
- characteristic(tight loop): true
"""
)
)
]
)
# tight loop at 0x403685
extractor = capa.features.extractors.viv.VivisectFeatureExtractor(sample_9324d1a8ae37a36ae560c37448c9705a.vw, sample_9324d1a8ae37a36ae560c37448c9705a.path)
extractor = capa.features.extractors.viv.VivisectFeatureExtractor(
sample_9324d1a8ae37a36ae560c37448c9705a.vw,
sample_9324d1a8ae37a36ae560c37448c9705a.path,
)
capabilities = capa.main.find_capabilities(rules, extractor)
assert 'test rule' in capabilities
assert "test rule" in capabilities
def test_byte_matching(sample_9324d1a8ae37a36ae560c37448c9705a):
rules = capa.rules.RuleSet([
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: byte match test
scope: function
features:
- and:
- bytes: ED 24 9E F4 52 A9 07 47 55 8E E1 AB 30 8E 23 61
'''))
])
rules = capa.rules.RuleSet(
[
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: byte match test
scope: function
features:
- and:
- bytes: ED 24 9E F4 52 A9 07 47 55 8E E1 AB 30 8E 23 61
"""
)
)
]
)
extractor = capa.features.extractors.viv.VivisectFeatureExtractor(sample_9324d1a8ae37a36ae560c37448c9705a.vw, sample_9324d1a8ae37a36ae560c37448c9705a.path)
extractor = capa.features.extractors.viv.VivisectFeatureExtractor(
sample_9324d1a8ae37a36ae560c37448c9705a.vw,
sample_9324d1a8ae37a36ae560c37448c9705a.path,
)
capabilities = capa.main.find_capabilities(rules, extractor)
assert 'byte match test' in capabilities
assert "byte match test" in capabilities

View File

@@ -7,13 +7,14 @@ from capa.features.insn import Number, Offset
def test_rule_ctor():
r = capa.rules.Rule('test rule', capa.rules.FUNCTION_SCOPE, Number(1), {})
r = capa.rules.Rule("test rule", capa.rules.FUNCTION_SCOPE, Number(1), {})
assert r.evaluate({Number(0): {1}}) == False
assert r.evaluate({Number(1): {1}}) == True
def test_rule_yaml():
rule = textwrap.dedent('''
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
@@ -26,16 +27,21 @@ def test_rule_yaml():
- and:
- number: 1
- number: 2
''')
"""
)
r = capa.rules.Rule.from_yaml(rule)
assert r.evaluate({Number(0): {1}}) == False
assert r.evaluate({Number(0): {1}, Number(1): {1}}) == False
assert r.evaluate({Number(0): {1}, Number(1): {1}, Number(2): {1}}) == True
assert r.evaluate({Number(0): {1}, Number(1): {1}, Number(2): {1}, Number(3): {1}}) == True
assert (
r.evaluate({Number(0): {1}, Number(1): {1}, Number(2): {1}, Number(3): {1}})
== True
)
def test_rule_yaml_complex():
rule = textwrap.dedent('''
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
@@ -50,36 +56,44 @@ def test_rule_yaml_complex():
- number: 4
- number: 5
- number: 6
''')
"""
)
r = capa.rules.Rule.from_yaml(rule)
assert r.evaluate({Number(5): {1}, Number(6): {1}, Number(7): {1}, Number(8): {1}}) == True
assert (
r.evaluate({Number(5): {1}, Number(6): {1}, Number(7): {1}, Number(8): {1}})
== True
)
assert r.evaluate({Number(6): {1}, Number(7): {1}, Number(8): {1}}) == False
def test_rule_yaml_not():
rule = textwrap.dedent('''
rule:
meta:
name: test rule
features:
- and:
- number: 1
- not:
- number: 2
''')
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- and:
- number: 1
- not:
- number: 2
"""
)
r = capa.rules.Rule.from_yaml(rule)
assert r.evaluate({Number(1): {1}}) == True
assert r.evaluate({Number(1): {1}, Number(2): {1}}) == False
def test_rule_yaml_count():
rule = textwrap.dedent('''
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- count(number(100)): 1
''')
"""
)
r = capa.rules.Rule.from_yaml(rule)
assert r.evaluate({Number(100): {}}) == False
assert r.evaluate({Number(100): {1}}) == True
@@ -87,13 +101,15 @@ def test_rule_yaml_count():
def test_rule_yaml_count_range():
rule = textwrap.dedent('''
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- count(number(100)): (1, 2)
''')
"""
)
r = capa.rules.Rule.from_yaml(rule)
assert r.evaluate({Number(100): {}}) == False
assert r.evaluate({Number(100): {1}}) == True
@@ -103,83 +119,115 @@ def test_rule_yaml_count_range():
def test_invalid_rule_feature():
with pytest.raises(capa.rules.InvalidRule):
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
features:
- foo: true
'''))
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- foo: true
"""
)
)
with pytest.raises(capa.rules.InvalidRule):
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
scope: file
features:
- characteristic(nzxor): true
'''))
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
scope: file
features:
- characteristic(nzxor): true
"""
)
)
with pytest.raises(capa.rules.InvalidRule):
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
scope: function
features:
- characteristic(embedded pe): true
'''))
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
scope: function
features:
- characteristic(embedded pe): true
"""
)
)
with pytest.raises(capa.rules.InvalidRule):
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
scope: basic block
features:
- characteristic(embedded pe): true
'''))
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
scope: basic block
features:
- characteristic(embedded pe): true
"""
)
)
def test_lib_rules():
rules = capa.rules.RuleSet([
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: a lib rule
lib: true
features:
- api: CreateFileA
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: a standard rule
lib: false
features:
- api: CreateFileW
''')),
])
rules = capa.rules.RuleSet(
[
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: a lib rule
lib: true
features:
- api: CreateFileA
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: a standard rule
lib: false
features:
- api: CreateFileW
"""
)
),
]
)
assert len(rules.function_rules) == 1
def test_subscope_rules():
rules = capa.rules.RuleSet([
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
scope: file
features:
- and:
- characteristic(embedded pe): true
- function:
rules = capa.rules.RuleSet(
[
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
scope: file
features:
- and:
- characteristic(nzxor): true
- characteristic(switch): true
'''))
])
- characteristic(embedded pe): true
- function:
- and:
- characteristic(nzxor): true
- characteristic(switch): true
"""
)
)
]
)
# the file rule scope will have one rules:
# - `test rule`
assert len(rules.file_rules) == 1
@@ -191,59 +239,84 @@ def test_subscope_rules():
def test_duplicate_rules():
with pytest.raises(capa.rules.InvalidRule):
rules = capa.rules.RuleSet([
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: rule-name
features:
- api: CreateFileA
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: rule-name
features:
- api: CreateFileW
''')),
])
rules = capa.rules.RuleSet(
[
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule-name
features:
- api: CreateFileA
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule-name
features:
- api: CreateFileW
"""
)
),
]
)
def test_missing_dependency():
with pytest.raises(capa.rules.InvalidRule):
rules = capa.rules.RuleSet([
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: dependent rule
features:
- match: missing rule
''')),
])
rules = capa.rules.RuleSet(
[
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: dependent rule
features:
- match: missing rule
"""
)
),
]
)
def test_invalid_rules():
with pytest.raises(capa.rules.InvalidRule):
r = capa.rules.Rule.from_yaml(textwrap.dedent('''
r = capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- characteristic(number(1)): True
'''))
"""
)
)
with pytest.raises(capa.rules.InvalidRule):
r = capa.rules.Rule.from_yaml(textwrap.dedent('''
r = capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- characteristic(count(number(100))): True
'''))
"""
)
)
def test_number_symbol():
rule = textwrap.dedent('''
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
@@ -256,19 +329,21 @@ def test_number_symbol():
- number: 4 = symbol name = another name
- number: 0x100 = symbol name
- number: 0x11 = (FLAG_A | FLAG_B)
''')
"""
)
r = capa.rules.Rule.from_yaml(rule)
children = list(r.statement.get_children())
assert (Number(1) in children) == True
assert (Number(0xFFFFFFFF) in children) == True
assert (Number(2, 'symbol name') in children) == True
assert (Number(3, 'symbol name') in children) == True
assert (Number(4, 'symbol name = another name') in children) == True
assert (Number(0x100, 'symbol name') in children) == True
assert (Number(2, "symbol name") in children) == True
assert (Number(3, "symbol name") in children) == True
assert (Number(4, "symbol name = another name") in children) == True
assert (Number(0x100, "symbol name") in children) == True
def test_count_number_symbol():
rule = textwrap.dedent('''
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
@@ -277,46 +352,60 @@ def test_count_number_symbol():
- count(number(2 = symbol name)): 1
- count(number(0x100 = symbol name)): 2 or more
- count(number(0x11 = (FLAG_A | FLAG_B))): 2 or more
''')
"""
)
r = capa.rules.Rule.from_yaml(rule)
assert r.evaluate({Number(2): {}}) == False
assert r.evaluate({Number(2): {1}}) == True
assert r.evaluate({Number(2): {1, 2}}) == False
assert r.evaluate({Number(0x100, 'symbol name'): {1}}) == False
assert r.evaluate({Number(0x100, 'symbol name'): {1, 2, 3}}) == True
assert r.evaluate({Number(0x100, "symbol name"): {1}}) == False
assert r.evaluate({Number(0x100, "symbol name"): {1, 2, 3}}) == True
def test_invalid_number():
with pytest.raises(capa.rules.InvalidRule):
r = capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
features:
- number: "this is a string"
'''))
r = capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- number: "this is a string"
"""
)
)
with pytest.raises(capa.rules.InvalidRule):
r = capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
features:
- number: 2=
'''))
r = capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- number: 2=
"""
)
)
with pytest.raises(capa.rules.InvalidRule):
r = capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
features:
- number: symbol name = 2
'''))
r = capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- number: symbol name = 2
"""
)
)
def test_offset_symbol():
rule = textwrap.dedent('''
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
@@ -327,18 +416,20 @@ def test_offset_symbol():
- offset: 3 = symbol name
- offset: 4 = symbol name = another name
- offset: 0x100 = symbol name
''')
"""
)
r = capa.rules.Rule.from_yaml(rule)
children = list(r.statement.get_children())
assert (Offset(1) in children) == True
assert (Offset(2, 'symbol name') in children) == True
assert (Offset(3, 'symbol name') in children) == True
assert (Offset(4, 'symbol name = another name') in children) == True
assert (Offset(0x100, 'symbol name') in children) == True
assert (Offset(2, "symbol name") in children) == True
assert (Offset(3, "symbol name") in children) == True
assert (Offset(4, "symbol name = another name") in children) == True
assert (Offset(0x100, "symbol name") in children) == True
def test_count_offset_symbol():
rule = textwrap.dedent('''
rule = textwrap.dedent(
"""
rule:
meta:
name: test rule
@@ -347,153 +438,215 @@ def test_count_offset_symbol():
- count(offset(2 = symbol name)): 1
- count(offset(0x100 = symbol name)): 2 or more
- count(offset(0x11 = (FLAG_A | FLAG_B))): 2 or more
''')
"""
)
r = capa.rules.Rule.from_yaml(rule)
assert r.evaluate({Offset(2): {}}) == False
assert r.evaluate({Offset(2): {1}}) == True
assert r.evaluate({Offset(2): {1, 2}}) == False
assert r.evaluate({Offset(0x100, 'symbol name'): {1}}) == False
assert r.evaluate({Offset(0x100, 'symbol name'): {1, 2, 3}}) == True
assert r.evaluate({Offset(0x100, "symbol name"): {1}}) == False
assert r.evaluate({Offset(0x100, "symbol name"): {1, 2, 3}}) == True
def test_invalid_offset():
with pytest.raises(capa.rules.InvalidRule):
r = capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
features:
- offset: "this is a string"
'''))
r = capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- offset: "this is a string"
"""
)
)
with pytest.raises(capa.rules.InvalidRule):
r = capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
features:
- offset: 2=
'''))
r = capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- offset: 2=
"""
)
)
with pytest.raises(capa.rules.InvalidRule):
r = capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: test rule
features:
- offset: symbol name = 2
'''))
r = capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: test rule
features:
- offset: symbol name = 2
"""
)
)
def test_filter_rules():
rules = capa.rules.RuleSet([
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: rule 1
author: joe
features:
- api: CreateFile
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: rule 2
features:
- string: joe
''')),
])
rules = rules.filter_rules_by_meta('joe')
assert len(rules) == 1
assert ('rule 1' in rules.rules)
def test_filter_rules_dependencies():
rules = capa.rules.RuleSet([
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: rule 1
features:
- match: rule 2
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: rule 2
features:
- match: rule 3
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: rule 3
features:
- api: CreateFile
''')),
])
rules = rules.filter_rules_by_meta('rule 1')
assert(len(rules.rules) == 3)
assert('rule 1' in rules.rules)
assert('rule 2' in rules.rules)
assert('rule 3' in rules.rules)
def test_filter_rules_missing_dependency():
with pytest.raises(capa.rules.InvalidRule):
capa.rules.RuleSet([
capa.rules.Rule.from_yaml(textwrap.dedent('''
rules = capa.rules.RuleSet(
[
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule 1
author: joe
features:
- api: CreateFile
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule 2
features:
- string: joe
"""
)
),
]
)
rules = rules.filter_rules_by_meta("joe")
assert len(rules) == 1
assert "rule 1" in rules.rules
def test_filter_rules_dependencies():
rules = capa.rules.RuleSet(
[
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule 1
features:
- match: rule 2
''')),
])
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule 2
features:
- match: rule 3
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule 3
features:
- api: CreateFile
"""
)
),
]
)
rules = rules.filter_rules_by_meta("rule 1")
assert len(rules.rules) == 3
assert "rule 1" in rules.rules
assert "rule 2" in rules.rules
assert "rule 3" in rules.rules
def test_filter_rules_missing_dependency():
with pytest.raises(capa.rules.InvalidRule):
capa.rules.RuleSet(
[
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule 1
author: joe
features:
- match: rule 2
"""
)
),
]
)
def test_rules_namespace_dependencies():
rules = [
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: rule 1
namespace: ns1/nsA
features:
- api: CreateFile
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
rule:
meta:
name: rule 2
namespace: ns1/nsB
features:
- api: CreateFile
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule 1
namespace: ns1/nsA
features:
- api: CreateFile
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule 2
namespace: ns1/nsB
features:
- api: CreateFile
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule 3
features:
- match: ns1/nsA
''')),
capa.rules.Rule.from_yaml(textwrap.dedent('''
"""
)
),
capa.rules.Rule.from_yaml(
textwrap.dedent(
"""
rule:
meta:
name: rule 4
features:
- match: ns1
''')),
"""
)
),
]
r3 = set(map(lambda r: r.name, capa.rules.get_rules_and_dependencies(rules, 'rule 3')))
assert 'rule 1' in r3
assert 'rule 2' not in r3
assert 'rule 4' not in r3
r4 = set(map(lambda r: r.name, capa.rules.get_rules_and_dependencies(rules, 'rule 4')))
assert 'rule 1' in r4
assert 'rule 2' in r4
assert 'rule 3' not in r4
r3 = set(
map(lambda r: r.name, capa.rules.get_rules_and_dependencies(rules, "rule 3"))
)
assert "rule 1" in r3
assert "rule 2" not in r3
assert "rule 4" not in r3
r4 = set(
map(lambda r: r.name, capa.rules.get_rules_and_dependencies(rules, "rule 4"))
)
assert "rule 1" in r4
assert "rule 2" in r4
assert "rule 3" not in r4

View File

@@ -1,5 +1,3 @@
import collections
import viv_utils
import capa.features
@@ -26,9 +24,13 @@ def extract_function_features(f):
features = collections.defaultdict(set)
for bb in f.basic_blocks:
for insn in bb.instructions:
for feature, va in capa.features.extractors.viv.insn.extract_features(f, bb, insn):
for feature, va in capa.features.extractors.viv.insn.extract_features(
f, bb, insn
):
features[feature].add(va)
for feature, va in capa.features.extractors.viv.basicblock.extract_features(f, bb):
for feature, va in capa.features.extractors.viv.basicblock.extract_features(
f, bb
):
features[feature].add(va)
for feature, va in capa.features.extractors.viv.function.extract_features(f):
features[feature].add(va)
@@ -38,7 +40,9 @@ def extract_function_features(f):
def extract_basic_block_features(f, bb):
features = set({})
for insn in bb.instructions:
for feature, _ in capa.features.extractors.viv.insn.extract_features(f, bb, insn):
for feature, _ in capa.features.extractors.viv.insn.extract_features(
f, bb, insn
):
features.add(feature)
for feature, _ in capa.features.extractors.viv.basicblock.extract_features(f, bb):
features.add(feature)
@@ -47,50 +51,60 @@ def extract_basic_block_features(f, bb):
def test_api_features(mimikatz):
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x403BAC))
assert capa.features.insn.API('advapi32.CryptAcquireContextW') in features
assert capa.features.insn.API('advapi32.CryptAcquireContext') in features
assert capa.features.insn.API('advapi32.CryptGenKey') in features
assert capa.features.insn.API('advapi32.CryptImportKey') in features
assert capa.features.insn.API('advapi32.CryptDestroyKey') in features
assert capa.features.insn.API('CryptAcquireContextW') in features
assert capa.features.insn.API('CryptAcquireContext') in features
assert capa.features.insn.API('CryptGenKey') in features
assert capa.features.insn.API('CryptImportKey') in features
assert capa.features.insn.API('CryptDestroyKey') in features
assert capa.features.insn.API("advapi32.CryptAcquireContextW") in features
assert capa.features.insn.API("advapi32.CryptAcquireContext") in features
assert capa.features.insn.API("advapi32.CryptGenKey") in features
assert capa.features.insn.API("advapi32.CryptImportKey") in features
assert capa.features.insn.API("advapi32.CryptDestroyKey") in features
assert capa.features.insn.API("CryptAcquireContextW") in features
assert capa.features.insn.API("CryptAcquireContext") in features
assert capa.features.insn.API("CryptGenKey") in features
assert capa.features.insn.API("CryptImportKey") in features
assert capa.features.insn.API("CryptDestroyKey") in features
def test_api_features_64_bit(sample_a198216798ca38f280dc413f8c57f2c2):
features = extract_function_features(viv_utils.Function(sample_a198216798ca38f280dc413f8c57f2c2.vw, 0x4011B0))
assert capa.features.insn.API('kernel32.GetStringTypeA') in features
assert capa.features.insn.API('kernel32.GetStringTypeW') not in features
assert capa.features.insn.API('kernel32.GetStringType') in features
assert capa.features.insn.API('GetStringTypeA') in features
assert capa.features.insn.API('GetStringType') in features
features = extract_function_features(
viv_utils.Function(sample_a198216798ca38f280dc413f8c57f2c2.vw, 0x4011B0)
)
assert capa.features.insn.API("kernel32.GetStringTypeA") in features
assert capa.features.insn.API("kernel32.GetStringTypeW") not in features
assert capa.features.insn.API("kernel32.GetStringType") in features
assert capa.features.insn.API("GetStringTypeA") in features
assert capa.features.insn.API("GetStringType") in features
# call via thunk in IDA Pro
features = extract_function_features(viv_utils.Function(sample_a198216798ca38f280dc413f8c57f2c2.vw, 0x401CB0))
assert capa.features.insn.API('msvcrt.vfprintf') in features
assert capa.features.insn.API('vfprintf') in features
features = extract_function_features(
viv_utils.Function(sample_a198216798ca38f280dc413f8c57f2c2.vw, 0x401CB0)
)
assert capa.features.insn.API("msvcrt.vfprintf") in features
assert capa.features.insn.API("vfprintf") in features
def test_string_features(mimikatz):
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x40105D))
assert capa.features.String('SCardControl') in features
assert capa.features.String('SCardTransmit') in features
assert capa.features.String('ACR > ') in features
assert capa.features.String("SCardControl") in features
assert capa.features.String("SCardTransmit") in features
assert capa.features.String("ACR > ") in features
# other strings not in this function
assert capa.features.String('bcrypt.dll') not in features
assert capa.features.String("bcrypt.dll") not in features
def test_byte_features(sample_9324d1a8ae37a36ae560c37448c9705a):
features = extract_function_features(viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60))
wanted = capa.features.Bytes(b'\xED\x24\x9E\xF4\x52\xA9\x07\x47\x55\x8E\xE1\xAB\x30\x8E\x23\x61')
features = extract_function_features(
viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60)
)
wanted = capa.features.Bytes(
b"\xED\x24\x9E\xF4\x52\xA9\x07\x47\x55\x8E\xE1\xAB\x30\x8E\x23\x61"
)
# use `==` rather than `is` because the result is not `True` but a truthy value.
assert wanted.evaluate(features) == True
def test_byte_features64(sample_lab21_01):
features = extract_function_features(viv_utils.Function(sample_lab21_01.vw, 0x1400010C0))
wanted = capa.features.Bytes(b'\x32\xA2\xDF\x2D\x99\x2B\x00\x00')
features = extract_function_features(
viv_utils.Function(sample_lab21_01.vw, 0x1400010C0)
)
wanted = capa.features.Bytes(b"\x32\xA2\xDF\x2D\x99\x2B\x00\x00")
# use `==` rather than `is` because the result is not `True` but a truthy value.
assert wanted.evaluate(features) == True
@@ -116,11 +130,11 @@ def test_offset_features(mimikatz):
def test_nzxor_features(mimikatz):
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x410DFC))
assert capa.features.Characteristic('nzxor', True) in features # 0x0410F0B
assert capa.features.Characteristic("nzxor", True) in features # 0x0410F0B
def get_bb_insn(f, va):
'''fetch the BasicBlock and Instruction instances for the given VA in the given function.'''
"""fetch the BasicBlock and Instruction instances for the given VA in the given function."""
for bb in f.basic_blocks:
for insn in bb.instructions:
if insn.va == va:
@@ -133,7 +147,9 @@ def test_is_security_cookie(mimikatz):
f = viv_utils.Function(mimikatz.vw, 0x410DFC)
for va in [0x0410F0B]:
bb, insn = get_bb_insn(f, va)
assert capa.features.extractors.viv.insn.is_security_cookie(f, bb, insn) == False
assert (
capa.features.extractors.viv.insn.is_security_cookie(f, bb, insn) == False
)
# security cookie initial set and final check
f = viv_utils.Function(mimikatz.vw, 0x46C54A)
@@ -144,24 +160,26 @@ def test_is_security_cookie(mimikatz):
def test_mnemonic_features(mimikatz):
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x40105D))
assert capa.features.insn.Mnemonic('push') in features
assert capa.features.insn.Mnemonic('movzx') in features
assert capa.features.insn.Mnemonic('xor') in features
assert capa.features.insn.Mnemonic("push") in features
assert capa.features.insn.Mnemonic("movzx") in features
assert capa.features.insn.Mnemonic("xor") in features
assert capa.features.insn.Mnemonic('in') not in features
assert capa.features.insn.Mnemonic('out') not in features
assert capa.features.insn.Mnemonic("in") not in features
assert capa.features.insn.Mnemonic("out") not in features
def test_peb_access_features(sample_a933a1a402775cfa94b6bee0963f4b46):
features = extract_function_features(viv_utils.Function(sample_a933a1a402775cfa94b6bee0963f4b46.vw, 0xABA6FEC))
assert capa.features.Characteristic('peb access', True) in features
features = extract_function_features(
viv_utils.Function(sample_a933a1a402775cfa94b6bee0963f4b46.vw, 0xABA6FEC)
)
assert capa.features.Characteristic("peb access", True) in features
def test_file_section_name_features(mimikatz):
features = extract_file_features(mimikatz.vw, mimikatz.path)
assert capa.features.file.Section('.rsrc') in features
assert capa.features.file.Section('.text') in features
assert capa.features.file.Section('.nope') not in features
assert capa.features.file.Section(".rsrc") in features
assert capa.features.file.Section(".text") in features
assert capa.features.file.Section(".nope") not in features
def test_tight_loop_features(mimikatz):
@@ -170,7 +188,7 @@ def test_tight_loop_features(mimikatz):
if bb.va != 0x402F8E:
continue
features = extract_basic_block_features(f, bb)
assert capa.features.Characteristic('tight loop', True) in features
assert capa.features.Characteristic("tight loop", True) in features
assert capa.features.basicblock.BasicBlock() in features
@@ -180,119 +198,166 @@ def test_tight_loop_bb_features(mimikatz):
if bb.va != 0x402F8E:
continue
features = extract_basic_block_features(f, bb)
assert capa.features.Characteristic('tight loop', True) in features
assert capa.features.Characteristic("tight loop", True) in features
assert capa.features.basicblock.BasicBlock() in features
def test_file_export_name_features(kernel32):
features = extract_file_features(kernel32.vw, kernel32.path)
assert capa.features.file.Export('BaseThreadInitThunk') in features
assert capa.features.file.Export('lstrlenW') in features
assert capa.features.file.Export("BaseThreadInitThunk") in features
assert capa.features.file.Export("lstrlenW") in features
def test_file_import_name_features(mimikatz):
features = extract_file_features(mimikatz.vw, mimikatz.path)
assert capa.features.file.Import('advapi32.CryptSetHashParam') in features
assert capa.features.file.Import('CryptSetHashParam') in features
assert capa.features.file.Import('kernel32.IsWow64Process') in features
assert capa.features.file.Import('msvcrt.exit') in features
assert capa.features.file.Import('cabinet.#11') in features
assert capa.features.file.Import('#11') not in features
assert capa.features.file.Import("advapi32.CryptSetHashParam") in features
assert capa.features.file.Import("CryptSetHashParam") in features
assert capa.features.file.Import("kernel32.IsWow64Process") in features
assert capa.features.file.Import("msvcrt.exit") in features
assert capa.features.file.Import("cabinet.#11") in features
assert capa.features.file.Import("#11") not in features
def test_cross_section_flow_features(sample_a198216798ca38f280dc413f8c57f2c2):
features = extract_function_features(viv_utils.Function(sample_a198216798ca38f280dc413f8c57f2c2.vw, 0x4014D0))
assert capa.features.Characteristic('cross section flow', True) in features
features = extract_function_features(
viv_utils.Function(sample_a198216798ca38f280dc413f8c57f2c2.vw, 0x4014D0)
)
assert capa.features.Characteristic("cross section flow", True) in features
# this function has calls to some imports,
# which should not trigger cross-section flow characteristic
features = extract_function_features(viv_utils.Function(sample_a198216798ca38f280dc413f8c57f2c2.vw, 0x401563))
assert capa.features.Characteristic('cross section flow', True) not in features
features = extract_function_features(
viv_utils.Function(sample_a198216798ca38f280dc413f8c57f2c2.vw, 0x401563)
)
assert capa.features.Characteristic("cross section flow", True) not in features
def test_segment_access_features(sample_a933a1a402775cfa94b6bee0963f4b46):
features = extract_function_features(viv_utils.Function(sample_a933a1a402775cfa94b6bee0963f4b46.vw, 0xABA6FEC))
assert capa.features.Characteristic('fs access', True) in features
features = extract_function_features(
viv_utils.Function(sample_a933a1a402775cfa94b6bee0963f4b46.vw, 0xABA6FEC)
)
assert capa.features.Characteristic("fs access", True) in features
def test_thunk_features(sample_9324d1a8ae37a36ae560c37448c9705a):
features = extract_function_features(viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x407970))
assert capa.features.insn.API('kernel32.CreateToolhelp32Snapshot') in features
assert capa.features.insn.API('CreateToolhelp32Snapshot') in features
features = extract_function_features(
viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x407970)
)
assert capa.features.insn.API("kernel32.CreateToolhelp32Snapshot") in features
assert capa.features.insn.API("CreateToolhelp32Snapshot") in features
def test_file_embedded_pe(pma_lab_12_04):
features = extract_file_features(pma_lab_12_04.vw, pma_lab_12_04.path)
assert capa.features.Characteristic('embedded pe', True) in features
assert capa.features.Characteristic("embedded pe", True) in features
def test_stackstring_features(mimikatz):
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x4556E5))
assert capa.features.Characteristic('stack string', True) in features
assert capa.features.Characteristic("stack string", True) in features
def test_switch_features(mimikatz):
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x409411))
assert capa.features.Characteristic('switch', True) in features
assert capa.features.Characteristic("switch", True) in features
features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x409393))
assert capa.features.Characteristic('switch', True) not in features
assert capa.features.Characteristic("switch", True) not in features
def test_recursive_call_feature(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41):
features = extract_function_features(viv_utils.Function(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw, 0x10003100))
assert capa.features.Characteristic('recursive call', True) in features
def test_recursive_call_feature(
sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41,
):
features = extract_function_features(
viv_utils.Function(
sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw,
0x10003100,
)
)
assert capa.features.Characteristic("recursive call", True) in features
features = extract_function_features(viv_utils.Function(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw, 0x10007B00))
assert capa.features.Characteristic('recursive call', True) not in features
features = extract_function_features(
viv_utils.Function(
sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw,
0x10007B00,
)
)
assert capa.features.Characteristic("recursive call", True) not in features
def test_loop_feature(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41):
features = extract_function_features(viv_utils.Function(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw, 0x10003D30))
assert capa.features.Characteristic('loop', True) in features
def test_loop_feature(
sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41,
):
features = extract_function_features(
viv_utils.Function(
sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw,
0x10003D30,
)
)
assert capa.features.Characteristic("loop", True) in features
features = extract_function_features(viv_utils.Function(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw, 0x10007250))
assert capa.features.Characteristic('loop', True) not in features
features = extract_function_features(
viv_utils.Function(
sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw,
0x10007250,
)
)
assert capa.features.Characteristic("loop", True) not in features
def test_file_string_features(sample_bfb9b5391a13d0afd787e87ab90f14f5):
features = extract_file_features(sample_bfb9b5391a13d0afd787e87ab90f14f5.vw, sample_bfb9b5391a13d0afd787e87ab90f14f5.path)
assert capa.features.String('WarStop') in features # ASCII, offset 0x40EC
assert capa.features.String('cimage/png') in features # UTF-16 LE, offset 0x350E
features = extract_file_features(
sample_bfb9b5391a13d0afd787e87ab90f14f5.vw,
sample_bfb9b5391a13d0afd787e87ab90f14f5.path,
)
assert capa.features.String("WarStop") in features # ASCII, offset 0x40EC
assert capa.features.String("cimage/png") in features # UTF-16 LE, offset 0x350E
def test_function_calls_to(sample_9324d1a8ae37a36ae560c37448c9705a):
features = extract_function_features(viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60))
assert capa.features.Characteristic('calls to', True) in features
assert len(features[capa.features.Characteristic('calls to', True)]) == 1
features = extract_function_features(
viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60)
)
assert capa.features.Characteristic("calls to", True) in features
assert len(features[capa.features.Characteristic("calls to", True)]) == 1
def test_function_calls_to64(sample_lab21_01):
features = extract_function_features(viv_utils.Function(sample_lab21_01.vw, 0x1400052D0)) # memcpy
assert capa.features.Characteristic('calls to', True) in features
assert len(features[capa.features.Characteristic('calls to', True)]) == 8
features = extract_function_features(
viv_utils.Function(sample_lab21_01.vw, 0x1400052D0)
) # memcpy
assert capa.features.Characteristic("calls to", True) in features
assert len(features[capa.features.Characteristic("calls to", True)]) == 8
def test_function_calls_from(sample_9324d1a8ae37a36ae560c37448c9705a):
features = extract_function_features(viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60))
assert capa.features.Characteristic('calls from', True) in features
assert len(features[capa.features.Characteristic('calls from', True)]) == 23
features = extract_function_features(
viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60)
)
assert capa.features.Characteristic("calls from", True) in features
assert len(features[capa.features.Characteristic("calls from", True)]) == 23
def test_basic_block_count(sample_9324d1a8ae37a36ae560c37448c9705a):
features = extract_function_features(viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60))
features = extract_function_features(
viv_utils.Function(sample_9324d1a8ae37a36ae560c37448c9705a.vw, 0x406F60)
)
assert len(features[capa.features.basicblock.BasicBlock()]) == 26
def test_indirect_call_features(sample_a933a1a402775cfa94b6bee0963f4b46):
features = extract_function_features(viv_utils.Function(sample_a933a1a402775cfa94b6bee0963f4b46.vw, 0xABA68A0))
assert capa.features.Characteristic('indirect call', True) in features
assert len(features[capa.features.Characteristic('indirect call', True)]) == 3
features = extract_function_features(
viv_utils.Function(sample_a933a1a402775cfa94b6bee0963f4b46.vw, 0xABA68A0)
)
assert capa.features.Characteristic("indirect call", True) in features
assert len(features[capa.features.Characteristic("indirect call", True)]) == 3
def test_indirect_calls_resolved(sample_c91887d861d9bd4a5872249b641bc9f9):
features = extract_function_features(viv_utils.Function(sample_c91887d861d9bd4a5872249b641bc9f9.vw, 0x401A77))
assert capa.features.insn.API('kernel32.CreatePipe') in features
assert capa.features.insn.API('kernel32.SetHandleInformation') in features
assert capa.features.insn.API('kernel32.CloseHandle') in features
assert capa.features.insn.API('kernel32.WriteFile') in features
features = extract_function_features(
viv_utils.Function(sample_c91887d861d9bd4a5872249b641bc9f9.vw, 0x401A77)
)
assert capa.features.insn.API("kernel32.CreatePipe") in features
assert capa.features.insn.API("kernel32.SetHandleInformation") in features
assert capa.features.insn.API("kernel32.CloseHandle") in features
assert capa.features.insn.API("kernel32.WriteFile") in features