mirror of
https://github.com/mandiant/capa.git
synced 2025-12-22 07:10:29 -08:00
black 20.8b1 updates
This commit is contained in:
@@ -84,7 +84,16 @@ def dumps(extractor):
|
||||
returns:
|
||||
str: the serialized features.
|
||||
"""
|
||||
ret = {"version": 1, "functions": {}, "scopes": {"file": [], "function": [], "basic block": [], "instruction": [],}}
|
||||
ret = {
|
||||
"version": 1,
|
||||
"functions": {},
|
||||
"scopes": {
|
||||
"file": [],
|
||||
"function": [],
|
||||
"basic block": [],
|
||||
"instruction": [],
|
||||
},
|
||||
}
|
||||
|
||||
for feature, va in extractor.extract_file_features():
|
||||
ret["scopes"]["file"].append(serialize_feature(feature) + (hex(va), ()))
|
||||
@@ -99,7 +108,16 @@ def dumps(extractor):
|
||||
ret["functions"][hex(f)][hex(bb)] = []
|
||||
|
||||
for feature, va in extractor.extract_basic_block_features(f, bb):
|
||||
ret["scopes"]["basic block"].append(serialize_feature(feature) + (hex(va), (hex(f), hex(bb),)))
|
||||
ret["scopes"]["basic block"].append(
|
||||
serialize_feature(feature)
|
||||
+ (
|
||||
hex(va),
|
||||
(
|
||||
hex(f),
|
||||
hex(bb),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
for insnva, insn in sorted(
|
||||
[(insn.__int__(), insn) for insn in extractor.get_instructions(f, bb)], key=lambda p: p[0]
|
||||
@@ -108,7 +126,15 @@ def dumps(extractor):
|
||||
|
||||
for feature, va in extractor.extract_insn_features(f, bb, insn):
|
||||
ret["scopes"]["instruction"].append(
|
||||
serialize_feature(feature) + (hex(va), (hex(f), hex(bb), hex(insnva),))
|
||||
serialize_feature(feature)
|
||||
+ (
|
||||
hex(va),
|
||||
(
|
||||
hex(f),
|
||||
hex(bb),
|
||||
hex(insnva),
|
||||
),
|
||||
)
|
||||
)
|
||||
return json.dumps(ret)
|
||||
|
||||
|
||||
@@ -497,7 +497,13 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel):
|
||||
|
||||
if len(locations) == 1:
|
||||
# only one location for feature so no need to nest children
|
||||
parent2 = self.render_capa_doc_feature(parent, feature, next(iter(locations)), doc, display=display,)
|
||||
parent2 = self.render_capa_doc_feature(
|
||||
parent,
|
||||
feature,
|
||||
next(iter(locations)),
|
||||
doc,
|
||||
display=display,
|
||||
)
|
||||
else:
|
||||
# feature has multiple children, nest under one parent feature node
|
||||
parent2 = CapaExplorerFeatureItem(parent, display)
|
||||
|
||||
@@ -102,6 +102,9 @@ def collect_metadata():
|
||||
"sha256": sha256,
|
||||
"path": idaapi.get_input_file_path(),
|
||||
},
|
||||
"analysis": {"format": idaapi.get_file_type_name(), "extractor": "ida",},
|
||||
"analysis": {
|
||||
"format": idaapi.get_file_type_name(),
|
||||
"extractor": "ida",
|
||||
},
|
||||
"version": capa.version.__version__,
|
||||
}
|
||||
|
||||
@@ -105,7 +105,12 @@ def find_capabilities(ruleset, extractor, disable_progress=None):
|
||||
all_function_matches = collections.defaultdict(list)
|
||||
all_bb_matches = collections.defaultdict(list)
|
||||
|
||||
meta = {"feature_counts": {"file": 0, "functions": {},}}
|
||||
meta = {
|
||||
"feature_counts": {
|
||||
"file": 0,
|
||||
"functions": {},
|
||||
}
|
||||
}
|
||||
|
||||
for f in tqdm.tqdm(list(extractor.get_functions()), disable=disable_progress, desc="matching", unit=" functions"):
|
||||
function_matches, bb_matches, feature_count = find_function_capabilities(ruleset, extractor, f)
|
||||
|
||||
@@ -152,7 +152,10 @@ def convert_match_to_result_document(rules, capabilities, result):
|
||||
scope = rule.meta["scope"]
|
||||
doc["node"] = {
|
||||
"type": "statement",
|
||||
"statement": {"type": "subscope", "subscope": scope,},
|
||||
"statement": {
|
||||
"type": "subscope",
|
||||
"subscope": scope,
|
||||
},
|
||||
}
|
||||
|
||||
for location in doc["locations"]:
|
||||
@@ -257,5 +260,7 @@ class CapaJsonObjectEncoder(json.JSONEncoder):
|
||||
|
||||
def render_json(meta, rules, capabilities):
|
||||
return json.dumps(
|
||||
convert_capabilities_to_result_document(meta, rules, capabilities), cls=CapaJsonObjectEncoder, sort_keys=True,
|
||||
convert_capabilities_to_result_document(meta, rules, capabilities),
|
||||
cls=CapaJsonObjectEncoder,
|
||||
sort_keys=True,
|
||||
)
|
||||
|
||||
@@ -109,7 +109,12 @@ def render_attack(doc, ostream):
|
||||
inner_rows.append("%s::%s %s" % (rutils.bold(technique), subtechnique, id))
|
||||
else:
|
||||
raise RuntimeError("unexpected ATT&CK spec format")
|
||||
rows.append((rutils.bold(tactic.upper()), "\n".join(inner_rows),))
|
||||
rows.append(
|
||||
(
|
||||
rutils.bold(tactic.upper()),
|
||||
"\n".join(inner_rows),
|
||||
)
|
||||
)
|
||||
|
||||
if rows:
|
||||
ostream.write(
|
||||
|
||||
@@ -399,7 +399,11 @@ def lint_rule(ctx, rule):
|
||||
print("")
|
||||
print(
|
||||
"%s%s %s"
|
||||
% (" (nursery) " if is_nursery_rule(rule) else "", rule.name, ("(%s)" % category) if category else "",)
|
||||
% (
|
||||
" (nursery) " if is_nursery_rule(rule) else "",
|
||||
rule.name,
|
||||
("(%s)" % category) if category else "",
|
||||
)
|
||||
)
|
||||
|
||||
level = "WARN" if is_nursery_rule(rule) else "FAIL"
|
||||
@@ -407,7 +411,12 @@ def lint_rule(ctx, rule):
|
||||
for violation in violations:
|
||||
print(
|
||||
"%s %s: %s: %s"
|
||||
% (" " if is_nursery_rule(rule) else "", level, violation.name, violation.recommendation,)
|
||||
% (
|
||||
" " if is_nursery_rule(rule) else "",
|
||||
level,
|
||||
violation.name,
|
||||
violation.recommendation,
|
||||
)
|
||||
)
|
||||
|
||||
elif len(violations) == 0 and is_nursery_rule(rule):
|
||||
@@ -487,7 +496,9 @@ def main(argv=None):
|
||||
parser.add_argument("rules", type=str, help="Path to rules")
|
||||
parser.add_argument("--samples", type=str, default=samples_path, help="Path to samples")
|
||||
parser.add_argument(
|
||||
"--thorough", action="store_true", help="Enable thorough linting - takes more time, but does a better job",
|
||||
"--thorough",
|
||||
action="store_true",
|
||||
help="Enable thorough linting - takes more time, but does a better job",
|
||||
)
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help="Enable debug logging")
|
||||
parser.add_argument("-q", "--quiet", action="store_true", help="Disable all output but errors")
|
||||
|
||||
6
setup.py
6
setup.py
@@ -42,7 +42,11 @@ setuptools.setup(
|
||||
url="https://www.github.com/fireeye/capa",
|
||||
packages=setuptools.find_packages(exclude=["tests"]),
|
||||
package_dir={"capa": "capa"},
|
||||
entry_points={"console_scripts": ["capa=capa.main:main",]},
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"capa=capa.main:main",
|
||||
]
|
||||
},
|
||||
include_package_data=True,
|
||||
install_requires=requirements,
|
||||
extras_require={
|
||||
|
||||
@@ -340,10 +340,20 @@ FEATURE_PRESENCE_TESTS = [
|
||||
("mimikatz", "function=0x4556E5", capa.features.insn.API("advapi32.LsaQueryInformationPolicy"), True),
|
||||
("mimikatz", "function=0x4556E5", capa.features.insn.API("LsaQueryInformationPolicy"), True),
|
||||
# insn/api: x64
|
||||
("kernel32-64", "function=0x180001010", capa.features.insn.API("RtlVirtualUnwind"), True,),
|
||||
(
|
||||
"kernel32-64",
|
||||
"function=0x180001010",
|
||||
capa.features.insn.API("RtlVirtualUnwind"),
|
||||
True,
|
||||
),
|
||||
("kernel32-64", "function=0x180001010", capa.features.insn.API("RtlVirtualUnwind"), True),
|
||||
# insn/api: x64 thunk
|
||||
("kernel32-64", "function=0x1800202B0", capa.features.insn.API("RtlCaptureContext"), True,),
|
||||
(
|
||||
"kernel32-64",
|
||||
"function=0x1800202B0",
|
||||
capa.features.insn.API("RtlCaptureContext"),
|
||||
True,
|
||||
),
|
||||
("kernel32-64", "function=0x1800202B0", capa.features.insn.API("RtlCaptureContext"), True),
|
||||
# insn/api: resolve indirect calls
|
||||
("c91887...", "function=0x401A77", capa.features.insn.API("kernel32.CreatePipe"), True),
|
||||
|
||||
@@ -59,7 +59,13 @@ def test_some():
|
||||
)
|
||||
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},}
|
||||
{
|
||||
Number(0): {1},
|
||||
Number(1): {1},
|
||||
Number(2): {1},
|
||||
Number(3): {1},
|
||||
Number(4): {1},
|
||||
}
|
||||
)
|
||||
== True
|
||||
)
|
||||
@@ -258,7 +264,9 @@ def test_match_matched_rules():
|
||||
]
|
||||
|
||||
features, matches = capa.engine.match(
|
||||
capa.engine.topologically_order_rules(rules), {capa.features.insn.Number(100): {1}}, 0x0,
|
||||
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
|
||||
@@ -266,7 +274,9 @@ def test_match_matched_rules():
|
||||
# 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,
|
||||
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
|
||||
@@ -312,22 +322,30 @@ def test_regex():
|
||||
),
|
||||
]
|
||||
features, matches = capa.engine.match(
|
||||
capa.engine.topologically_order_rules(rules), {capa.features.insn.Number(100): {1}}, 0x0,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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
|
||||
@@ -350,7 +368,9 @@ def test_regex_ignorecase():
|
||||
),
|
||||
]
|
||||
features, matches = capa.engine.match(
|
||||
capa.engine.topologically_order_rules(rules), {capa.features.String("aBBBBa"): {1}}, 0x0,
|
||||
capa.engine.topologically_order_rules(rules),
|
||||
{capa.features.String("aBBBBa"): {1}},
|
||||
0x0,
|
||||
)
|
||||
assert capa.features.MatchedRule("test rule") in features
|
||||
|
||||
@@ -429,7 +449,9 @@ def test_match_namespace():
|
||||
]
|
||||
|
||||
features, matches = capa.engine.match(
|
||||
capa.engine.topologically_order_rules(rules), {capa.features.insn.API("CreateFile"): {1}}, 0x0,
|
||||
capa.engine.topologically_order_rules(rules),
|
||||
{capa.features.insn.API("CreateFile"): {1}},
|
||||
0x0,
|
||||
)
|
||||
assert "CreateFile API" in matches
|
||||
assert "file-create" in matches
|
||||
@@ -439,7 +461,9 @@ def test_match_namespace():
|
||||
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,
|
||||
capa.engine.topologically_order_rules(rules),
|
||||
{capa.features.insn.API("WriteFile"): {1}},
|
||||
0x0,
|
||||
)
|
||||
assert "WriteFile API" in matches
|
||||
assert "file-create" not in matches
|
||||
|
||||
@@ -21,13 +21,19 @@ import capa.features.extractors
|
||||
EXTRACTOR = capa.features.extractors.NullFeatureExtractor(
|
||||
{
|
||||
"base address": 0x401000,
|
||||
"file features": [(0x402345, capa.features.Characteristic("embedded pe")),],
|
||||
"file features": [
|
||||
(0x402345, capa.features.Characteristic("embedded pe")),
|
||||
],
|
||||
"functions": {
|
||||
0x401000: {
|
||||
"features": [(0x401000, capa.features.Characteristic("indirect call")),],
|
||||
"features": [
|
||||
(0x401000, capa.features.Characteristic("indirect call")),
|
||||
],
|
||||
"basic blocks": {
|
||||
0x401000: {
|
||||
"features": [(0x401000, capa.features.Characteristic("tight loop")),],
|
||||
"features": [
|
||||
(0x401000, capa.features.Characteristic("tight loop")),
|
||||
],
|
||||
"instructions": {
|
||||
0x401000: {
|
||||
"features": [
|
||||
@@ -35,7 +41,11 @@ EXTRACTOR = capa.features.extractors.NullFeatureExtractor(
|
||||
(0x401000, capa.features.Characteristic("nzxor")),
|
||||
],
|
||||
},
|
||||
0x401002: {"features": [(0x401002, capa.features.insn.Mnemonic("mov")),],},
|
||||
0x401002: {
|
||||
"features": [
|
||||
(0x401002, capa.features.insn.Mnemonic("mov")),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -44,7 +44,17 @@ def test_main_single_rule(z9324d_extractor, tmpdir):
|
||||
path = z9324d_extractor.path
|
||||
rule_file = tmpdir.mkdir("capa").join("rule.yml")
|
||||
rule_file.write(RULE_CONTENT)
|
||||
assert capa.main.main([path, "-v", "-r", rule_file.strpath,]) == 0
|
||||
assert (
|
||||
capa.main.main(
|
||||
[
|
||||
path,
|
||||
"-v",
|
||||
"-r",
|
||||
rule_file.strpath,
|
||||
]
|
||||
)
|
||||
== 0
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.xfail(sys.version_info >= (3, 0), reason="vivsect only works on py2")
|
||||
|
||||
@@ -680,12 +680,16 @@ def test_regex_values_always_string():
|
||||
),
|
||||
]
|
||||
features, matches = capa.engine.match(
|
||||
capa.engine.topologically_order_rules(rules), {capa.features.String("123"): {1}}, 0x0,
|
||||
capa.engine.topologically_order_rules(rules),
|
||||
{capa.features.String("123"): {1}},
|
||||
0x0,
|
||||
)
|
||||
assert capa.features.MatchedRule("test rule") in features
|
||||
|
||||
features, matches = capa.engine.match(
|
||||
capa.engine.topologically_order_rules(rules), {capa.features.String("0x123"): {1}}, 0x0,
|
||||
capa.engine.topologically_order_rules(rules),
|
||||
{capa.features.String("0x123"): {1}},
|
||||
0x0,
|
||||
)
|
||||
assert capa.features.MatchedRule("test rule") in features
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@ from fixtures import *
|
||||
|
||||
|
||||
@parametrize(
|
||||
"sample,scope,feature,expected", FEATURE_PRESENCE_TESTS, indirect=["sample", "scope"],
|
||||
"sample,scope,feature,expected",
|
||||
FEATURE_PRESENCE_TESTS,
|
||||
indirect=["sample", "scope"],
|
||||
)
|
||||
def test_viv_features(sample, scope, feature, expected):
|
||||
with xfail(sys.version_info >= (3, 0), reason="vivsect only works on py2"):
|
||||
@@ -19,7 +21,9 @@ def test_viv_features(sample, scope, feature, expected):
|
||||
|
||||
|
||||
@parametrize(
|
||||
"sample,scope,feature,expected", FEATURE_COUNT_TESTS, indirect=["sample", "scope"],
|
||||
"sample,scope,feature,expected",
|
||||
FEATURE_COUNT_TESTS,
|
||||
indirect=["sample", "scope"],
|
||||
)
|
||||
def test_viv_feature_counts(sample, scope, feature, expected):
|
||||
with xfail(sys.version_info >= (3, 0), reason="vivsect only works on py2"):
|
||||
|
||||
Reference in New Issue
Block a user