From d15f6ae0c9dddcfdc699a9e3e33b4ddd0cb1fb96 Mon Sep 17 00:00:00 2001 From: William Ballenthin Date: Mon, 6 Jul 2020 16:49:19 -0600 Subject: [PATCH 1/5] offset: use description closes #102 --- capa/features/insn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capa/features/insn.py b/capa/features/insn.py index 5b6b16f2..f6b24b43 100644 --- a/capa/features/insn.py +++ b/capa/features/insn.py @@ -22,7 +22,7 @@ class Number(Feature): class Offset(Feature): def __init__(self, value, description=None): - super(Offset, self).__init__([value]) + super(Offset, self).__init__([value], description) self.value = value def get_args_str(self): From 0fe8c9352ec27d087989c739851ecb3cce9f19ba Mon Sep 17 00:00:00 2001 From: William Ballenthin Date: Mon, 6 Jul 2020 16:50:18 -0600 Subject: [PATCH 2/5] rules: use six.string_types to better support py2 --- capa/features/extractors/viv/insn.py | 1 - capa/rules.py | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/capa/features/extractors/viv/insn.py b/capa/features/extractors/viv/insn.py index 1516b13e..5e83c3b2 100644 --- a/capa/features/extractors/viv/insn.py +++ b/capa/features/extractors/viv/insn.py @@ -7,7 +7,6 @@ from capa.features import MAX_BYTES_FEATURE_SIZE, Bytes, String, Characteristic from capa.features.insn import Number, Offset, Mnemonic from capa.features.extractors.viv.indirect_calls import NotFoundError, resolve_indirect_call - # security cookie checks may perform non-zeroing XORs, these are expected within a certain # byte range within the first and returning basic blocks, this helps to reduce FP features SECURITY_COOKIE_BYTES_DELTA = 0x40 diff --git a/capa/rules.py b/capa/rules.py index f81cfac3..6347f754 100644 --- a/capa/rules.py +++ b/capa/rules.py @@ -211,7 +211,7 @@ def parse_description(s, value_type, description=None): """ s can be an int or a string """ - if value_type != "string" and isinstance(s, str) and " = " in s: + if value_type != "string" and isinstance(s, six.string_types) and " = " in s: if description: raise InvalidRule( 'unexpected value: "%s", only one description allowed (inline description with ` = `)' % s @@ -222,7 +222,7 @@ def parse_description(s, value_type, description=None): else: value = s - if isinstance(value, str): + if isinstance(value, six.string_types): if value_type == "bytes": try: value = codecs.decode(value.replace(" ", ""), "hex") @@ -801,7 +801,7 @@ class RuleSet(object): rules_filtered = set([]) for rule in rules: for k, v in rule.meta.items(): - if isinstance(v, str) and tag in v: + if isinstance(v, six.string_types) and tag in v: logger.debug('using rule "%s" and dependencies, found tag in meta.%s: %s', rule.name, k, v) rules_filtered.update(set(capa.rules.get_rules_and_dependencies(rules, rule.name))) break From 959b66b26a368a08cafb2ecb879d212cf203d08c Mon Sep 17 00:00:00 2001 From: William Ballenthin Date: Mon, 6 Jul 2020 16:50:47 -0600 Subject: [PATCH 3/5] rules: use rpartition instead of split(..., 1) to better express intent --- capa/rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capa/rules.py b/capa/rules.py index 6347f754..4ca42553 100644 --- a/capa/rules.py +++ b/capa/rules.py @@ -216,7 +216,7 @@ def parse_description(s, value_type, description=None): raise InvalidRule( 'unexpected value: "%s", only one description allowed (inline description with ` = `)' % s ) - value, description = s.split(" = ", 1) + value, _, description = s.rpartition(" = ") if description == "": raise InvalidRule('unexpected value: "%s", description cannot be empty' % s) else: From ca175f02c7a80125fd7ae9b2371a3ec61ed9542c Mon Sep 17 00:00:00 2001 From: William Ballenthin Date: Mon, 6 Jul 2020 16:54:40 -0600 Subject: [PATCH 4/5] rules: factor out DESCRIPTION_SEPARATOR into a constant closes #87 --- capa/render/vverbose.py | 2 +- capa/rules.py | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/capa/render/vverbose.py b/capa/render/vverbose.py index df204745..7a703e96 100644 --- a/capa/render/vverbose.py +++ b/capa/render/vverbose.py @@ -82,7 +82,7 @@ def render_feature(ostream, match, feature, indent=0): ostream.write(rutils.bold2(feature[feature["type"]])) if "description" in feature: - ostream.write(" = ") + ostream.write(capa.rules.DESCRIPTION_SEPARATOR) ostream.write(feature["description"]) render_locations(ostream, match) diff --git a/capa/rules.py b/capa/rules.py index 4ca42553..305c4a95 100644 --- a/capa/rules.py +++ b/capa/rules.py @@ -207,16 +207,23 @@ def parse_feature(key): raise InvalidRule("unexpected statement: %s" % key) +# this is the separator between a feature value and its description +# when using the inline description syntax, like: +# +# number: 42 = ENUM_FAVORITE_NUMBER +DESCRIPTION_SEPARATOR = " = " + + def parse_description(s, value_type, description=None): """ s can be an int or a string """ - if value_type != "string" and isinstance(s, six.string_types) and " = " in s: + if value_type != "string" and isinstance(s, six.string_types) and DESCRIPTION_SEPARATOR in s: if description: raise InvalidRule( - 'unexpected value: "%s", only one description allowed (inline description with ` = `)' % s + 'unexpected value: "%s", only one description allowed (inline description with `%s`)' % (s, DESCRIPTION_SEPARATOR) ) - value, _, description = s.rpartition(" = ") + value, _, description = s.rpartition(DESCRIPTION_SEPARATOR) if description == "": raise InvalidRule('unexpected value: "%s", description cannot be empty' % s) else: From 248c27c9e885d5ecea14c7df20786c22a059a6ea Mon Sep 17 00:00:00 2001 From: William Ballenthin Date: Mon, 6 Jul 2020 16:54:52 -0600 Subject: [PATCH 5/5] pep8 --- capa/rules.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/capa/rules.py b/capa/rules.py index 305c4a95..09508df2 100644 --- a/capa/rules.py +++ b/capa/rules.py @@ -221,7 +221,8 @@ def parse_description(s, value_type, description=None): if value_type != "string" and isinstance(s, six.string_types) and DESCRIPTION_SEPARATOR in s: if description: raise InvalidRule( - 'unexpected value: "%s", only one description allowed (inline description with `%s`)' % (s, DESCRIPTION_SEPARATOR) + 'unexpected value: "%s", only one description allowed (inline description with `%s`)' + % (s, DESCRIPTION_SEPARATOR) ) value, _, description = s.rpartition(DESCRIPTION_SEPARATOR) if description == "":