mirror of
https://github.com/mandiant/capa.git
synced 2025-12-31 15:06:17 -08:00
render: add vverbose mode impl
This commit is contained in:
104
capa/render/vverbose.py
Normal file
104
capa/render/vverbose.py
Normal 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()
|
||||
Reference in New Issue
Block a user