diff --git a/capa/features/extractors/ida/extractor.py b/capa/features/extractors/ida/extractor.py index 93f2ce55..7d00c7ef 100644 --- a/capa/features/extractors/ida/extractor.py +++ b/capa/features/extractors/ida/extractor.py @@ -5,10 +5,7 @@ # Unless required by applicable law or agreed to in writing, software distributed under the License # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and limitations under the License. -import contextlib - import idaapi -import ida_loader import capa.ida.helpers import capa.features.extractors.elf @@ -17,26 +14,9 @@ import capa.features.extractors.ida.insn import capa.features.extractors.ida.global_ import capa.features.extractors.ida.function import capa.features.extractors.ida.basicblock -from capa.features.common import OS, OS_WINDOWS from capa.features.extractors.base_extractor import FeatureExtractor -def extract_os(): - format_name = ida_loader.get_file_type_name() - - if "PE" in format_name: - yield OS(OS_WINDOWS), 0x0 - - elif "ELF" in format_name: - with contextlib.closing(capa.ida.helpers.IDAIO()) as f: - os = capa.features.extractors.elf.detect_elf_os(f) - - yield OS(os), 0x0 - - else: - raise NotImplementedError("file format: %s", format_name) - - class FunctionHandle: """this acts like an idaapi.func_t but with __int__()""" @@ -80,7 +60,7 @@ class IdaFeatureExtractor(FeatureExtractor): def __init__(self): super(IdaFeatureExtractor, self).__init__() self.global_features = [] - self.global_features.extend(extract_os()) + self.global_features.extend(capa.features.extractors.ida.global_.extract_os()) self.global_features.extend(capa.features.extractors.ida.global_.extract_arch()) def get_base_address(self): diff --git a/capa/features/extractors/ida/global_.py b/capa/features/extractors/ida/global_.py index 1919dd5d..b6f525f6 100644 --- a/capa/features/extractors/ida/global_.py +++ b/capa/features/extractors/ida/global_.py @@ -1,7 +1,43 @@ -import idaapi +import logging +import contextlib +import idaapi +import ida_loader + +import capa.ida.helpers +import capa.features.extractors.elf +from capa.features.common import OS, OS_WINDOWS from capa.features.common import ARCH_I386, ARCH_AMD64, Arch +logger = logging.getLogger(__name__) + + +def extract_os(): + format_name = ida_loader.get_file_type_name() + + if "PE" in format_name: + yield OS(OS_WINDOWS), 0x0 + + elif "ELF" in format_name: + with contextlib.closing(capa.ida.helpers.IDAIO()) as f: + os = capa.features.extractors.elf.detect_elf_os(f) + + yield OS(os), 0x0 + + else: + # we likely end up here: + # 1. handling shellcode, or + # 2. handling a new file format (e.g. macho) + # + # for (1) we can't do much - its shellcode and all bets are off. + # we could maybe accept a futher CLI argument to specify the OS, + # but i think this would be rarely used. + # rules that rely on OS conditions will fail to match on shellcode. + # + # for (2), this logic will need to be updated as the format is implemented. + logger.debug("unsupported file format: %s, will not guess OS", format_name) + return + def extract_arch(): info = idaapi.get_inf_structure() @@ -10,6 +46,12 @@ def extract_arch(): elif info.procName == "metapc" and info.is_32bit(): yield Arch(ARCH_I386), 0x0 elif info.procName == "metapc": - raise NotImplementedError("unsupported architecture: non-32-bit nor non-64-bit intel") + logger.debug("unsupported architecture: non-32-bit nor non-64-bit intel") + return else: - raise NotImplementedError("unsupported architecture: %s" % (info.procName)) + # we likely end up here: + # 1. handling a new architecture (e.g. aarch64) + # + # for (1), this logic will need to be updated as the format is implemented. + logger.debug("unsupported architecture: %s", info.procName) + return