diff --git a/capa/features/capabilities/__init__.py b/capa/capabilities/__init__.py similarity index 100% rename from capa/features/capabilities/__init__.py rename to capa/capabilities/__init__.py diff --git a/capa/features/capabilities/common.py b/capa/capabilities/common.py similarity index 94% rename from capa/features/capabilities/common.py rename to capa/capabilities/common.py index b9252c9f..6098f789 100644 --- a/capa/features/capabilities/common.py +++ b/capa/capabilities/common.py @@ -43,8 +43,8 @@ def find_file_capabilities(ruleset: RuleSet, extractor: FeatureExtractor, functi def find_capabilities( ruleset: RuleSet, extractor: FeatureExtractor, disable_progress=None, **kwargs ) -> Tuple[MatchResults, Any]: - from capa.features.capabilities.static import find_static_capabilities - from capa.features.capabilities.dynamic import find_dynamic_capabilities + from capa.capabilities.static import find_static_capabilities + from capa.capabilities.dynamic import find_dynamic_capabilities if isinstance(extractor, StaticFeatureExtractor): # for the time being, extractors are either static or dynamic. diff --git a/capa/features/capabilities/dynamic.py b/capa/capabilities/dynamic.py similarity index 99% rename from capa/features/capabilities/dynamic.py rename to capa/capabilities/dynamic.py index acf50546..8c503cde 100644 --- a/capa/features/capabilities/dynamic.py +++ b/capa/capabilities/dynamic.py @@ -19,7 +19,7 @@ import capa.render.result_document as rdoc from capa.rules import Scope, RuleSet from capa.engine import FeatureSet, MatchResults from capa.helpers import redirecting_print_to_tqdm -from capa.features.capabilities.common import find_file_capabilities +from capa.capabilities.common import find_file_capabilities from capa.features.extractors.base_extractor import CallHandle, ThreadHandle, ProcessHandle, DynamicFeatureExtractor logger = logging.getLogger("capa") diff --git a/capa/features/capabilities/static.py b/capa/capabilities/static.py similarity index 99% rename from capa/features/capabilities/static.py rename to capa/capabilities/static.py index 785917c0..f072ed20 100644 --- a/capa/features/capabilities/static.py +++ b/capa/capabilities/static.py @@ -20,7 +20,7 @@ import capa.render.result_document as rdoc from capa.rules import Scope, RuleSet from capa.engine import FeatureSet, MatchResults from capa.helpers import redirecting_print_to_tqdm -from capa.features.capabilities.common import find_file_capabilities +from capa.capabilities.common import find_file_capabilities from capa.features.extractors.base_extractor import BBHandle, InsnHandle, FunctionHandle, StaticFeatureExtractor logger = logging.getLogger("capa") diff --git a/capa/ghidra/capa_ghidra.py b/capa/ghidra/capa_ghidra.py index 99beaffc..72eae7cf 100644 --- a/capa/ghidra/capa_ghidra.py +++ b/capa/ghidra/capa_ghidra.py @@ -19,6 +19,7 @@ import capa.main import capa.rules import capa.ghidra.helpers import capa.render.default +import capa.capabilities.common import capa.features.extractors.ghidra.extractor logger = logging.getLogger("capa_ghidra") @@ -73,7 +74,7 @@ def run_headless(): meta = capa.ghidra.helpers.collect_metadata([rules_path]) extractor = capa.features.extractors.ghidra.extractor.GhidraFeatureExtractor() - capabilities, counts = capa.main.find_capabilities(rules, extractor, False) + capabilities, counts = capa.capabilities.common.find_capabilities(rules, extractor, False) meta.analysis.feature_counts = counts["feature_counts"] meta.analysis.library_functions = counts["library_functions"] @@ -123,7 +124,7 @@ def run_ui(): meta = capa.ghidra.helpers.collect_metadata([rules_path]) extractor = capa.features.extractors.ghidra.extractor.GhidraFeatureExtractor() - capabilities, counts = capa.main.find_capabilities(rules, extractor, True) + capabilities, counts = capa.capabilities.common.find_capabilities(rules, extractor, True) meta.analysis.feature_counts = counts["feature_counts"] meta.analysis.library_functions = counts["library_functions"] diff --git a/capa/ida/plugin/form.py b/capa/ida/plugin/form.py index bc78045e..f0a4e13e 100644 --- a/capa/ida/plugin/form.py +++ b/capa/ida/plugin/form.py @@ -25,6 +25,7 @@ import capa.version import capa.ida.helpers import capa.render.json import capa.features.common +import capa.capabilities.common import capa.render.result_document import capa.features.extractors.ida.extractor from capa.rules import Rule @@ -768,7 +769,7 @@ class CapaExplorerForm(idaapi.PluginForm): try: meta = capa.ida.helpers.collect_metadata([Path(settings.user[CAPA_SETTINGS_RULE_PATH])]) - capabilities, counts = capa.main.find_capabilities( + capabilities, counts = capa.capabilities.common.find_capabilities( ruleset, self.feature_extractor, disable_progress=True ) diff --git a/capa/main.py b/capa/main.py index 262b6333..8a6a398a 100644 --- a/capa/main.py +++ b/capa/main.py @@ -84,7 +84,7 @@ from capa.features.common import ( FORMAT_RESULT, ) from capa.features.address import Address -from capa.features.capabilities.common import find_capabilities, find_file_capabilities +from capa.capabilities.common import find_capabilities, find_file_capabilities from capa.features.extractors.base_extractor import ( SampleHashes, FeatureExtractor, diff --git a/scripts/bulk-process.py b/scripts/bulk-process.py index 3e3cdfb2..8950b893 100644 --- a/scripts/bulk-process.py +++ b/scripts/bulk-process.py @@ -75,6 +75,7 @@ import capa import capa.main import capa.rules import capa.render.json +import capa.capabilities.common import capa.render.result_document as rd from capa.features.common import OS_AUTO @@ -136,7 +137,7 @@ def get_capa_results(args): "error": f"unexpected error: {e}", } - capabilities, counts = capa.main.find_capabilities(rules, extractor, disable_progress=True) + capabilities, counts = capa.capabilities.common.find_capabilities(rules, extractor, disable_progress=True) meta = capa.main.collect_metadata([], path, format, os_, [], extractor, counts) meta.analysis.layout = capa.main.compute_layout(rules, extractor, capabilities) diff --git a/scripts/capa_as_library.py b/scripts/capa_as_library.py index 7311107a..61157690 100644 --- a/scripts/capa_as_library.py +++ b/scripts/capa_as_library.py @@ -19,6 +19,7 @@ import capa.features import capa.render.json import capa.render.utils as rutils import capa.render.default +import capa.capabilities.common import capa.render.result_document as rd import capa.features.freeze.features as frzf from capa.features.common import OS_AUTO, FORMAT_AUTO @@ -175,7 +176,7 @@ def capa_details(rules_path: Path, file_path: Path, output_format="dictionary"): extractor = capa.main.get_extractor( file_path, FORMAT_AUTO, OS_AUTO, capa.main.BACKEND_VIV, [], False, disable_progress=True ) - capabilities, counts = capa.main.find_capabilities(rules, extractor, disable_progress=True) + capabilities, counts = capa.capabilities.common.find_capabilities(rules, extractor, disable_progress=True) # collect metadata (used only to make rendering more complete) meta = capa.main.collect_metadata([], file_path, FORMAT_AUTO, OS_AUTO, [rules_path], extractor, counts) diff --git a/scripts/lint.py b/scripts/lint.py index 065e694b..edcf9f56 100644 --- a/scripts/lint.py +++ b/scripts/lint.py @@ -41,6 +41,7 @@ import capa.rules import capa.engine import capa.helpers import capa.features.insn +import capa.capabilities.common from capa.rules import Rule, RuleSet from capa.features.common import OS_AUTO, String, Feature, Substring from capa.render.result_document import RuleMetadata @@ -366,7 +367,7 @@ def get_sample_capabilities(ctx: Context, path: Path) -> Set[str]: nice_path, format_, OS_AUTO, capa.main.BACKEND_VIV, DEFAULT_SIGNATURES, False, disable_progress=True ) - capabilities, _ = capa.main.find_capabilities(ctx.rules, extractor, disable_progress=True) + capabilities, _ = capa.capabilities.common.find_capabilities(ctx.rules, extractor, disable_progress=True) # mypy doesn't seem to be happy with the MatchResults type alias & set(...keys())? # so we ignore a few types here. capabilities = set(capabilities.keys()) # type: ignore diff --git a/scripts/profile-time.py b/scripts/profile-time.py index 9acd60ff..86590a80 100644 --- a/scripts/profile-time.py +++ b/scripts/profile-time.py @@ -54,6 +54,7 @@ import capa.helpers import capa.features import capa.features.common import capa.features.freeze +import capa.capabilities.common logger = logging.getLogger("capa.profile") @@ -114,7 +115,7 @@ def main(argv=None): def do_iteration(): capa.perf.reset() - capa.main.find_capabilities(rules, extractor, disable_progress=True) + capa.capabilities.common.find_capabilities(rules, extractor, disable_progress=True) pbar.update(1) samples = timeit.repeat(do_iteration, number=args.number, repeat=args.repeat) diff --git a/scripts/show-capabilities-by-function.py b/scripts/show-capabilities-by-function.py index 509c3a84..e987b680 100644 --- a/scripts/show-capabilities-by-function.py +++ b/scripts/show-capabilities-by-function.py @@ -74,6 +74,7 @@ import capa.exceptions import capa.render.utils as rutils import capa.render.verbose import capa.features.freeze +import capa.capabilities.common import capa.render.result_document as rd from capa.helpers import get_file_taste from capa.features.common import FORMAT_AUTO @@ -186,7 +187,7 @@ def main(argv=None): capa.helpers.log_unsupported_runtime_error() return -1 - capabilities, counts = capa.main.find_capabilities(rules, extractor) + capabilities, counts = capa.capabilities.common.find_capabilities(rules, extractor) meta = capa.main.collect_metadata(argv, args.sample, format_, args.os, args.rules, extractor, counts) meta.analysis.layout = capa.main.compute_layout(rules, extractor, capabilities) diff --git a/tests/test_capabilities.py b/tests/test_capabilities.py index ef86d102..fe02985c 100644 --- a/tests/test_capabilities.py +++ b/tests/test_capabilities.py @@ -8,7 +8,7 @@ # See the License for the specific language governing permissions and limitations under the License. import textwrap -import capa.features.capabilities.common +import capa.capabilities.common def test_match_across_scopes_file_function(z9324d_extractor): @@ -74,7 +74,7 @@ def test_match_across_scopes_file_function(z9324d_extractor): ), ] ) - capabilities, meta = capa.features.capabilities.common.find_capabilities(rules, z9324d_extractor) + capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) assert "install service" in capabilities assert ".text section" in capabilities assert ".text section and install service" in capabilities @@ -142,7 +142,7 @@ def test_match_across_scopes(z9324d_extractor): ), ] ) - capabilities, meta = capa.features.capabilities.common.find_capabilities(rules, z9324d_extractor) + capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) assert "tight loop" in capabilities assert "kill thread loop" in capabilities assert "kill thread program" in capabilities @@ -170,7 +170,7 @@ def test_subscope_bb_rules(z9324d_extractor): ] ) # tight loop at 0x403685 - capabilities, meta = capa.features.capabilities.common.find_capabilities(rules, z9324d_extractor) + capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) assert "test rule" in capabilities @@ -194,7 +194,7 @@ def test_byte_matching(z9324d_extractor): ) ] ) - capabilities, meta = capa.features.capabilities.common.find_capabilities(rules, z9324d_extractor) + capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) assert "byte match test" in capabilities @@ -219,7 +219,7 @@ def test_count_bb(z9324d_extractor): ) ] ) - capabilities, meta = capa.features.capabilities.common.find_capabilities(rules, z9324d_extractor) + capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) assert "count bb" in capabilities @@ -246,7 +246,7 @@ def test_instruction_scope(z9324d_extractor): ) ] ) - capabilities, meta = capa.features.capabilities.common.find_capabilities(rules, z9324d_extractor) + capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) assert "push 1000" in capabilities assert 0x4071A4 in {result[0] for result in capabilities["push 1000"]} @@ -278,6 +278,6 @@ def test_instruction_subscope(z9324d_extractor): ) ] ) - capabilities, meta = capa.features.capabilities.common.find_capabilities(rules, z9324d_extractor) + capabilities, meta = capa.capabilities.common.find_capabilities(rules, z9324d_extractor) assert "push 1000 on i386" in capabilities assert 0x406F60 in {result[0] for result in capabilities["push 1000 on i386"]}