render: add vverbose mode impl

This commit is contained in:
William Ballenthin
2020-06-28 01:26:18 -06:00
parent bee29e4d3d
commit cd8446b7cd

104
capa/render/vverbose.py Normal file
View File

@@ -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()