diff --git a/capa/features/extractors/__init__.py b/capa/features/extractors/__init__.py index 98ecf80b..2cdbd4af 100644 --- a/capa/features/extractors/__init__.py +++ b/capa/features/extractors/__init__.py @@ -196,7 +196,7 @@ class NullFeatureExtractor(FeatureExtractor): 'functions': { 0x401000: { 'features': [ - (0x401000, capa.features.Characteristic('switch')), + (0x401000, capa.features.Characteristic('nzxor')), ], 'basic blocks': { 0x401000: { diff --git a/capa/features/extractors/ida/function.py b/capa/features/extractors/ida/function.py index eedd31e4..67234b02 100644 --- a/capa/features/extractors/ida/function.py +++ b/capa/features/extractors/ida/function.py @@ -14,16 +14,6 @@ from capa.features import Characteristic from capa.features.extractors import loops -def extract_function_switch(f): - """ extract switch indicators from a function - - arg: - f (IDA func_t) - """ - if capa.features.extractors.ida.helpers.is_function_switch_statement(f): - yield Characteristic("switch"), f.start_ea - - def extract_function_calls_to(f): """ extract callers to a function @@ -72,7 +62,7 @@ def extract_features(f): yield feature, ea -FUNCTION_HANDLERS = (extract_function_calls_to, extract_function_switch, extract_function_loop, extract_recursive_call) +FUNCTION_HANDLERS = (extract_function_calls_to, extract_function_loop, extract_recursive_call) def main(): diff --git a/capa/features/extractors/ida/helpers.py b/capa/features/extractors/ida/helpers.py index 8466c1bb..1e364e7c 100644 --- a/capa/features/extractors/ida/helpers.py +++ b/capa/features/extractors/ida/helpers.py @@ -300,22 +300,6 @@ def is_function_recursive(f): return False -def is_function_switch_statement(f): - """ check a function for switch statement indicators - - adapted from: - https://reverseengineering.stackexchange.com/questions/17548/calc-switch-cases-in-idapython-cant-iterate-over-results?rq=1 - - arg: - f (IDA func_t) - """ - for (start, end) in idautils.Chunks(f.start_ea): - for head in idautils.Heads(start, end): - if idaapi.get_switch_info(head): - return True - return False - - def is_basic_block_tight_loop(bb): """ check basic block loops to self diff --git a/capa/features/extractors/viv/function.py b/capa/features/extractors/viv/function.py index 323dbd1d..bb9ef605 100644 --- a/capa/features/extractors/viv/function.py +++ b/capa/features/extractors/viv/function.py @@ -25,45 +25,6 @@ def interface_extract_function_XXX(f): yield NotImplementedError("feature"), NotImplementedError("virtual address") -def get_switches(vw): - """ - caching accessor to vivisect workspace switch constructs. - """ - if "switches" in vw.metadata: - return vw.metadata["switches"] - else: - # addresses of switches in the program - switches = set() - - for case_va, _ in filter(lambda t: "case" in t[1], vw.getNames()): - # assume that the xref to a case location is a switch construct - for switch_va, _, _, _ in vw.getXrefsTo(case_va): - switches.add(switch_va) - - vw.metadata["switches"] = switches - return switches - - -def get_functions_with_switch(vw): - if "functions_with_switch" in vw.metadata: - return vw.metadata["functions_with_switch"] - else: - functions = set() - for switch in get_switches(vw): - functions.add(vw.getFunction(switch)) - vw.metadata["functions_with_switch"] = functions - return functions - - -def extract_function_switch(f): - """ - parse if a function contains a switch statement based on location names - method can be optimized - """ - if f.va in get_functions_with_switch(f.vw): - yield Characteristic("switch"), f.va - - def extract_function_calls_to(f): for src, _, _, _ in f.vw.getXrefsTo(f.va, rtype=vivisect.const.REF_CODE): yield Characteristic("calls to"), src @@ -106,4 +67,4 @@ def extract_features(f): yield feature, va -FUNCTION_HANDLERS = (extract_function_switch, extract_function_calls_to, extract_function_loop) +FUNCTION_HANDLERS = (extract_function_calls_to, extract_function_loop) diff --git a/capa/ida/explorer/model.py b/capa/ida/explorer/model.py index 4aa787c9..3949e9bb 100644 --- a/capa/ida/explorer/model.py +++ b/capa/ida/explorer/model.py @@ -528,7 +528,7 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel): if feature[feature["type"]] in ("embedded pe",): return CapaExplorerByteViewItem(parent, display, location) - if feature[feature["type"]] in ("loop", "recursive call", "tight loop", "switch"): + if feature[feature["type"]] in ("loop", "recursive call", "tight loop"): return CapaExplorerFeatureItem(parent, display=display) # default to instruction view for all other characteristics diff --git a/capa/rules.py b/capa/rules.py index 52b0ba39..eb9cc318 100644 --- a/capa/rules.py +++ b/capa/rules.py @@ -69,7 +69,6 @@ SUPPORTED_FEATURES = { FUNCTION_SCOPE: { # plus basic block scope features, see below capa.features.basicblock.BasicBlock, - capa.features.Characteristic("switch"), capa.features.Characteristic("calls from"), capa.features.Characteristic("calls to"), capa.features.Characteristic("loop"), diff --git a/tests/test_freeze.py b/tests/test_freeze.py index e8a4e238..b0131388 100644 --- a/tests/test_freeze.py +++ b/tests/test_freeze.py @@ -23,7 +23,7 @@ EXTRACTOR = capa.features.extractors.NullFeatureExtractor( "file features": [(0x402345, capa.features.Characteristic("embedded pe")),], "functions": { 0x401000: { - "features": [(0x401000, capa.features.Characteristic("switch")),], + "features": [(0x401000, capa.features.Characteristic("indirect call")),], "basic blocks": { 0x401000: { "features": [(0x401000, capa.features.Characteristic("tight loop")),], diff --git a/tests/test_ida_features.py b/tests/test_ida_features.py index c1e3f163..f9459e9a 100644 --- a/tests/test_ida_features.py +++ b/tests/test_ida_features.py @@ -243,17 +243,6 @@ def test_stackstring_features(): assert capa.features.Characteristic("stack string") in features -@pytest.mark.skip(reason="IDA Pro tests must be run within IDA") -def test_switch_features(): - f = get_extractor().get_function(0x409411) - features = extract_function_features(f) - assert capa.features.Characteristic("switch") in features - - f = get_extractor().get_function(0x409393) - features = extract_function_features(f) - assert capa.features.Characteristic("switch") not in features - - @pytest.mark.skip(reason="IDA Pro tests must be run within IDA") def test_function_calls_to(): # this function is used in a function pointer diff --git a/tests/test_main.py b/tests/test_main.py index c0e767ea..6bcbd74f 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -73,7 +73,7 @@ def test_ruleset(): name: function rule scope: function features: - - characteristic: switch + - characteristic: tight loop """ ) ), diff --git a/tests/test_rules.py b/tests/test_rules.py index 6c70b7e1..b60a5503 100644 --- a/tests/test_rules.py +++ b/tests/test_rules.py @@ -267,7 +267,7 @@ def test_subscope_rules(): - function: - and: - characteristic: nzxor - - characteristic: switch + - characteristic: loop """ ) ) diff --git a/tests/test_viv_features.py b/tests/test_viv_features.py index 4e612bdf..8eab1249 100644 --- a/tests/test_viv_features.py +++ b/tests/test_viv_features.py @@ -272,14 +272,6 @@ def test_stackstring_features(mimikatz): assert capa.features.Characteristic("stack string") in features -def test_switch_features(mimikatz): - features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x409411)) - assert capa.features.Characteristic("switch") in features - - features = extract_function_features(viv_utils.Function(mimikatz.vw, 0x409393)) - assert capa.features.Characteristic("switch") not in features - - def test_recursive_call_feature(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41): features = extract_function_features( viv_utils.Function(sample_39c05b15e9834ac93f206bc114d0a00c357c888db567ba8f5345da0529cbed41.vw, 0x10003100)