From 9d5ecadf958912918d7e3bbaa43c580b4af0188b Mon Sep 17 00:00:00 2001 From: Michael Hunhoff Date: Thu, 25 Jun 2020 13:22:07 -0600 Subject: [PATCH 1/6] adding support to display appropriate scope name in vverbose mode --- capa/main.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/capa/main.py b/capa/main.py index 662eebd0..33d91aac 100644 --- a/capa/main.py +++ b/capa/main.py @@ -374,7 +374,7 @@ def render_result(res, indent=''): render_result(children, indent=indent + ' ') -def render_capabilities_vverbose(results): +def render_capabilities_vverbose(ruleset, results): ''' print the matching rules, the functions in which they matched, and the logic tree with annotated matching features. @@ -392,8 +392,13 @@ def render_capabilities_vverbose(results): ''' for rule, ress in results.items(): print('rule %s:' % (rule)) - for (fva, res) in sorted(ress, key=lambda p: p[0]): - print(' - function 0x%x:' % (fva)) + for (va, res) in sorted(ress, key=lambda p: p[0]): + rule_scope = ruleset.rules[rule].scope + if rule_scope == capa.rules.FILE_SCOPE: + # does not make sense to display va at file scope + print(' - %s:' % rule_scope) + else: + print(' - %s 0x%x:' % (rule_scope, va)) render_result(res, indent=' ') @@ -722,7 +727,7 @@ def main(argv=None): logger.warning('-' * 80) if args.vverbose: - render_capabilities_vverbose(capabilities) + render_capabilities_vverbose(rules, capabilities) elif args.verbose: render_capabilities_verbose(capabilities) else: From e1f924ffd10172035a160ef9805ffa7829d792ab Mon Sep 17 00:00:00 2001 From: Michael Hunhoff Date: Thu, 25 Jun 2020 13:39:05 -0600 Subject: [PATCH 2/6] tweak verbose display to remove empty va addresses for file scope --- capa/main.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/capa/main.py b/capa/main.py index 093ebe6d..c9f21126 100644 --- a/capa/main.py +++ b/capa/main.py @@ -305,7 +305,7 @@ def render_capabilities_concise(results): print(rule) -def render_capabilities_verbose(results): +def render_capabilities_verbose(ruleset, results): ''' print the matching rules, and the functions in which they matched. @@ -321,6 +321,11 @@ def render_capabilities_verbose(results): - 0x40105d ''' for rule, ress in results.items(): + rule_scope = ruleset.rules[rule].scope + if rule_scope == capa.rules.FILE_SCOPE: + # only display rule name at file scope + print('%s' % rule) + continue print('%s:' % (rule)) seen = set([]) for (fva, _) in sorted(ress, key=lambda p: p[0]): @@ -725,7 +730,7 @@ def main(argv=None): if args.vverbose: render_capabilities_vverbose(rules, capabilities) elif args.verbose: - render_capabilities_verbose(capabilities) + render_capabilities_verbose(rules, capabilities) else: render_capabilities_default(rules, capabilities) From dcd66f41fab091fc980a05fb7116bbb683027b81 Mon Sep 17 00:00:00 2001 From: Moritz Raabe Date: Fri, 26 Jun 2020 16:19:07 +0200 Subject: [PATCH 3/6] do not display subscope rules in any mode --- capa/features/extractors/viv/insn.py | 2 +- capa/main.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/capa/features/extractors/viv/insn.py b/capa/features/extractors/viv/insn.py index 142c0f3f..08cc431a 100644 --- a/capa/features/extractors/viv/insn.py +++ b/capa/features/extractors/viv/insn.py @@ -298,7 +298,7 @@ def extract_insn_peb_access_characteristic_features(f, bb, insn): ''' parse peb access from the given function. fs:[0x30] on x86, gs:[0x60] on x64 ''' - # TODO extract x64 + # TODO handle where fs/gs are loaded into a register or onto the stack and used later if insn.mnem not in ['push', 'mov']: return diff --git a/capa/main.py b/capa/main.py index c9f21126..5600caab 100644 --- a/capa/main.py +++ b/capa/main.py @@ -321,6 +321,10 @@ def render_capabilities_verbose(ruleset, results): - 0x40105d ''' for rule, ress in results.items(): + if ruleset.rules[rule].meta.get('capa/subscope-rule', False): + # don't display subscope rules + continue + rule_scope = ruleset.rules[rule].scope if rule_scope == capa.rules.FILE_SCOPE: # only display rule name at file scope @@ -396,6 +400,10 @@ def render_capabilities_vverbose(ruleset, results): - virtual address: 0x4010c8 ''' for rule, ress in results.items(): + if ruleset.rules[rule].meta.get('capa/subscope-rule', False): + # don't display subscope rules + continue + print('rule %s:' % (rule)) for (va, res) in sorted(ress, key=lambda p: p[0]): rule_scope = ruleset.rules[rule].scope @@ -686,6 +694,9 @@ def main(argv=None): if args.tag: rules = rules.filter_rules_by_meta(args.tag) logger.info('selected %s rules', len(rules)) + for i, r in enumerate(rules.rules, 1): + # TODO don't display subscope rules? + logger.debug(' %d. %s', i, r) except (IOError, capa.rules.InvalidRule, capa.rules.InvalidRuleSet) as e: logger.error('%s', str(e)) return -1 From 540f68c5c73ba1e8edb69aa6b9a1b08582130013 Mon Sep 17 00:00:00 2001 From: Michael Hunhoff Date: Fri, 26 Jun 2020 11:28:11 -0600 Subject: [PATCH 4/6] tree view default to sorted asc, trim regex matches --- capa/ida/explorer/model.py | 2 ++ capa/ida/ida_capa_explorer.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/capa/ida/explorer/model.py b/capa/ida/explorer/model.py index ba472644..69e91272 100644 --- a/capa/ida/explorer/model.py +++ b/capa/ida/explorer/model.py @@ -383,6 +383,8 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel): if isinstance(feature, string_view): # TODO: move string collection to item constructor if isinstance(feature, capa.engine.Regex): + # rstrip "matched="")" because data already displayed in interface + name = name.split(',')[0] + ')' return CapaExplorerStringViewItem(parent, name, ea, feature.match) if isinstance(feature, capa.features.Characteristic): diff --git a/capa/ida/ida_capa_explorer.py b/capa/ida/ida_capa_explorer.py index c322a274..fb2759ec 100644 --- a/capa/ida/ida_capa_explorer.py +++ b/capa/ida/ida_capa_explorer.py @@ -351,6 +351,8 @@ class CapaExplorerForm(idaapi.PluginForm): self._model_data.render_capa_results(rules, capabilities) self._render_capa_summary(rules, capabilities) + self._view_tree.sortByColumn(CapaExplorerDataModel.COLUMN_INDEX_RULE_INFORMATION, Qt.AscendingOrder) + logger.info('render views completed.') def _render_capa_summary(self, ruleset, results): From 009368f2786d5be3d56dc9be43250a6ba1229367 Mon Sep 17 00:00:00 2001 From: Michael Hunhoff Date: Fri, 26 Jun 2020 14:38:14 -0600 Subject: [PATCH 5/6] removing empty structural expressions from tree view --- capa/ida/explorer/model.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/capa/ida/explorer/model.py b/capa/ida/explorer/model.py index 69e91272..dd062261 100644 --- a/capa/ida/explorer/model.py +++ b/capa/ida/explorer/model.py @@ -324,14 +324,9 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel): parent2 = CapaExplorerDefaultItem(parent, '%d or more' % result.statement.count) elif not isinstance(result.statement, (capa.features.Feature, capa.engine.Range, capa.engine.Regex)): # when rending a structural node (and/or/not) then we only care about the node name. - ''' - succs = list(filter(lambda c: bool(c), result.children)) - if len(succs) == 1: - # skip structural node with single succeeding child - parent2 = parent - else: - parent2 = CapaExplorerDefaultItem(parent, result.statement.name.lower()) - ''' + if not len(list(filter(lambda c: bool(c), result.children))): + # ignore empty structural expressions (e.g. not) + return parent2 = CapaExplorerDefaultItem(parent, result.statement.name.lower()) else: # but when rendering a Feature, want to see any arguments to it From 65a4960ce9d25672878f8b6a30136a8da0733ce6 Mon Sep 17 00:00:00 2001 From: Michael Hunhoff Date: Fri, 26 Jun 2020 14:44:52 -0600 Subject: [PATCH 6/6] code changes --- capa/ida/explorer/model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/capa/ida/explorer/model.py b/capa/ida/explorer/model.py index dd062261..06b64e86 100644 --- a/capa/ida/explorer/model.py +++ b/capa/ida/explorer/model.py @@ -324,8 +324,8 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel): parent2 = CapaExplorerDefaultItem(parent, '%d or more' % result.statement.count) elif not isinstance(result.statement, (capa.features.Feature, capa.engine.Range, capa.engine.Regex)): # when rending a structural node (and/or/not) then we only care about the node name. - if not len(list(filter(lambda c: bool(c), result.children))): - # ignore empty structural expressions (e.g. not) + if not list(filter(lambda c: bool(c), result.children)): + # ignore structural expressions that do not have any successful children (e.g. not) return parent2 = CapaExplorerDefaultItem(parent, result.statement.name.lower()) else: