mirror of
https://github.com/mandiant/capa.git
synced 2025-12-12 15:49:46 -08:00
use new IDAPython 9.0 APIs (#2339)
* use new IDAPython 9.0 APIs * add IDAPython compatibility wrappers
This commit is contained in:
@@ -11,7 +11,8 @@ Unlock powerful malware analysis with capa's new [VMRay sandbox](https://www.vmr
|
||||
- add .justfile @williballenthin #2325
|
||||
- dynamic: add support for VMRay dynamic sandbox traces #2208 @mike-hunhoff @r-sm2024 @mr-tz
|
||||
- cli: use modern terminal features to hyperlink to the rules website #2337 @williballenthin
|
||||
|
||||
- update IDAPython to IDA Pro 9.0 @mr-tz
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
### New Rules (0)
|
||||
|
||||
@@ -14,6 +14,7 @@ import idaapi
|
||||
import idautils
|
||||
import ida_entry
|
||||
|
||||
import capa.ida.helpers
|
||||
import capa.features.extractors.common
|
||||
import capa.features.extractors.helpers
|
||||
import capa.features.extractors.strings
|
||||
@@ -177,17 +178,17 @@ def extract_file_function_names() -> Iterator[Tuple[Feature, Address]]:
|
||||
|
||||
|
||||
def extract_file_format() -> Iterator[Tuple[Feature, Address]]:
|
||||
file_info = idaapi.get_inf_structure()
|
||||
filetype = capa.ida.helpers.get_filetype()
|
||||
|
||||
if file_info.filetype in (idaapi.f_PE, idaapi.f_COFF):
|
||||
if filetype in (idaapi.f_PE, idaapi.f_COFF):
|
||||
yield Format(FORMAT_PE), NO_ADDRESS
|
||||
elif file_info.filetype == idaapi.f_ELF:
|
||||
elif filetype == idaapi.f_ELF:
|
||||
yield Format(FORMAT_ELF), NO_ADDRESS
|
||||
elif file_info.filetype == idaapi.f_BIN:
|
||||
elif filetype == idaapi.f_BIN:
|
||||
# no file type to return when processing a binary file, but we want to continue processing
|
||||
return
|
||||
else:
|
||||
raise NotImplementedError(f"unexpected file format: {file_info.filetype}")
|
||||
raise NotImplementedError(f"unexpected file format: {filetype}")
|
||||
|
||||
|
||||
def extract_features() -> Iterator[Tuple[Feature, Address]]:
|
||||
|
||||
@@ -9,7 +9,6 @@ import logging
|
||||
import contextlib
|
||||
from typing import Tuple, Iterator
|
||||
|
||||
import idaapi
|
||||
import ida_loader
|
||||
|
||||
import capa.ida.helpers
|
||||
@@ -48,12 +47,12 @@ def extract_os() -> Iterator[Tuple[Feature, Address]]:
|
||||
|
||||
|
||||
def extract_arch() -> Iterator[Tuple[Feature, Address]]:
|
||||
info: idaapi.idainfo = idaapi.get_inf_structure()
|
||||
if info.procname == "metapc" and info.is_64bit():
|
||||
procname = capa.ida.helpers.get_processor_name()
|
||||
if procname == "metapc" and capa.ida.helpers.is_64bit():
|
||||
yield Arch(ARCH_AMD64), NO_ADDRESS
|
||||
elif info.procname == "metapc" and info.is_32bit():
|
||||
elif procname == "metapc" and capa.ida.helpers.is_32bit():
|
||||
yield Arch(ARCH_I386), NO_ADDRESS
|
||||
elif info.procname == "metapc":
|
||||
elif procname == "metapc":
|
||||
logger.debug("unsupported architecture: non-32-bit nor non-64-bit intel")
|
||||
return
|
||||
else:
|
||||
@@ -61,5 +60,5 @@ def extract_arch() -> Iterator[Tuple[Feature, Address]]:
|
||||
# 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)
|
||||
logger.debug("unsupported architecture: %s", procname)
|
||||
return
|
||||
|
||||
@@ -21,6 +21,8 @@ from capa.features.extractors.base_extractor import FunctionHandle
|
||||
IDA_NALT_ENCODING = ida_nalt.get_default_encoding_idx(ida_nalt.BPU_1B) # use one byte-per-character encoding
|
||||
|
||||
|
||||
# TODO (mr): use find_bytes
|
||||
# https://github.com/mandiant/capa/issues/2339
|
||||
def find_byte_sequence(start: int, end: int, seq: bytes) -> Iterator[int]:
|
||||
"""yield all ea of a given byte sequence
|
||||
|
||||
@@ -38,7 +40,7 @@ def find_byte_sequence(start: int, end: int, seq: bytes) -> Iterator[int]:
|
||||
return
|
||||
|
||||
while True:
|
||||
ea = ida_bytes.bin_search(start, end, patterns, ida_bytes.BIN_SEARCH_FORWARD)
|
||||
ea, _ = ida_bytes.bin_search3(start, end, patterns, ida_bytes.BIN_SEARCH_FORWARD)
|
||||
if ea == idaapi.BADADDR:
|
||||
break
|
||||
start = ea + 1
|
||||
|
||||
@@ -13,6 +13,7 @@ from pathlib import Path
|
||||
|
||||
import idc
|
||||
import idaapi
|
||||
import ida_ida
|
||||
import idautils
|
||||
import ida_bytes
|
||||
import ida_loader
|
||||
@@ -45,6 +46,39 @@ NETNODE_RESULTS = "results"
|
||||
NETNODE_RULES_CACHE_ID = "rules-cache-id"
|
||||
|
||||
|
||||
# wrappers for IDA Pro (IDAPython) 7, 8 and 9 compability
|
||||
version = float(idaapi.get_kernel_version())
|
||||
if version < 9.0:
|
||||
|
||||
def get_filetype() -> "ida_ida.filetype_t":
|
||||
return idaapi.get_inf_structure().filetype
|
||||
|
||||
def get_processor_name() -> str:
|
||||
return idaapi.get_inf_structure().procname
|
||||
|
||||
def is_32bit() -> bool:
|
||||
info: idaapi.idainfo = idaapi.get_inf_structure()
|
||||
return info.is_32bit()
|
||||
|
||||
def is_64bit() -> bool:
|
||||
info: idaapi.idainfo = idaapi.get_inf_structure()
|
||||
return info.is_64bit()
|
||||
|
||||
else:
|
||||
|
||||
def get_filetype() -> "ida_ida.filetype_t":
|
||||
return ida_ida.inf_get_filetype()
|
||||
|
||||
def get_processor_name() -> str:
|
||||
return idc.get_processor_name()
|
||||
|
||||
def is_32bit() -> bool:
|
||||
return idaapi.inf_is_32bit_exactly()
|
||||
|
||||
def is_64bit() -> bool:
|
||||
return idaapi.inf_is_64bit()
|
||||
|
||||
|
||||
def inform_user_ida_ui(message):
|
||||
# this isn't a logger, this is IDA's logging facility
|
||||
idaapi.info(f"{message}. Please refer to IDA Output window for more information.") # noqa: G004
|
||||
@@ -52,17 +86,16 @@ def inform_user_ida_ui(message):
|
||||
|
||||
def is_supported_ida_version():
|
||||
version = float(idaapi.get_kernel_version())
|
||||
if version < 7.4 or version >= 9:
|
||||
if version < 7.4 or version >= 10:
|
||||
warning_msg = "This plugin does not support your IDA Pro version"
|
||||
logger.warning(warning_msg)
|
||||
logger.warning("Your IDA Pro version is: %s. Supported versions are: IDA >= 7.4 and IDA < 9.0.", version)
|
||||
logger.warning("Your IDA Pro version is: %s. Supported versions are: IDA >= 7.4 and IDA < 10.0.", version)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def is_supported_file_type():
|
||||
file_info = idaapi.get_inf_structure()
|
||||
if file_info.filetype not in SUPPORTED_FILE_TYPES:
|
||||
if get_filetype() not in SUPPORTED_FILE_TYPES:
|
||||
logger.error("-" * 80)
|
||||
logger.error(" Input file does not appear to be a supported file type.")
|
||||
logger.error(" ")
|
||||
@@ -76,8 +109,7 @@ def is_supported_file_type():
|
||||
|
||||
|
||||
def is_supported_arch_type():
|
||||
file_info = idaapi.get_inf_structure()
|
||||
if file_info.procname not in SUPPORTED_ARCH_TYPES or not any((file_info.is_32bit(), file_info.is_64bit())):
|
||||
if get_processor_name() not in SUPPORTED_ARCH_TYPES or not any((is_32bit(), is_64bit())):
|
||||
logger.error("-" * 80)
|
||||
logger.error(" Input file does not appear to target a supported architecture.")
|
||||
logger.error(" ")
|
||||
@@ -125,10 +157,10 @@ def collect_metadata(rules: List[Path]):
|
||||
md5 = get_file_md5()
|
||||
sha256 = get_file_sha256()
|
||||
|
||||
info: idaapi.idainfo = idaapi.get_inf_structure()
|
||||
if info.procname == "metapc" and info.is_64bit():
|
||||
procname = get_processor_name()
|
||||
if procname == "metapc" and is_64bit():
|
||||
arch = "x86_64"
|
||||
elif info.procname == "metapc" and info.is_32bit():
|
||||
elif procname == "metapc" and is_32bit():
|
||||
arch = "x86"
|
||||
else:
|
||||
arch = "unknown arch"
|
||||
|
||||
@@ -183,6 +183,7 @@ known_first_party = [
|
||||
"binaryninja",
|
||||
"flirt",
|
||||
"ghidra",
|
||||
"ida_ida",
|
||||
"ida_bytes",
|
||||
"ida_entry",
|
||||
"ida_funcs",
|
||||
|
||||
Reference in New Issue
Block a user