From cd8446b7cd2e2eeec8014dae394909c709bb1ead Mon Sep 17 00:00:00 2001 From: William Ballenthin Date: Sun, 28 Jun 2020 01:26:18 -0600 Subject: [PATCH] render: add vverbose mode impl --- capa/render/vverbose.py | 104 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 capa/render/vverbose.py diff --git a/capa/render/vverbose.py b/capa/render/vverbose.py new file mode 100644 index 00000000..7af04e42 --- /dev/null +++ b/capa/render/vverbose.py @@ -0,0 +1,104 @@ +import tabulate + +import capa.rules +import capa.render.utils as rutils + + +def render_statement(ostream, statement, indent=0): + ostream.write(' ' * indent) + if statement['type'] in ('and', 'or', 'optional'): + ostream.write(statement['type']) + ostream.writeln(':') + elif statement['type'] == 'not': + # we won't have successful results for the children of a not + # so display a placeholder `...` + ostream.writeln('not: ...') + elif statement['type'] == 'some': + ostream.write(statement['count'] + ' or more') + ostream.writeln(':') + elif statement['type'] == 'range': + ostream.write('range(%d, %d)' % (statement['min'], statement['max'])) + ostream.writeln(':') + elif statement['type'] == 'subscope': + ostream.write(statement['subscope']) + ostream.writeln(':') + elif statement['type'] == 'regex': + ostream.writeln('string: /%s/' % (statement['pattern'])) + else: + raise RuntimeError("unexpected match statement type: " + str(statement)) + + +def render_feature(ostream, match, feature, indent=0): + ostream.write(' ' * indent) + + if feature['type'] in ('string', 'api', 'number', 'offset', 'mnemonic', 'basic block', 'export', 'import', 'section', 'match'): + ostream.write(feature['type']) + ostream.write(': ') + ostream.write(rutils.bold2(feature[feature['type']])) + elif feature['type'] == 'characteristic': + ostream.write('characteristic(%s): %s' % (rutils.bold2(feature['characteristic'][0]), feature['characteristic'][1])) + else: + raise RuntimeError('unexpected feature type: ' + str(feature)) + + if len(match['locations']) == 1: + ostream.write(' @ ') + ostream.write(hex(list(match['locations'])[0])) + elif len(match['locations']) > 1: + ostream.write(' @ ') + ostream.write(', '.join(map(hex, sorted(match['locations'])))) + + ostream.write('\n') + + +def render_node(ostream, match, node, indent=0): + if node['type'] == 'statement': + render_statement(ostream, node['statement'], indent=indent) + elif node['type'] == 'feature': + render_feature(ostream, match, node['feature'], indent=indent) + else: + raise RuntimeError('unexpected node type: ' + str(node)) + + +def render_match(ostream, match, indent=0): + if not match['success']: + return + + if match['node'].get('statement', {}).get('type') == 'optional' and not any(map(lambda m: m['success'], match['children'])): + return + + render_node(ostream, match, match['node'], indent=indent) + + for child in match['children']: + render_match(ostream, child, indent=indent+1) + + +def render_vverbose(doc): + ostream = rutils.StringIO() + + for rule in rutils.capability_rules(doc): + ostream.writeln(rutils.bold(rule['meta']['name'])) + + rows = [] + for key in capa.rules.META_KEYS: + if key == 'name' or key not in rule['meta']: + continue + + v = rule['meta'][key] + if isinstance(v, list) and len(v) == 1: + v = v[0] + rows.append((key, v)) + + ostream.writeln(tabulate.tabulate(rows, tablefmt='plain')) + + if rule['meta']['scope'] == capa.rules.FILE_SCOPE: + render_match(ostream, match, indent=0) + else: + for location, match in doc[rule['meta']['name']]['matches'].items(): + ostream.write(rule['meta']['scope']) + ostream.write(' @ ') + ostream.writeln(hex(location)) + render_match(ostream, match, indent=1) + + ostream.write('\n') + + return ostream.getvalue() \ No newline at end of file