From d061e0c591f3b0109e4546c62fbea91bdeaecd13 Mon Sep 17 00:00:00 2001 From: aaronatp <58194911+aaronatp@users.noreply.github.com> Date: Tue, 7 Nov 2023 17:49:59 -0800 Subject: [PATCH 01/24] Update main.py --- capa/main.py | 145 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 89 insertions(+), 56 deletions(-) diff --git a/capa/main.py b/capa/main.py index ae8421560..ff7323155 100644 --- a/capa/main.py +++ b/capa/main.py @@ -517,6 +517,89 @@ def get_workspace(path: Path, format_: str, sigpaths: List[Path]): return vw +def check_supported_format(path, os_): + if not is_supported_format(path): + raise UnsupportedFormatError() + + if not is_supported_arch(path): + raise UnsupportedArchError() + + if os_ == OS_AUTO and not is_supported_os(path): + raise UnsupportedOSError() + + + +def add_binja_to_path(): + from capa.features.extractors.binja.find_binja_api import find_binja_path + + bn_api = find_binja_path() + if bn_api.exists(): + sys.path.append(str(bn_api)) + + + +def import_binja(): + # When we are running as a standalone executable, we cannot directly import binaryninja + # We need to fist find the binja API installation path and add it into sys.path + if is_running_standalone(): + add_binja_to_path() + + try: + import binaryninja + from binaryninja import BinaryView + except ImportError: + raise RuntimeError( + "Cannot import binaryninja module. Please install the Binary Ninja Python API first: " + + "https://docs.binary.ninja/dev/batch.html#install-the-api)." + ) + + + +def handle_binja_backend(path): + import capa.features.extractors.binja.extractor + + import_binja() + + with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): + bv: BinaryView = binaryninja.load(str(path)) + if bv is None: + raise RuntimeError(f"Binary Ninja cannot open file {path}") + + return capa.features.extractors.binja.extractor.BinjaFeatureExtractor(bv) + + + +def handle_viv_backend(path, format_, sigpaths, os_): + import capa.features.extractors.viv.extractor + + with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): + vw = get_workspace(path, format_, sigpaths) + + if should_save_workspace: + logger.debug("saving workspace") + try: + vw.saveWorkspace() + except IOError: + # see #168 for discussion around how to handle non-writable directories + logger.info("source directory is not writable, won't save intermediate workspace") + else: + logger.debug("CAPA_SAVE_WORKSPACE unset, not saving workspace") + + return capa.features.extractors.viv.extractor.VivisectFeatureExtractor(vw, path, os_) + + + +def handle_pefile_backend(path): + import capa.features.extractors.pefile + return capa.features.extractors.pefile.PefileFeatureExtractor(path) + + +def handle_dotnet_format(format): + import capa.features.extractors.dnfile.extractor + return capa.features.extractors.dnfile.extractor.DnfileFeatureExtractor(path) + + + def get_extractor( path: Path, format_: str, @@ -533,73 +616,23 @@ def get_extractor( UnsupportedOSError """ if format_ not in (FORMAT_SC32, FORMAT_SC64): - if not is_supported_format(path): - raise UnsupportedFormatError() - - if not is_supported_arch(path): - raise UnsupportedArchError() - - if os_ == OS_AUTO and not is_supported_os(path): - raise UnsupportedOSError() + check_supported_format(path, os_) if format_ == FORMAT_DOTNET: - import capa.features.extractors.dnfile.extractor - - return capa.features.extractors.dnfile.extractor.DnfileFeatureExtractor(path) + return handle_dotnet_format(format) elif backend == BACKEND_BINJA: - from capa.features.extractors.binja.find_binja_api import find_binja_path - - # When we are running as a standalone executable, we cannot directly import binaryninja - # We need to fist find the binja API installation path and add it into sys.path - if is_running_standalone(): - bn_api = find_binja_path() - if bn_api.exists(): - sys.path.append(str(bn_api)) - - try: - import binaryninja - from binaryninja import BinaryView - except ImportError: - raise RuntimeError( - "Cannot import binaryninja module. Please install the Binary Ninja Python API first: " - + "https://docs.binary.ninja/dev/batch.html#install-the-api)." - ) - - import capa.features.extractors.binja.extractor - - with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): - bv: BinaryView = binaryninja.load(str(path)) - if bv is None: - raise RuntimeError(f"Binary Ninja cannot open file {path}") - - return capa.features.extractors.binja.extractor.BinjaFeatureExtractor(bv) + return handle_binja_backend(path) elif backend == BACKEND_PEFILE: - import capa.features.extractors.pefile - - return capa.features.extractors.pefile.PefileFeatureExtractor(path) + return handle_pefile_backend(path) elif backend == BACKEND_VIV: - import capa.features.extractors.viv.extractor - - with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): - vw = get_workspace(path, format_, sigpaths) - - if should_save_workspace: - logger.debug("saving workspace") - try: - vw.saveWorkspace() - except IOError: - # see #168 for discussion around how to handle non-writable directories - logger.info("source directory is not writable, won't save intermediate workspace") - else: - logger.debug("CAPA_SAVE_WORKSPACE unset, not saving workspace") - - return capa.features.extractors.viv.extractor.VivisectFeatureExtractor(vw, path, os_) + return handle_viv_backend(path, format, sigpaths, os_) else: raise ValueError("unexpected backend: " + backend) + def get_file_extractors(sample: Path, format_: str) -> List[FeatureExtractor]: From d46fa26cedccfe312b3196f4c61c02440a747f7e Mon Sep 17 00:00:00 2001 From: aaronatp <58194911+aaronatp@users.noreply.github.com> Date: Tue, 7 Nov 2023 21:48:57 -0800 Subject: [PATCH 02/24] Incremental PR improvements --- capa/main.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/capa/main.py b/capa/main.py index ff7323155..74f5145c5 100644 --- a/capa/main.py +++ b/capa/main.py @@ -517,7 +517,7 @@ def get_workspace(path: Path, format_: str, sigpaths: List[Path]): return vw -def check_supported_format(path, os_): +def check_supported_format(path: Path, os_: str): if not is_supported_format(path): raise UnsupportedFormatError() @@ -528,7 +528,6 @@ def check_supported_format(path, os_): raise UnsupportedOSError() - def add_binja_to_path(): from capa.features.extractors.binja.find_binja_api import find_binja_path @@ -537,7 +536,6 @@ def add_binja_to_path(): sys.path.append(str(bn_api)) - def import_binja(): # When we are running as a standalone executable, we cannot directly import binaryninja # We need to fist find the binja API installation path and add it into sys.path @@ -554,8 +552,7 @@ def import_binja(): ) - -def handle_binja_backend(path): +def handle_binja_backend(path: Path, disable_progress: bool) -> BinjaFeatureExtractor: import capa.features.extractors.binja.extractor import_binja() @@ -568,8 +565,7 @@ def handle_binja_backend(path): return capa.features.extractors.binja.extractor.BinjaFeatureExtractor(bv) - -def handle_viv_backend(path, format_, sigpaths, os_): +def handle_viv_backend(path: Path, format_: str, sigpaths: List[Path], os_: str, disable_progress: bool) -> VivisectFeatureExtractor: import capa.features.extractors.viv.extractor with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): @@ -588,18 +584,16 @@ def handle_viv_backend(path, format_, sigpaths, os_): return capa.features.extractors.viv.extractor.VivisectFeatureExtractor(vw, path, os_) - -def handle_pefile_backend(path): +def handle_pefile_backend(path: Path) -> PefileFeatureExtractor: import capa.features.extractors.pefile return capa.features.extractors.pefile.PefileFeatureExtractor(path) -def handle_dotnet_format(format): +def handle_dotnet_format(format_: str) -> DnfileFeatureExtractor: import capa.features.extractors.dnfile.extractor return capa.features.extractors.dnfile.extractor.DnfileFeatureExtractor(path) - def get_extractor( path: Path, format_: str, @@ -619,22 +613,21 @@ def get_extractor( check_supported_format(path, os_) if format_ == FORMAT_DOTNET: - return handle_dotnet_format(format) + return handle_dotnet_format(format_) elif backend == BACKEND_BINJA: - return handle_binja_backend(path) + return handle_binja_backend(path, disable_progress) elif backend == BACKEND_PEFILE: return handle_pefile_backend(path) elif backend == BACKEND_VIV: - return handle_viv_backend(path, format, sigpaths, os_) + return handle_viv_backend(path, format, sigpaths, os_, disable_progress) else: raise ValueError("unexpected backend: " + backend) - def get_file_extractors(sample: Path, format_: str) -> List[FeatureExtractor]: file_extractors: List[FeatureExtractor] = [] From 222cd6c4e9ed46575c535d3acc41821bcafc4564 Mon Sep 17 00:00:00 2001 From: aaronatp <58194911+aaronatp@users.noreply.github.com> Date: Tue, 7 Nov 2023 22:11:37 -0800 Subject: [PATCH 03/24] Update main.py --- capa/main.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/capa/main.py b/capa/main.py index 74f5145c5..714713597 100644 --- a/capa/main.py +++ b/capa/main.py @@ -552,7 +552,7 @@ def import_binja(): ) -def handle_binja_backend(path: Path, disable_progress: bool) -> BinjaFeatureExtractor: +def handle_binja_backend(path: Path, disable_progress: bool) -> FeatureExtractor: import capa.features.extractors.binja.extractor import_binja() @@ -565,7 +565,8 @@ def handle_binja_backend(path: Path, disable_progress: bool) -> BinjaFeatureExtr return capa.features.extractors.binja.extractor.BinjaFeatureExtractor(bv) -def handle_viv_backend(path: Path, format_: str, sigpaths: List[Path], os_: str, disable_progress: bool) -> VivisectFeatureExtractor: +def handle_viv_backend(path: Path, format_: str, sigpaths: List[Path], should_save_workspace: bool, \ + os_: str, disable_progress: bool) -> FeatureExtractor: import capa.features.extractors.viv.extractor with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): @@ -584,12 +585,12 @@ def handle_viv_backend(path: Path, format_: str, sigpaths: List[Path], os_: str, return capa.features.extractors.viv.extractor.VivisectFeatureExtractor(vw, path, os_) -def handle_pefile_backend(path: Path) -> PefileFeatureExtractor: +def handle_pefile_backend(path: Path) -> FeatureExtractor: import capa.features.extractors.pefile return capa.features.extractors.pefile.PefileFeatureExtractor(path) -def handle_dotnet_format(format_: str) -> DnfileFeatureExtractor: +def handle_dotnet_format(path: Path) -> FeatureExtractor: import capa.features.extractors.dnfile.extractor return capa.features.extractors.dnfile.extractor.DnfileFeatureExtractor(path) From d6498974865fbbe4fe28659a649c47eebf15846b Mon Sep 17 00:00:00 2001 From: aaronatp <58194911+aaronatp@users.noreply.github.com> Date: Tue, 7 Nov 2023 22:52:16 -0800 Subject: [PATCH 04/24] Update main.py --- capa/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capa/main.py b/capa/main.py index 714713597..9e923fbf5 100644 --- a/capa/main.py +++ b/capa/main.py @@ -623,7 +623,7 @@ def get_extractor( return handle_pefile_backend(path) elif backend == BACKEND_VIV: - return handle_viv_backend(path, format, sigpaths, os_, disable_progress) + return handle_viv_backend(path, format, sigpaths, should_save_workspace, os_, disable_progress) else: raise ValueError("unexpected backend: " + backend) From 0aab720696222f33970ded684347791d9dd25d21 Mon Sep 17 00:00:00 2001 From: aaronatp <58194911+aaronatp@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:18:59 -0800 Subject: [PATCH 05/24] Update main.py --- capa/main.py | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/capa/main.py b/capa/main.py index 9e923fbf5..b045888e7 100644 --- a/capa/main.py +++ b/capa/main.py @@ -517,7 +517,7 @@ def get_workspace(path: Path, format_: str, sigpaths: List[Path]): return vw -def check_supported_format(path: Path, os_: str): +def check_unsupported_raise_exception(path: Path, os_: str): if not is_supported_format(path): raise UnsupportedFormatError() @@ -536,7 +536,7 @@ def add_binja_to_path(): sys.path.append(str(bn_api)) -def import_binja(): +def attempt_binja_import(): # When we are running as a standalone executable, we cannot directly import binaryninja # We need to fist find the binja API installation path and add it into sys.path if is_running_standalone(): @@ -555,8 +555,8 @@ def import_binja(): def handle_binja_backend(path: Path, disable_progress: bool) -> FeatureExtractor: import capa.features.extractors.binja.extractor - import_binja() - + attempt_binja_import() + with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): bv: BinaryView = binaryninja.load(str(path)) if bv is None: @@ -565,36 +565,30 @@ def handle_binja_backend(path: Path, disable_progress: bool) -> FeatureExtractor return capa.features.extractors.binja.extractor.BinjaFeatureExtractor(bv) +def attempt_save_workspace(vw): + try: + vw.saveWorkspace() + except IOError: + # see #168 for discussion around how to handle non-writable directories + logger.info("source directory is not writable, won't save intermediate workspace") + + def handle_viv_backend(path: Path, format_: str, sigpaths: List[Path], should_save_workspace: bool, \ os_: str, disable_progress: bool) -> FeatureExtractor: import capa.features.extractors.viv.extractor - + with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): vw = get_workspace(path, format_, sigpaths) if should_save_workspace: logger.debug("saving workspace") - try: - vw.saveWorkspace() - except IOError: - # see #168 for discussion around how to handle non-writable directories - logger.info("source directory is not writable, won't save intermediate workspace") + attempt_save_workspace(vw) else: logger.debug("CAPA_SAVE_WORKSPACE unset, not saving workspace") return capa.features.extractors.viv.extractor.VivisectFeatureExtractor(vw, path, os_) -def handle_pefile_backend(path: Path) -> FeatureExtractor: - import capa.features.extractors.pefile - return capa.features.extractors.pefile.PefileFeatureExtractor(path) - - -def handle_dotnet_format(path: Path) -> FeatureExtractor: - import capa.features.extractors.dnfile.extractor - return capa.features.extractors.dnfile.extractor.DnfileFeatureExtractor(path) - - def get_extractor( path: Path, format_: str, @@ -611,16 +605,18 @@ def get_extractor( UnsupportedOSError """ if format_ not in (FORMAT_SC32, FORMAT_SC64): - check_supported_format(path, os_) + check_unsupported_raise_exception(path, os_) if format_ == FORMAT_DOTNET: - return handle_dotnet_format(format_) + import capa.features.extractors.dnfile.extractor + return capa.features.extractors.dnfile.extractor.DnfileFeatureExtractor(path) elif backend == BACKEND_BINJA: return handle_binja_backend(path, disable_progress) elif backend == BACKEND_PEFILE: - return handle_pefile_backend(path) + import capa.features.extractors.pefile + return capa.features.extractors.pefile.PefileFeatureExtractor(path) elif backend == BACKEND_VIV: return handle_viv_backend(path, format, sigpaths, should_save_workspace, os_, disable_progress) From 50b4b067dd8c0580367e65b132f0722f72db2b1f Mon Sep 17 00:00:00 2001 From: aaronatp <58194911+aaronatp@users.noreply.github.com> Date: Fri, 10 Nov 2023 17:34:40 -0800 Subject: [PATCH 06/24] Updated handling for logging Unsupported Format, Arch, and OS --- capa/helpers.py | 60 ++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/capa/helpers.py b/capa/helpers.py index 38f94b028..5441b6de2 100644 --- a/capa/helpers.py +++ b/capa/helpers.py @@ -126,33 +126,41 @@ def new_print(*args, **kwargs): inspect.builtins.print = old_print # type: ignore -def log_unsupported_format_error(): - logger.error("-" * 80) - logger.error(" Input file does not appear to be a PE or ELF file.") - logger.error(" ") - logger.error( - " capa currently only supports analyzing PE and ELF files (or shellcode, when using --format sc32|sc64)." - ) - logger.error(" If you don't know the input file type, you can try using the `file` utility to guess it.") - logger.error("-" * 80) +def exceptUnsupportedError(func): + e_list = [UnsupportedFormatError, UnsupportedArchError, UnsupportedOSError] + + messsage_list = [ # UnsupportedFormatError + (" Input file does not appear to be a PE or ELF file.", + " capa currently only supports analyzing PE and ELF files (or shellcode, when using --format sc32|sc64).", + " If you don't know the input file type, you can try using the `file` utility to guess it."), + + # UnsupportedArchError + (" Input file does not appear to target a supported architecture.", + " capa currently only supports analyzing x86 (32- and 64-bit)."), + + # UnsupportedOSError + (" Input file does not appear to target a supported OS.", + " capa currently only supports analyzing executables for some operating systems (including Windows and Linux).") + ] + + def logging_wrapper(exception): + assert(exception in e_list) + e_messages = message_list[e_list.index(exception)] + + logger.error("-" * 80) + logger.error(f"{e_messages[0]}") + logger.error(" ") + + for i in e_messages[1:]: + logger.error(i) + + logger.error("-" * 80) + + if type(func(*args, **kwargs)) = ValueError: + return logging_wrapper(func(*args, **kwargs)) - -def log_unsupported_os_error(): - logger.error("-" * 80) - logger.error(" Input file does not appear to target a supported OS.") - logger.error(" ") - logger.error( - " capa currently only supports analyzing executables for some operating systems (including Windows and Linux)." - ) - logger.error("-" * 80) - - -def log_unsupported_arch_error(): - logger.error("-" * 80) - logger.error(" Input file does not appear to target a supported architecture.") - logger.error(" ") - logger.error(" capa currently only supports analyzing x86 (32- and 64-bit).") - logger.error("-" * 80) + else: + return func(*args, **kwargs) def log_unsupported_runtime_error(): From a9ead1208792bc04effcd667051c9f5a232406ab Mon Sep 17 00:00:00 2001 From: aaronatp <58194911+aaronatp@users.noreply.github.com> Date: Fri, 10 Nov 2023 17:51:52 -0800 Subject: [PATCH 07/24] Update helpers.py --- capa/helpers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/capa/helpers.py b/capa/helpers.py index 5441b6de2..75a0b7d94 100644 --- a/capa/helpers.py +++ b/capa/helpers.py @@ -127,7 +127,9 @@ def new_print(*args, **kwargs): def exceptUnsupportedError(func): - e_list = [UnsupportedFormatError, UnsupportedArchError, UnsupportedOSError] + e_list, return_values = [(UnsupportedFormatError E_INVALID_FILE_TYPE), + (UnsupportedArchError, E_INVALID_FILE_ARCH), + (UnsupportedOSError, E_INVALID_FILE_OS)] messsage_list = [ # UnsupportedFormatError (" Input file does not appear to be a PE or ELF file.", @@ -146,6 +148,7 @@ def exceptUnsupportedError(func): def logging_wrapper(exception): assert(exception in e_list) e_messages = message_list[e_list.index(exception)] + e_return_value = return_values[e_list.index(exception)] logger.error("-" * 80) logger.error(f"{e_messages[0]}") @@ -155,6 +158,8 @@ def logging_wrapper(exception): logger.error(i) logger.error("-" * 80) + + return e_return_value if type(func(*args, **kwargs)) = ValueError: return logging_wrapper(func(*args, **kwargs)) From bc616d07e7ef765afa1cccbb9d7d0176edb70e6b Mon Sep 17 00:00:00 2001 From: aaronatp <58194911+aaronatp@users.noreply.github.com> Date: Fri, 10 Nov 2023 17:59:38 -0800 Subject: [PATCH 08/24] Update main.py --- capa/main.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/capa/main.py b/capa/main.py index b045888e7..d49b38d2c 100644 --- a/capa/main.py +++ b/capa/main.py @@ -1280,8 +1280,11 @@ def main(argv: Optional[List[str]] = None): should_save_workspace = os.environ.get("CAPA_SAVE_WORKSPACE") not in ("0", "no", "NO", "n", None) - try: - extractor = get_extractor( + + # Perform error checking + # Return if unsupported hardware or software + extractor = exceptUnsupportedError( + get_extractor( args.sample, format_, args.os, @@ -1290,15 +1293,7 @@ def main(argv: Optional[List[str]] = None): should_save_workspace, disable_progress=args.quiet or args.debug, ) - except UnsupportedFormatError: - log_unsupported_format_error() - return E_INVALID_FILE_TYPE - except UnsupportedArchError: - log_unsupported_arch_error() - return E_INVALID_FILE_ARCH - except UnsupportedOSError: - log_unsupported_os_error() - return E_INVALID_FILE_OS + ) meta = collect_metadata(argv, args.sample, args.format, args.os, args.rules, extractor) From 329ac2de7e2baba1ac8987e41efdb3742580d1b5 Mon Sep 17 00:00:00 2001 From: aaronatp <58194911+aaronatp@users.noreply.github.com> Date: Sun, 12 Nov 2023 17:22:28 -0800 Subject: [PATCH 09/24] Update helpers.py --- capa/helpers.py | 57 +++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/capa/helpers.py b/capa/helpers.py index 75a0b7d94..878c060d1 100644 --- a/capa/helpers.py +++ b/capa/helpers.py @@ -9,6 +9,7 @@ import logging import contextlib import importlib.util +import functools from typing import NoReturn from pathlib import Path @@ -126,44 +127,40 @@ def new_print(*args, **kwargs): inspect.builtins.print = old_print # type: ignore -def exceptUnsupportedError(func): - e_list, return_values = [(UnsupportedFormatError E_INVALID_FILE_TYPE), - (UnsupportedArchError, E_INVALID_FILE_ARCH), - (UnsupportedOSError, E_INVALID_FILE_OS)] - - messsage_list = [ # UnsupportedFormatError - (" Input file does not appear to be a PE or ELF file.", - " capa currently only supports analyzing PE and ELF files (or shellcode, when using --format sc32|sc64).", - " If you don't know the input file type, you can try using the `file` utility to guess it."), - - # UnsupportedArchError - (" Input file does not appear to target a supported architecture.", - " capa currently only supports analyzing x86 (32- and 64-bit)."), - - # UnsupportedOSError - (" Input file does not appear to target a supported OS.", - " capa currently only supports analyzing executables for some operating systems (including Windows and Linux).") - ] - +def catch_log_return_errors(func): + error_list, return_values, message_list = [(UnsupportedFormatError, E_INVALID_FILE_TYPE, + (" Input file does not appear to be a PE or ELF file.", + " capa currently only supports analyzing PE and ELF files (or shellcode, when using --format sc32|sc64).", + " If you don't know the input file type, you can try using the `file` utility to guess it.")), + + (UnsupportedArchError, E_INVALID_FILE_ARCH, + (" Input file does not appear to target a supported architecture.", + " capa currently only supports analyzing x86 (32- and 64-bit).")), + + (UnsupportedOSError, E_INVALID_FILE_OS, + (" Input file does not appear to target a supported OS.", + " capa currently only supports analyzing executables for some operating systems (including Windows and Linux)."))] + + @functools.wraps(func) def logging_wrapper(exception): - assert(exception in e_list) - e_messages = message_list[e_list.index(exception)] - e_return_value = return_values[e_list.index(exception)] - + assert(exception in error_list) + error_messages = message_list[error_list.index(exception)] + error_return_value = return_values[error_list.index(exception)] + logger.error("-" * 80) - logger.error(f"{e_messages[0]}") + logger.error(f"{error_messages[0]}") logger.error(" ") - - for i in e_messages[1:]: + + for i in error_messages[1:]: logger.error(i) - + logger.error("-" * 80) - return e_return_value - + return error_return_value + if type(func(*args, **kwargs)) = ValueError: return logging_wrapper(func(*args, **kwargs)) - + else: return func(*args, **kwargs) From 4162c90e8c8ee015fd31260647883108153c4338 Mon Sep 17 00:00:00 2001 From: aaronatp <58194911+aaronatp@users.noreply.github.com> Date: Sun, 12 Nov 2023 17:52:45 -0800 Subject: [PATCH 10/24] Update main.py --- capa/main.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/capa/main.py b/capa/main.py index d49b38d2c..ad338649c 100644 --- a/capa/main.py +++ b/capa/main.py @@ -589,6 +589,7 @@ def handle_viv_backend(path: Path, format_: str, sigpaths: List[Path], should_sa return capa.features.extractors.viv.extractor.VivisectFeatureExtractor(vw, path, os_) +@catch_log_return_errors def get_extractor( path: Path, format_: str, @@ -1281,18 +1282,15 @@ def main(argv: Optional[List[str]] = None): should_save_workspace = os.environ.get("CAPA_SAVE_WORKSPACE") not in ("0", "no", "NO", "n", None) - # Perform error checking - # Return if unsupported hardware or software - extractor = exceptUnsupportedError( - get_extractor( - args.sample, - format_, - args.os, - args.backend, - sig_paths, - should_save_workspace, - disable_progress=args.quiet or args.debug, - ) + # Error checking and logging is performed in the get_extractor call + extractor = get_extractor( + args.sample, + format_, + args.os, + args.backend, + sig_paths, + should_save_workspace, + disable_progress=args.quiet or args.debug, ) meta = collect_metadata(argv, args.sample, args.format, args.os, args.rules, extractor) From 3533550dd8e951f02a00b09dd9cf92779294f5a7 Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 20:57:15 -0800 Subject: [PATCH 11/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/main.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/capa/main.py b/capa/main.py index ad338649c..3f9f5aa55 100644 --- a/capa/main.py +++ b/capa/main.py @@ -1281,6 +1281,7 @@ def main(argv: Optional[List[str]] = None): should_save_workspace = os.environ.get("CAPA_SAVE_WORKSPACE") not in ("0", "no", "NO", "n", None) +<<<<<<< HEAD # Error checking and logging is performed in the get_extractor call extractor = get_extractor( @@ -1292,6 +1293,27 @@ def main(argv: Optional[List[str]] = None): should_save_workspace, disable_progress=args.quiet or args.debug, ) +======= + try: + extractor = get_extractor( + args.sample, + format_, + args.os, + args.backend, + sig_paths, + should_save_workspace, + disable_progress=args.quiet or args.debug, + ) + except UnsupportedFormatError: + log_unsupported_format_error() + return E_INVALID_FILE_TYPE + except UnsupportedArchError: + log_unsupported_arch_error() + return E_INVALID_FILE_ARCH + except UnsupportedOSError: + log_unsupported_os_error() + return E_INVALID_FILE_OS +>>>>>>> parent of bc616d07 (Update main.py) meta = collect_metadata(argv, args.sample, args.format, args.os, args.rules, extractor) From c8a2003a1e4a98db72ad73a8e8bb663296a55fc9 Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 20:57:33 -0800 Subject: [PATCH 12/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/capa/main.py b/capa/main.py index 3f9f5aa55..1941e81f9 100644 --- a/capa/main.py +++ b/capa/main.py @@ -1281,6 +1281,7 @@ def main(argv: Optional[List[str]] = None): should_save_workspace = os.environ.get("CAPA_SAVE_WORKSPACE") not in ("0", "no", "NO", "n", None) +<<<<<<< HEAD <<<<<<< HEAD # Error checking and logging is performed in the get_extractor call @@ -1294,6 +1295,8 @@ def main(argv: Optional[List[str]] = None): disable_progress=args.quiet or args.debug, ) ======= +======= +>>>>>>> parent of bc616d07 (Update main.py) try: extractor = get_extractor( args.sample, @@ -1313,6 +1316,9 @@ def main(argv: Optional[List[str]] = None): except UnsupportedOSError: log_unsupported_os_error() return E_INVALID_FILE_OS +<<<<<<< HEAD +>>>>>>> parent of bc616d07 (Update main.py) +======= >>>>>>> parent of bc616d07 (Update main.py) meta = collect_metadata(argv, args.sample, args.format, args.os, args.rules, extractor) From 3d7831609a3da764496b6440639b31473c215d38 Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 20:57:46 -0800 Subject: [PATCH 13/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/capa/main.py b/capa/main.py index 1941e81f9..a1cfd6f94 100644 --- a/capa/main.py +++ b/capa/main.py @@ -1281,6 +1281,7 @@ def main(argv: Optional[List[str]] = None): should_save_workspace = os.environ.get("CAPA_SAVE_WORKSPACE") not in ("0", "no", "NO", "n", None) +<<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD @@ -1296,6 +1297,8 @@ def main(argv: Optional[List[str]] = None): ) ======= ======= +>>>>>>> parent of bc616d07 (Update main.py) +======= >>>>>>> parent of bc616d07 (Update main.py) try: extractor = get_extractor( @@ -1317,6 +1320,9 @@ def main(argv: Optional[List[str]] = None): log_unsupported_os_error() return E_INVALID_FILE_OS <<<<<<< HEAD +<<<<<<< HEAD +>>>>>>> parent of bc616d07 (Update main.py) +======= >>>>>>> parent of bc616d07 (Update main.py) ======= >>>>>>> parent of bc616d07 (Update main.py) From 932b36e0ad3a246fc07d917efe9c0975073019df Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 20:57:56 -0800 Subject: [PATCH 14/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/capa/main.py b/capa/main.py index a1cfd6f94..b4de6ce54 100644 --- a/capa/main.py +++ b/capa/main.py @@ -1283,6 +1283,7 @@ def main(argv: Optional[List[str]] = None): <<<<<<< HEAD <<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD # Error checking and logging is performed in the get_extractor call @@ -1299,6 +1300,8 @@ def main(argv: Optional[List[str]] = None): ======= >>>>>>> parent of bc616d07 (Update main.py) ======= +>>>>>>> parent of bc616d07 (Update main.py) +======= >>>>>>> parent of bc616d07 (Update main.py) try: extractor = get_extractor( @@ -1321,6 +1324,9 @@ def main(argv: Optional[List[str]] = None): return E_INVALID_FILE_OS <<<<<<< HEAD <<<<<<< HEAD +<<<<<<< HEAD +>>>>>>> parent of bc616d07 (Update main.py) +======= >>>>>>> parent of bc616d07 (Update main.py) ======= >>>>>>> parent of bc616d07 (Update main.py) From b7552274eb3cbbca962ed71de9fc968e350d41cc Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 20:59:19 -0800 Subject: [PATCH 15/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/helpers.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/capa/helpers.py b/capa/helpers.py index 878c060d1..4b038c49e 100644 --- a/capa/helpers.py +++ b/capa/helpers.py @@ -127,6 +127,7 @@ def new_print(*args, **kwargs): inspect.builtins.print = old_print # type: ignore +<<<<<<< HEAD def catch_log_return_errors(func): error_list, return_values, message_list = [(UnsupportedFormatError, E_INVALID_FILE_TYPE, (" Input file does not appear to be a PE or ELF file.", @@ -147,6 +148,29 @@ def logging_wrapper(exception): error_messages = message_list[error_list.index(exception)] error_return_value = return_values[error_list.index(exception)] +======= +def exceptUnsupportedError(func): + e_list = [UnsupportedFormatError, UnsupportedArchError, UnsupportedOSError] + + messsage_list = [ # UnsupportedFormatError + (" Input file does not appear to be a PE or ELF file.", + " capa currently only supports analyzing PE and ELF files (or shellcode, when using --format sc32|sc64).", + " If you don't know the input file type, you can try using the `file` utility to guess it."), + + # UnsupportedArchError + (" Input file does not appear to target a supported architecture.", + " capa currently only supports analyzing x86 (32- and 64-bit)."), + + # UnsupportedOSError + (" Input file does not appear to target a supported OS.", + " capa currently only supports analyzing executables for some operating systems (including Windows and Linux).") + ] + + def logging_wrapper(exception): + assert(exception in e_list) + e_messages = message_list[e_list.index(exception)] + +>>>>>>> parent of a9ead120 (Update helpers.py) logger.error("-" * 80) logger.error(f"{error_messages[0]}") logger.error(" ") @@ -155,9 +179,13 @@ def logging_wrapper(exception): logger.error(i) logger.error("-" * 80) +<<<<<<< HEAD return error_return_value +======= + +>>>>>>> parent of a9ead120 (Update helpers.py) if type(func(*args, **kwargs)) = ValueError: return logging_wrapper(func(*args, **kwargs)) From 98d15fd159a84050b3e9a319f10924ed9393e189 Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 20:59:31 -0800 Subject: [PATCH 16/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/helpers.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/capa/helpers.py b/capa/helpers.py index 4b038c49e..e691656ec 100644 --- a/capa/helpers.py +++ b/capa/helpers.py @@ -127,6 +127,7 @@ def new_print(*args, **kwargs): inspect.builtins.print = old_print # type: ignore +<<<<<<< HEAD <<<<<<< HEAD def catch_log_return_errors(func): error_list, return_values, message_list = [(UnsupportedFormatError, E_INVALID_FILE_TYPE, @@ -191,6 +192,35 @@ def logging_wrapper(exception): else: return func(*args, **kwargs) +======= +def log_unsupported_format_error(): + logger.error("-" * 80) + logger.error(" Input file does not appear to be a PE or ELF file.") + logger.error(" ") + logger.error( + " capa currently only supports analyzing PE and ELF files (or shellcode, when using --format sc32|sc64)." + ) + logger.error(" If you don't know the input file type, you can try using the `file` utility to guess it.") + logger.error("-" * 80) + + +def log_unsupported_os_error(): + logger.error("-" * 80) + logger.error(" Input file does not appear to target a supported OS.") + logger.error(" ") + logger.error( + " capa currently only supports analyzing executables for some operating systems (including Windows and Linux)." + ) + logger.error("-" * 80) + + +def log_unsupported_arch_error(): + logger.error("-" * 80) + logger.error(" Input file does not appear to target a supported architecture.") + logger.error(" ") + logger.error(" capa currently only supports analyzing x86 (32- and 64-bit).") + logger.error("-" * 80) +>>>>>>> parent of 50b4b067 (Updated handling for logging Unsupported Format, Arch, and OS) def log_unsupported_runtime_error(): From d3aead931f0db9a92d68a755c1e533294bcdf541 Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 20:59:45 -0800 Subject: [PATCH 17/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/main.py | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/capa/main.py b/capa/main.py index b4de6ce54..b95cffa04 100644 --- a/capa/main.py +++ b/capa/main.py @@ -517,7 +517,7 @@ def get_workspace(path: Path, format_: str, sigpaths: List[Path]): return vw -def check_unsupported_raise_exception(path: Path, os_: str): +def check_supported_format(path: Path, os_: str): if not is_supported_format(path): raise UnsupportedFormatError() @@ -536,7 +536,7 @@ def add_binja_to_path(): sys.path.append(str(bn_api)) -def attempt_binja_import(): +def import_binja(): # When we are running as a standalone executable, we cannot directly import binaryninja # We need to fist find the binja API installation path and add it into sys.path if is_running_standalone(): @@ -555,8 +555,8 @@ def attempt_binja_import(): def handle_binja_backend(path: Path, disable_progress: bool) -> FeatureExtractor: import capa.features.extractors.binja.extractor - attempt_binja_import() - + import_binja() + with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): bv: BinaryView = binaryninja.load(str(path)) if bv is None: @@ -565,31 +565,40 @@ def handle_binja_backend(path: Path, disable_progress: bool) -> FeatureExtractor return capa.features.extractors.binja.extractor.BinjaFeatureExtractor(bv) -def attempt_save_workspace(vw): - try: - vw.saveWorkspace() - except IOError: - # see #168 for discussion around how to handle non-writable directories - logger.info("source directory is not writable, won't save intermediate workspace") - - def handle_viv_backend(path: Path, format_: str, sigpaths: List[Path], should_save_workspace: bool, \ os_: str, disable_progress: bool) -> FeatureExtractor: import capa.features.extractors.viv.extractor - + with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): vw = get_workspace(path, format_, sigpaths) if should_save_workspace: logger.debug("saving workspace") - attempt_save_workspace(vw) + try: + vw.saveWorkspace() + except IOError: + # see #168 for discussion around how to handle non-writable directories + logger.info("source directory is not writable, won't save intermediate workspace") else: logger.debug("CAPA_SAVE_WORKSPACE unset, not saving workspace") return capa.features.extractors.viv.extractor.VivisectFeatureExtractor(vw, path, os_) +<<<<<<< HEAD @catch_log_return_errors +======= +def handle_pefile_backend(path: Path) -> FeatureExtractor: + import capa.features.extractors.pefile + return capa.features.extractors.pefile.PefileFeatureExtractor(path) + + +def handle_dotnet_format(path: Path) -> FeatureExtractor: + import capa.features.extractors.dnfile.extractor + return capa.features.extractors.dnfile.extractor.DnfileFeatureExtractor(path) + + +>>>>>>> parent of 0aab7206 (Update main.py) def get_extractor( path: Path, format_: str, @@ -606,18 +615,16 @@ def get_extractor( UnsupportedOSError """ if format_ not in (FORMAT_SC32, FORMAT_SC64): - check_unsupported_raise_exception(path, os_) + check_supported_format(path, os_) if format_ == FORMAT_DOTNET: - import capa.features.extractors.dnfile.extractor - return capa.features.extractors.dnfile.extractor.DnfileFeatureExtractor(path) + return handle_dotnet_format(format_) elif backend == BACKEND_BINJA: return handle_binja_backend(path, disable_progress) elif backend == BACKEND_PEFILE: - import capa.features.extractors.pefile - return capa.features.extractors.pefile.PefileFeatureExtractor(path) + return handle_pefile_backend(path) elif backend == BACKEND_VIV: return handle_viv_backend(path, format, sigpaths, should_save_workspace, os_, disable_progress) From 4247a94685c350edb0bfe5b02882835e6ecba927 Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 20:59:49 -0800 Subject: [PATCH 18/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capa/main.py b/capa/main.py index b95cffa04..c02a63da4 100644 --- a/capa/main.py +++ b/capa/main.py @@ -627,7 +627,7 @@ def get_extractor( return handle_pefile_backend(path) elif backend == BACKEND_VIV: - return handle_viv_backend(path, format, sigpaths, should_save_workspace, os_, disable_progress) + return handle_viv_backend(path, format, sigpaths, os_, disable_progress) else: raise ValueError("unexpected backend: " + backend) From faa4c0c6fd7cce839b68df3fb3de663d34aaa2f1 Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 21:00:00 -0800 Subject: [PATCH 19/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/main.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/capa/main.py b/capa/main.py index c02a63da4..627978ecb 100644 --- a/capa/main.py +++ b/capa/main.py @@ -552,7 +552,7 @@ def import_binja(): ) -def handle_binja_backend(path: Path, disable_progress: bool) -> FeatureExtractor: +def handle_binja_backend(path: Path, disable_progress: bool) -> BinjaFeatureExtractor: import capa.features.extractors.binja.extractor import_binja() @@ -565,8 +565,7 @@ def handle_binja_backend(path: Path, disable_progress: bool) -> FeatureExtractor return capa.features.extractors.binja.extractor.BinjaFeatureExtractor(bv) -def handle_viv_backend(path: Path, format_: str, sigpaths: List[Path], should_save_workspace: bool, \ - os_: str, disable_progress: bool) -> FeatureExtractor: +def handle_viv_backend(path: Path, format_: str, sigpaths: List[Path], os_: str, disable_progress: bool) -> VivisectFeatureExtractor: import capa.features.extractors.viv.extractor with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): @@ -585,15 +584,19 @@ def handle_viv_backend(path: Path, format_: str, sigpaths: List[Path], should_sa return capa.features.extractors.viv.extractor.VivisectFeatureExtractor(vw, path, os_) +<<<<<<< HEAD <<<<<<< HEAD @catch_log_return_errors ======= def handle_pefile_backend(path: Path) -> FeatureExtractor: +======= +def handle_pefile_backend(path: Path) -> PefileFeatureExtractor: +>>>>>>> parent of 222cd6c4 (Update main.py) import capa.features.extractors.pefile return capa.features.extractors.pefile.PefileFeatureExtractor(path) -def handle_dotnet_format(path: Path) -> FeatureExtractor: +def handle_dotnet_format(format_: str) -> DnfileFeatureExtractor: import capa.features.extractors.dnfile.extractor return capa.features.extractors.dnfile.extractor.DnfileFeatureExtractor(path) From f96a5ff526d44c14d25ed43b1c9e754f61e5889a Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 21:00:12 -0800 Subject: [PATCH 20/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/main.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/capa/main.py b/capa/main.py index 627978ecb..4dcd4e4d3 100644 --- a/capa/main.py +++ b/capa/main.py @@ -517,7 +517,7 @@ def get_workspace(path: Path, format_: str, sigpaths: List[Path]): return vw -def check_supported_format(path: Path, os_: str): +def check_supported_format(path, os_): if not is_supported_format(path): raise UnsupportedFormatError() @@ -528,6 +528,7 @@ def check_supported_format(path: Path, os_: str): raise UnsupportedOSError() + def add_binja_to_path(): from capa.features.extractors.binja.find_binja_api import find_binja_path @@ -536,6 +537,7 @@ def add_binja_to_path(): sys.path.append(str(bn_api)) + def import_binja(): # When we are running as a standalone executable, we cannot directly import binaryninja # We need to fist find the binja API installation path and add it into sys.path @@ -552,7 +554,8 @@ def import_binja(): ) -def handle_binja_backend(path: Path, disable_progress: bool) -> BinjaFeatureExtractor: + +def handle_binja_backend(path): import capa.features.extractors.binja.extractor import_binja() @@ -565,7 +568,8 @@ def handle_binja_backend(path: Path, disable_progress: bool) -> BinjaFeatureExtr return capa.features.extractors.binja.extractor.BinjaFeatureExtractor(bv) -def handle_viv_backend(path: Path, format_: str, sigpaths: List[Path], os_: str, disable_progress: bool) -> VivisectFeatureExtractor: + +def handle_viv_backend(path, format_, sigpaths, os_): import capa.features.extractors.viv.extractor with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): @@ -584,6 +588,7 @@ def handle_viv_backend(path: Path, format_: str, sigpaths: List[Path], os_: str, return capa.features.extractors.viv.extractor.VivisectFeatureExtractor(vw, path, os_) +<<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD @catch_log_return_errors @@ -592,16 +597,24 @@ def handle_pefile_backend(path: Path) -> FeatureExtractor: ======= def handle_pefile_backend(path: Path) -> PefileFeatureExtractor: >>>>>>> parent of 222cd6c4 (Update main.py) +======= + +def handle_pefile_backend(path): +>>>>>>> parent of d46fa26c (Incremental PR improvements) import capa.features.extractors.pefile return capa.features.extractors.pefile.PefileFeatureExtractor(path) -def handle_dotnet_format(format_: str) -> DnfileFeatureExtractor: +def handle_dotnet_format(format): import capa.features.extractors.dnfile.extractor return capa.features.extractors.dnfile.extractor.DnfileFeatureExtractor(path) +<<<<<<< HEAD >>>>>>> parent of 0aab7206 (Update main.py) +======= + +>>>>>>> parent of d46fa26c (Incremental PR improvements) def get_extractor( path: Path, format_: str, @@ -621,21 +634,22 @@ def get_extractor( check_supported_format(path, os_) if format_ == FORMAT_DOTNET: - return handle_dotnet_format(format_) + return handle_dotnet_format(format) elif backend == BACKEND_BINJA: - return handle_binja_backend(path, disable_progress) + return handle_binja_backend(path) elif backend == BACKEND_PEFILE: return handle_pefile_backend(path) elif backend == BACKEND_VIV: - return handle_viv_backend(path, format, sigpaths, os_, disable_progress) + return handle_viv_backend(path, format, sigpaths, os_) else: raise ValueError("unexpected backend: " + backend) + def get_file_extractors(sample: Path, format_: str) -> List[FeatureExtractor]: file_extractors: List[FeatureExtractor] = [] From 10d8a208127f3effd444b8ffd9ddd674fbfab6ed Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 21:00:21 -0800 Subject: [PATCH 21/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/main.py | 65 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/capa/main.py b/capa/main.py index 4dcd4e4d3..7d0266f60 100644 --- a/capa/main.py +++ b/capa/main.py @@ -517,6 +517,7 @@ def get_workspace(path: Path, format_: str, sigpaths: List[Path]): return vw +<<<<<<< HEAD def check_supported_format(path, os_): if not is_supported_format(path): raise UnsupportedFormatError() @@ -615,6 +616,8 @@ def handle_dotnet_format(format): ======= >>>>>>> parent of d46fa26c (Incremental PR improvements) +======= +>>>>>>> parent of d061e0c5 (Update main.py) def get_extractor( path: Path, format_: str, @@ -631,23 +634,73 @@ def get_extractor( UnsupportedOSError """ if format_ not in (FORMAT_SC32, FORMAT_SC64): - check_supported_format(path, os_) + if not is_supported_format(path): + raise UnsupportedFormatError() + + if not is_supported_arch(path): + raise UnsupportedArchError() + + if os_ == OS_AUTO and not is_supported_os(path): + raise UnsupportedOSError() if format_ == FORMAT_DOTNET: - return handle_dotnet_format(format) + import capa.features.extractors.dnfile.extractor + + return capa.features.extractors.dnfile.extractor.DnfileFeatureExtractor(path) elif backend == BACKEND_BINJA: - return handle_binja_backend(path) + from capa.features.extractors.binja.find_binja_api import find_binja_path + + # When we are running as a standalone executable, we cannot directly import binaryninja + # We need to fist find the binja API installation path and add it into sys.path + if is_running_standalone(): + bn_api = find_binja_path() + if bn_api.exists(): + sys.path.append(str(bn_api)) + + try: + import binaryninja + from binaryninja import BinaryView + except ImportError: + raise RuntimeError( + "Cannot import binaryninja module. Please install the Binary Ninja Python API first: " + + "https://docs.binary.ninja/dev/batch.html#install-the-api)." + ) + + import capa.features.extractors.binja.extractor + + with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): + bv: BinaryView = binaryninja.load(str(path)) + if bv is None: + raise RuntimeError(f"Binary Ninja cannot open file {path}") + + return capa.features.extractors.binja.extractor.BinjaFeatureExtractor(bv) elif backend == BACKEND_PEFILE: - return handle_pefile_backend(path) + import capa.features.extractors.pefile + + return capa.features.extractors.pefile.PefileFeatureExtractor(path) elif backend == BACKEND_VIV: - return handle_viv_backend(path, format, sigpaths, os_) + import capa.features.extractors.viv.extractor + + with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): + vw = get_workspace(path, format_, sigpaths) + + if should_save_workspace: + logger.debug("saving workspace") + try: + vw.saveWorkspace() + except IOError: + # see #168 for discussion around how to handle non-writable directories + logger.info("source directory is not writable, won't save intermediate workspace") + else: + logger.debug("CAPA_SAVE_WORKSPACE unset, not saving workspace") + + return capa.features.extractors.viv.extractor.VivisectFeatureExtractor(vw, path, os_) else: raise ValueError("unexpected backend: " + backend) - def get_file_extractors(sample: Path, format_: str) -> List[FeatureExtractor]: From 4acd8cdbd64b1240d49ed14300542aacec856d55 Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 23:12:12 -0800 Subject: [PATCH 22/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/helpers.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/capa/helpers.py b/capa/helpers.py index e691656ec..f9c2c7826 100644 --- a/capa/helpers.py +++ b/capa/helpers.py @@ -175,6 +175,7 @@ def logging_wrapper(exception): logger.error("-" * 80) logger.error(f"{error_messages[0]}") logger.error(" ") +<<<<<<< HEAD for i in error_messages[1:]: logger.error(i) @@ -185,6 +186,13 @@ def logging_wrapper(exception): return error_return_value ======= +======= + + for i in e_messages[1:]: + logger.error(i) + + logger.error("-" * 80) +>>>>>>> parent of a9ead120 (Update helpers.py) >>>>>>> parent of a9ead120 (Update helpers.py) if type(func(*args, **kwargs)) = ValueError: From 88d725f45781bfa1c677b95d681da2cda8cd40f3 Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 23:12:35 -0800 Subject: [PATCH 23/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/helpers.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/capa/helpers.py b/capa/helpers.py index f9c2c7826..48e1e8e2f 100644 --- a/capa/helpers.py +++ b/capa/helpers.py @@ -127,6 +127,7 @@ def new_print(*args, **kwargs): inspect.builtins.print = old_print # type: ignore +<<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD def catch_log_return_errors(func): @@ -201,6 +202,8 @@ def logging_wrapper(exception): else: return func(*args, **kwargs) ======= +======= +>>>>>>> parent of 50b4b067 (Updated handling for logging Unsupported Format, Arch, and OS) def log_unsupported_format_error(): logger.error("-" * 80) logger.error(" Input file does not appear to be a PE or ELF file.") @@ -228,6 +231,9 @@ def log_unsupported_arch_error(): logger.error(" ") logger.error(" capa currently only supports analyzing x86 (32- and 64-bit).") logger.error("-" * 80) +<<<<<<< HEAD +>>>>>>> parent of 50b4b067 (Updated handling for logging Unsupported Format, Arch, and OS) +======= >>>>>>> parent of 50b4b067 (Updated handling for logging Unsupported Format, Arch, and OS) From f537838060faa541421b669a1953eff857dd8c10 Mon Sep 17 00:00:00 2001 From: aaronatp Date: Sun, 12 Nov 2023 23:12:53 -0800 Subject: [PATCH 24/24] Revert "Update main.py" This reverts commit d6498974865fbbe4fe28659a649c47eebf15846b. --- capa/main.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/capa/main.py b/capa/main.py index 7d0266f60..344eeb55f 100644 --- a/capa/main.py +++ b/capa/main.py @@ -518,6 +518,9 @@ def get_workspace(path: Path, format_: str, sigpaths: List[Path]): <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> parent of d46fa26c (Incremental PR improvements) def check_supported_format(path, os_): if not is_supported_format(path): raise UnsupportedFormatError() @@ -592,6 +595,7 @@ def handle_viv_backend(path, format_, sigpaths, os_): <<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD +<<<<<<< HEAD @catch_log_return_errors ======= def handle_pefile_backend(path: Path) -> FeatureExtractor: @@ -600,6 +604,10 @@ def handle_pefile_backend(path: Path) -> PefileFeatureExtractor: >>>>>>> parent of 222cd6c4 (Update main.py) ======= +def handle_pefile_backend(path): +>>>>>>> parent of d46fa26c (Incremental PR improvements) +======= + def handle_pefile_backend(path): >>>>>>> parent of d46fa26c (Incremental PR improvements) import capa.features.extractors.pefile @@ -611,6 +619,7 @@ def handle_dotnet_format(format): return capa.features.extractors.dnfile.extractor.DnfileFeatureExtractor(path) +<<<<<<< HEAD <<<<<<< HEAD >>>>>>> parent of 0aab7206 (Update main.py) ======= @@ -618,6 +627,9 @@ def handle_dotnet_format(format): >>>>>>> parent of d46fa26c (Incremental PR improvements) ======= >>>>>>> parent of d061e0c5 (Update main.py) +======= + +>>>>>>> parent of d46fa26c (Incremental PR improvements) def get_extractor( path: Path, format_: str, @@ -644,6 +656,7 @@ def get_extractor( raise UnsupportedOSError() if format_ == FORMAT_DOTNET: +<<<<<<< HEAD import capa.features.extractors.dnfile.extractor return capa.features.extractors.dnfile.extractor.DnfileFeatureExtractor(path) @@ -675,6 +688,12 @@ def get_extractor( raise RuntimeError(f"Binary Ninja cannot open file {path}") return capa.features.extractors.binja.extractor.BinjaFeatureExtractor(bv) +======= + return handle_dotnet_format(format) + + elif backend == BACKEND_BINJA: + return handle_binja_backend(path) +>>>>>>> parent of d46fa26c (Incremental PR improvements) elif backend == BACKEND_PEFILE: import capa.features.extractors.pefile @@ -682,6 +701,7 @@ def get_extractor( return capa.features.extractors.pefile.PefileFeatureExtractor(path) elif backend == BACKEND_VIV: +<<<<<<< HEAD import capa.features.extractors.viv.extractor with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): @@ -698,11 +718,15 @@ def get_extractor( logger.debug("CAPA_SAVE_WORKSPACE unset, not saving workspace") return capa.features.extractors.viv.extractor.VivisectFeatureExtractor(vw, path, os_) +======= + return handle_viv_backend(path, format, sigpaths, os_) +>>>>>>> parent of d46fa26c (Incremental PR improvements) else: raise ValueError("unexpected backend: " + backend) + def get_file_extractors(sample: Path, format_: str) -> List[FeatureExtractor]: file_extractors: List[FeatureExtractor] = []