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/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): 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 f81cfac3..09508df2 100644 --- a/capa/rules.py +++ b/capa/rules.py @@ -207,22 +207,30 @@ 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, str) 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.split(" = ", 1) + value, _, description = s.rpartition(DESCRIPTION_SEPARATOR) if description == "": raise InvalidRule('unexpected value: "%s", description cannot be empty' % s) 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 +809,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