From 5f598e8a084a4cf19bff996476badc8366e214f6 Mon Sep 17 00:00:00 2001 From: William Ballenthin Date: Sun, 28 Jun 2020 11:30:23 -0600 Subject: [PATCH] render: learn to render Range/Count statements --- capa/render/__init__.py | 1 + capa/render/vverbose.py | 27 +++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/capa/render/__init__.py b/capa/render/__init__.py index 773984c2..51793115 100644 --- a/capa/render/__init__.py +++ b/capa/render/__init__.py @@ -41,6 +41,7 @@ def convert_statement_to_result_document(statement): 'type': 'range', 'min': statement.min, 'max': statement.max, + 'child': convert_feature_to_result_document(statement.child), } elif isinstance(statement, capa.engine.Regex): return { diff --git a/capa/render/vverbose.py b/capa/render/vverbose.py index 2f24f50a..db03c3d8 100644 --- a/capa/render/vverbose.py +++ b/capa/render/vverbose.py @@ -17,8 +17,31 @@ def render_statement(ostream, statement, indent=0): ostream.write(statement['count'] + ' or more') ostream.writeln(':') elif statement['type'] == 'range': - ostream.write('range(%d, %d)' % (statement['min'], statement['max'])) - ostream.writeln(':') + # `range` is a weird node, its almost a hybrid of statement+feature. + # it is a specific feature repeated multiple times. + # there's no additional logic in the feature part, just the existence of a feature. + # so, we have to inline some of the feature rendering here. + + child = statement['child'] + if child['type'] in ('string', 'bytes', 'api', 'mnemonic', 'basic block', 'export', 'import', 'section', 'match'): + feature = '%s(%s)' % (child['type'], rutils.bold2(child[child['type']])) + elif child['type'] in ('number', 'offset'): + feature = '%s(%s)' % (child['type'], rutils.bold2(rutils.hex(child[child['type']]))) + elif child['type'] == 'characteristic': + feature = 'characteristic(%s)' % (rutils.bold2(child['characteristic'][0])) + else: + raise RuntimeError('unexpected feature type: ' + str(child)) + + ostream.write('count(%s): ' % feature) + + if statement['max'] == statement['min']: + ostream.writeln('%d' % (statement['min'])) + elif statement['min'] == 0: + ostream.writeln('%d or fewer' % (statement['max'])) + elif statement['max'] == (1 << 64 - 1): + ostream.writeln('%d or more' % (statement['min'])) + else: + ostream.writeln('between %d and %d' % (statement['min'], statement['max'])) elif statement['type'] == 'subscope': ostream.write(statement['subscope']) ostream.writeln(':')