diff options
Diffstat (limited to 'plugins/warp/api/python/warp.py')
| -rw-r--r-- | plugins/warp/api/python/warp.py | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/plugins/warp/api/python/warp.py b/plugins/warp/api/python/warp.py new file mode 100644 index 00000000..e0500215 --- /dev/null +++ b/plugins/warp/api/python/warp.py @@ -0,0 +1,384 @@ +import ctypes +import dataclasses +import uuid +from typing import List, Optional, Union + +import binaryninja +from binaryninja import BinaryView, Function, BasicBlock, Architecture, Platform, Type, Symbol, LowLevelILInstruction, LowLevelILFunction +from binaryninja._binaryninjacore import BNFreeString, BNAllocString, BNType + +from . import _warpcore as warpcore + + +class WarpUUID: + def __init__(self, _uuid: Union[warpcore.BNWARPUUID, str, uuid.UUID]): + if isinstance(_uuid, str): + _uuid = uuid.UUID(_uuid) + if isinstance(_uuid, uuid.UUID): + uuid_bytes = _uuid.bytes + _uuid = warpcore.BNWARPUUID() + _uuid.uuid = (ctypes.c_ubyte * 16).from_buffer_copy(uuid_bytes) + elif isinstance(_uuid, warpcore.BNWARPUUID): + # We must create a copy! + new_uuid = warpcore.BNWARPUUID() + new_uuid.uuid = (ctypes.c_ubyte * 16).from_buffer_copy(_uuid.uuid) + _uuid = new_uuid + self._uuid = _uuid + + def to_string(self) -> str: + return warpcore.BNWARPUUIDGetString(self._uuid) + + def __str__(self): + return self.to_string() + + def __repr__(self): + return f"<WarpUUID '{str(self)}'>" + + def __hash__(self): + # Hash based on the UUID bytes + return hash(bytes(self._uuid.uuid)) + + def __eq__(self, other): + if not isinstance(other, WarpUUID): + return False + return warpcore.BNWARPUUIDEqual(self._uuid, other._uuid) + + @property + def uuid(self): + return self._uuid + + +class Source(WarpUUID): + def __repr__(self): + return f"<Source '{str(self)}'>" + + +class BasicBlockGUID(WarpUUID): + def __repr__(self): + return f"<BasicBlockGUID '{str(self)}'>" + + +class FunctionGUID(WarpUUID): + def __repr__(self): + return f"<FunctionGUID '{str(self)}'>" + + +class ConstraintGUID(WarpUUID): + def __repr__(self): + return f"<ConstraintGUID '{str(self)}'>" + + +class TypeGUID(WarpUUID): + def __repr__(self): + return f"<TypeGUID '{str(self)}'>" + + +@dataclasses.dataclass +class WarpFunctionComment: + text: str + offset: int + + def __str__(self): + return repr(self) + + def __repr__(self): + return f"<WarpFunctionComment '{self.text}': {self.offset:#x}>" + + @staticmethod + def from_api(comment: warpcore.BNWARPFunctionComment) -> 'WarpFunctionComment': + return WarpFunctionComment( + text=comment.text.decode('utf-8'), + offset=comment.offset + ) + +@dataclasses.dataclass +class WarpConstraint: + guid: ConstraintGUID + offset: Optional[int] + + def __str__(self): + return repr(self) + + def __repr__(self): + if self.offset is None: + return f"<WarpConstraint '{self.guid}'>" + return f"<WarpConstraint '{self.guid}': {self.offset:#x}>" + + @staticmethod + def from_api(constraint: warpcore.BNWARPConstraint) -> 'WarpConstraint': + if constraint.offset == -1: + return WarpConstraint(guid=ConstraintGUID(constraint.guid), offset=None) + return WarpConstraint(guid=ConstraintGUID(constraint.guid), offset=constraint.offset) + +class WarpTarget: + def __init__(self, handle: Union[warpcore.BNWARPTarget, Platform]): + if isinstance(handle, Platform): + self.handle = warpcore.BNWARPGetTarget(handle.handle) + else: + self.handle = handle + + def __del__(self): + if self.handle is not None: + warpcore.BNWARPFreeTargetReference(self.handle) + + @staticmethod + def from_platform(platform: Platform) -> Optional['WarpTarget']: + handle = warpcore.BNWARPGetTarget(platform.handle) + if not handle: + return None + return WarpTarget(handle) + + +class WarpFunction: + def __init__(self, handle: Union[warpcore.BNWARPFunction, Function]): + if isinstance(handle, Function): + self.handle = warpcore.BNWARPGetFunction(handle.handle) + else: + self.handle = handle + def __del__(self): + if self.handle is not None: + warpcore.BNWARPFreeFunctionReference(self.handle) + + def __repr__(self): + return f"<WarpFunction '{self.name}': '{self.guid}'>" + + @property + def guid(self) -> FunctionGUID: + return FunctionGUID(warpcore.BNWARPFunctionGetGUID(self.handle)) + + @property + def name(self) -> str: + return warpcore.BNWARPFunctionGetSymbolName(self.handle) + + def get_symbol(self, function: Function) -> Symbol: + symbol_handle = warpcore.BNWARPFunctionGetSymbol(self.handle, function.handle) + return Symbol(symbol_handle) + + def get_type(self, function: Function) -> Optional[Type]: + type_handle = warpcore.BNWARPFunctionGetType(self.handle, function.handle) + if not type_handle: + return None + return Type(type_handle) + + @property + def constraints(self) -> List[WarpConstraint]: + count = ctypes.c_size_t() + constraints = warpcore.BNWARPFunctionGetConstraints(self.handle, count) + if not constraints: + return [] + result = [] + for i in range(count.value): + result.append(WarpConstraint.from_api(constraints[i])) + warpcore.BNWARPFreeConstraintList(constraints, count.value) + return result + + @property + def comments(self) -> List[WarpFunctionComment]: + count = ctypes.c_size_t() + comments = warpcore.BNWARPFunctionGetComments(self.handle, count) + if not comments: + return [] + result = [] + for i in range(count.value): + result.append(WarpFunctionComment.from_api(comments[i])) + warpcore.BNWARPFreeFunctionCommentList(comments, count.value) + return result + + @staticmethod + def get_matched(function: Function) -> Optional['WarpFunction']: + handle = warpcore.BNWARPGetMatchedFunction(function.handle) + if not handle: + return None + return WarpFunction(handle) + + def apply(self, function: Function): + warpcore.BNWARPFunctionApply(self.handle, function.handle) + + +class _WarpContainerMetaclass(type): + def __iter__(self): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + containers = warpcore.BNWARPGetContainers(count) + try: + for i in range(0, count.value): + yield WarpContainer(warpcore.BNWARPNewContainerReference(containers[i])) + finally: + warpcore.BNWARPFreeContainerList(containers, count.value) + + def __getitem__(self, value): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + containers = warpcore.BNWARPGetContainers(count) + try: + for i in range(0, count.value): + container = WarpContainer(warpcore.BNWARPNewContainerReference(containers[i])) + if container.name == str(value): + return container + raise KeyError(f"'{value}' is not a valid container name") + finally: + warpcore.BNWARPFreeContainerList(containers, count.value) + + +class WarpContainer(metaclass=_WarpContainerMetaclass): + def __init__(self, handle: warpcore.BNWARPContainer): + self.handle = handle + + def __del__(self): + if self.handle is not None: + warpcore.BNWARPFreeContainerReference(self.handle) + + def __repr__(self): + return f"<WarpContainer '{self.name}'>" + + @staticmethod + def all() -> List['WarpContainer']: + count = ctypes.c_size_t() + containers = warpcore.BNWARPGetContainers(count) + if not containers: + return [] + result = [] + for i in range(count.value): + result.append(WarpContainer(warpcore.BNWARPNewContainerReference(containers[i]))) + warpcore.BNWARPFreeContainerList(containers, count.value) + return result + + @property + def name(self) -> str: + return warpcore.BNWARPContainerGetName(self.handle) + + @property + def sources(self) -> List[Source]: + count = ctypes.c_size_t() + sources = warpcore.BNWARPContainerGetSources(self.handle, count) + if not sources: + return [] + result = [] + for i in range(count.value): + result.append(Source(sources[i])) + warpcore.BNWARPFreeUUIDList(sources, count.value) + return result + + def add_source(self, source_path: str) -> Optional[Source]: + source = warpcore.BNWARPUUID() + if not warpcore.BNWARPContainerAddSource(self.handle, source_path, source): + return None + return Source(source) + + def commit_source(self, source: Source) -> bool: + return warpcore.BNWARPContainerCommitSource(self.handle, source.uuid) + + def is_source_uncommitted(self, source: Source) -> bool: + return warpcore.BNWARPContainerIsSourceWritable(self.handle, source.uuid) + + def is_source_writable(self, source: Source) -> bool: + return warpcore.BNWARPContainerIsSourceWritable(self.handle, source.uuid) + + def get_source_path(self, source: Source) -> Optional[str]: + return warpcore.BNWARPContainerGetSourcePath(self.handle, source.uuid) + + def add_functions(self, target: WarpTarget, source: Source, functions: List[Function]) -> bool: + count = len(functions) + core_funcs = (ctypes.POINTER(warpcore.BNWARPFunction) * count)() + for i in range(count): + core_funcs[i] = functions[i].handle + return warpcore.BNWARPContainerAddFunctions(self.handle, target.handle, source.uuid, core_funcs, count) + + def add_types(self, view: BinaryView, source: Source, types: List[Type]) -> bool: + count = len(types) + core_types = (ctypes.POINTER(BNType) * count)() + for i in range(count): + core_types[i] = types[i].handle + return warpcore.BNWARPContainerAddTypes(view.handle, self.handle, source.uuid, core_types, count) + + def remove_functions(self, target: WarpTarget, source: Source, functions: List[Function]) -> bool: + count = len(functions) + core_funcs = (ctypes.POINTER(warpcore.BNWARPFunction) * count)() + for i in range(count): + core_funcs[i] = functions[i].handle + return warpcore.BNWARPContainerRemoveFunctions(self.handle, target.handle, source.uuid, core_funcs, count) + + def remove_types(self, source: Source, guids: List[TypeGUID]) -> bool: + count = len(guids) + core_guids = (ctypes.POINTER(warpcore.BNWARPTypeGUID) * count)() + for i in range(count): + core_guids[i] = guids[i].uuid + return warpcore.BNWARPContainerRemoveTypes(self.handle, source.uuid, core_guids, count) + + def get_sources_with_function_guid(self, target: WarpTarget, guid: FunctionGUID) -> List[Source]: + count = ctypes.c_size_t() + sources = warpcore.BNWARPContainerGetSourcesWithFunctionGUID(self.handle, target.handle, guid.uuid, count) + if not sources: + return [] + result = [] + for i in range(count.value): + result.append(Source(sources[i])) + warpcore.BNWARPFreeUUIDList(sources, count.value) + return result + + def get_sources_with_type_guid(self, guid: TypeGUID) -> List[Source]: + count = ctypes.c_size_t() + sources = warpcore.BNWARPContainerGetSourcesWithTypeGUID(self.handle, guid.uuid, count) + if not sources: + return [] + result = [] + for i in range(count.value): + result.append(Source(sources[i])) + warpcore.BNWARPFreeUUIDList(sources, count.value) + return result + + def get_functions_with_guid(self, target: WarpTarget, source: Source, guid: FunctionGUID) -> List[Function]: + count = ctypes.c_size_t() + funcs = warpcore.BNWARPContainerGetFunctionsWithGUID(self.handle, target.handle, source.uuid, guid.uuid, count) + if not funcs: + return [] + result = [] + for i in range(count.value): + result.append(WarpFunction(warpcore.BNWARPNewFunctionReference(funcs[i]))) + warpcore.BNWARPFreeFunctionList(funcs, count.value) + return result + + def get_type_with_guid(self, arch: Architecture, source: Source, guid: TypeGUID) -> Optional[Type]: + ty = warpcore.BNWARPContainerGetTypeWithGUID(self.handle, arch.handle, source.uuid, guid.uuid) + if not ty: + return None + return Type(ty) + + def get_type_guids_with_name(self, source: Source, name: str) -> List[TypeGUID]: + count = ctypes.c_size_t() + guids = warpcore.BNWARPContainerGetTypeGUIDsWithName(self.handle, source.uuid, name, count) + if not guids: + return [] + result = [] + for i in range(count.value): + result.append(TypeGUID(guids[i])) + warpcore.BNWARPFreeUUIDList(guids, count.value) + return result + + +def run_matcher(view: BinaryView): + warpcore.BNWARPRunMatcher(view.handle) + +def is_instruction_variant(function: LowLevelILFunction, variant: LowLevelILInstruction) -> bool: + return warpcore.BNWARPIsLiftedInstructionVariant(function.handle, variant.instr_index) + +def is_instruction_blacklisted(function: LowLevelILFunction, variant: LowLevelILInstruction) -> bool: + return warpcore.BNWARPIsLiftedInstructionBlacklisted(function.handle, variant.instr_index) + +def get_function_guid(function: Function) -> Optional[FunctionGUID]: + guid = warpcore.BNWARPUUID() + if not warpcore.BNWARPGetAnalysisFunctionGUID(function.handle, guid): + return None + return FunctionGUID(guid) + + +def get_basic_block_guid(basic_block: BasicBlock) -> Optional[BasicBlockGUID]: + # TODO: I believe this won't work for HLIL: https://github.com/Vector35/binaryninja-api/issues/6998 + if basic_block.is_il: + basic_block = basic_block.source_block + guid = warpcore.BNWARPUUID() + if not warpcore.BNWARPGetBasicBlockGUID(basic_block.handle, guid): + return None + return BasicBlockGUID(guid) + +# TODO: Magic matched_function, possible_functions
\ No newline at end of file |
