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
from .warp_enums import WARPContainerSearchItemKind
[docs]
class WarpUUID:
[docs]
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
[docs]
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
[docs]
class Source(WarpUUID):
def __repr__(self):
return f"<Source '{str(self)}'>"
[docs]
class BasicBlockGUID(WarpUUID):
def __repr__(self):
return f"<BasicBlockGUID '{str(self)}'>"
[docs]
class FunctionGUID(WarpUUID):
def __repr__(self):
return f"<FunctionGUID '{str(self)}'>"
[docs]
class ConstraintGUID(WarpUUID):
def __repr__(self):
return f"<ConstraintGUID '{str(self)}'>"
[docs]
class TypeGUID(WarpUUID):
def __repr__(self):
return f"<TypeGUID '{str(self)}'>"
[docs]
@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}>"
[docs]
@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)
[docs]
class WarpTarget:
[docs]
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)
[docs]
class WarpFunction:
[docs]
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)
[docs]
def get_symbol(self, function: Function) -> Symbol:
symbol_handle = warpcore.BNWARPFunctionGetSymbol(self.handle, function.handle)
return Symbol(symbol_handle)
[docs]
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
[docs]
@staticmethod
def get_matched(function: Function) -> Optional['WarpFunction']:
handle = warpcore.BNWARPGetMatchedFunction(function.handle)
if not handle:
return None
return WarpFunction(handle)
[docs]
def apply(self, function: Function):
warpcore.BNWARPFunctionApply(self.handle, function.handle)
[docs]
@staticmethod
def remove_matched(function: Function):
warpcore.BNWARPFunctionApply(None, function.handle)
[docs]
class WarpContainerSearchQuery:
[docs]
def __init__(self, query: str, offset: Optional[int] = None, limit: Optional[int] = None, source: Optional[Source] = None, source_tags: Optional[List[str]] = None):
self.query = query
self.source = source
self.offset = offset
self.limit = limit
offset_ptr = None
if offset is not None:
self._c_offset = ctypes.c_size_t(offset)
offset_ptr = ctypes.byref(self._c_offset)
limit_ptr = None
if limit is not None:
self._c_limit = ctypes.c_size_t(limit)
limit_ptr = ctypes.byref(self._c_limit)
source_ptr = None
if source is not None:
self._c_source = source.uuid
source_ptr = ctypes.byref(self._c_source)
source_tags_len = 0
source_tags_array_ptr = None
if source_tags is not None:
source_tags_ptr = (ctypes.c_char_p * len(source_tags))()
source_tags_len = len(source_tags)
for i in range(len(source_tags)):
source_tags_ptr[i] = source_tags[i].encode('utf-8')
source_tags_array_ptr = ctypes.cast(source_tags_ptr, ctypes.POINTER(ctypes.c_char_p))
self.handle = warpcore.BNWARPNewContainerSearchQuery(query, offset_ptr, limit_ptr, source_ptr, source_tags_array_ptr, source_tags_len)
def __del__(self):
if self.handle is not None:
warpcore.BNWARPFreeContainerSearchQueryReference(self.handle)
def __repr__(self):
# TODO: Display offset and limit in a pythonic way.
if self.source is None:
return f"<WarpContainerSearchQuery '{self.query}'>"
return f"<WarpContainerSearchQuery '{self.query}': '{self.source}'>"
[docs]
class WarpContainerSearchItem:
[docs]
def __init__(self, handle: warpcore.BNWARPContainerSearchItem):
self.handle = handle
def __del__(self):
if self.handle is not None:
warpcore.BNWARPFreeContainerSearchItemReference(self.handle)
@property
def kind(self) -> WARPContainerSearchItemKind:
return WARPContainerSearchItemKind(warpcore.BNWARPContainerSearchItemGetKind(self.handle))
@property
def source(self) -> Source:
return Source(warpcore.BNWARPContainerSearchItemGetSource(self.handle))
@property
def name(self) -> str:
return warpcore.BNWARPContainerSearchItemGetName(self.handle)
[docs]
def get_type(self, arch: Architecture) -> Optional[Type]:
ty = warpcore.BNWARPContainerSearchItemGetType(arch.handle, self.handle)
if not ty:
return None
return Type(ty)
@property
def function(self) -> Optional[WarpFunction]:
func = warpcore.BNWARPContainerSearchItemGetFunction(self.handle)
if not func:
return None
return WarpFunction(func)
def __repr__(self):
return f"<WarpContainerSearchItem '{self.name}': '{self.source}'>"
[docs]
class WarpContainerResponse:
[docs]
def __init__(self, items: List[WarpContainerSearchItem], offset: int, total: int):
self.items = items
self.offset = offset
self.total = total
def __iter__(self):
return iter(self.items)
def __len__(self):
return len(self.items)
def __repr__(self):
return f"<WarpContainerResponse items={len(self.items)} offset={self.offset} total={self.total}>"
[docs]
@staticmethod
def from_api(response: warpcore.BNWARPContainerSearchResponse) -> 'WarpContainerResponse':
try:
items = []
for i in range(response.count):
items.append(WarpContainerSearchItem(warpcore.BNWARPNewContainerSearchItemReference(response.items[i])))
return WarpContainerResponse(items=items, offset=response.offset, total=response.total)
finally:
warpcore.BNWARPFreeContainerSearchResponse(response)
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)
[docs]
class WarpContainer(metaclass=_WarpContainerMetaclass):
[docs]
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}'>"
[docs]
@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
[docs]
@staticmethod
def by_name(name: str) -> Optional['WarpContainer']:
for container in WarpContainer:
if container.name == name:
return container
return None
@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
[docs]
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)
[docs]
def commit_source(self, source: Source) -> bool:
return warpcore.BNWARPContainerCommitSource(self.handle, source.uuid)
[docs]
def is_source_uncommitted(self, source: Source) -> bool:
return warpcore.BNWARPContainerIsSourceWritable(self.handle, source.uuid)
[docs]
def is_source_writable(self, source: Source) -> bool:
return warpcore.BNWARPContainerIsSourceWritable(self.handle, source.uuid)
[docs]
def get_source_path(self, source: Source) -> Optional[str]:
return warpcore.BNWARPContainerGetSourcePath(self.handle, source.uuid)
[docs]
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)
[docs]
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)
[docs]
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)
[docs]
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)
[docs]
def fetch_functions(self, target: WarpTarget, guids: List[FunctionGUID], source_tags: Optional[List[str]] = None):
count = len(guids)
core_guids = (warpcore.BNWARPFunctionGUID * count)()
for i in range(count):
core_guids[i] = guids[i].uuid
if source_tags is None:
source_tags = []
source_tags_ptr = (ctypes.c_char_p * len(source_tags))()
source_tags_len = len(source_tags)
for i in range(len(source_tags)):
source_tags_ptr[i] = source_tags[i].encode('utf-8')
source_tags_array_ptr = ctypes.cast(source_tags_ptr, ctypes.POINTER(ctypes.c_char_p))
warpcore.BNWARPContainerFetchFunctions(self.handle, target.handle, source_tags_array_ptr, source_tags_len, core_guids, count)
[docs]
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
[docs]
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
[docs]
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
[docs]
def get_type_with_guid(self, arch: Architecture, source: Source, guid: TypeGUID) -> Optional[Type]:
ty = warpcore.BNWARPContainerGetTypeWithGUID(arch.handle, self.handle, source.uuid, guid.uuid)
if not ty:
return None
return Type(ty)
[docs]
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
[docs]
def search(self, query: WarpContainerSearchQuery) -> Optional[WarpContainerResponse]:
response = warpcore.BNWARPContainerSearch(self.handle, query.handle)
if not response:
return None
return WarpContainerResponse.from_api(response.contents)
[docs]
def run_matcher(view: BinaryView):
warpcore.BNWARPRunMatcher(view.handle)
[docs]
def is_instruction_variant(function: LowLevelILFunction, variant: LowLevelILInstruction) -> bool:
return warpcore.BNWARPIsLiftedInstructionVariant(function.handle, variant.instr_index)
[docs]
def is_instruction_blacklisted(function: LowLevelILFunction, variant: LowLevelILInstruction) -> bool:
return warpcore.BNWARPIsLiftedInstructionBlacklisted(function.handle, variant.instr_index)
[docs]
def is_instruction_computed_variant(function: LowLevelILFunction, variant: LowLevelILInstruction) -> bool:
"""
Checks to see if the instruction is variant due to some computed value. **Must use LLIL.**
"""
return warpcore.BNWARPIsLowLevelInstructionComputedVariant(function.handle, variant.instr_index)
[docs]
def get_function_guid(function: Function) -> Optional[FunctionGUID]:
guid = warpcore.BNWARPUUID()
if not warpcore.BNWARPGetAnalysisFunctionGUID(function.handle, guid):
return None
return FunctionGUID(guid)
[docs]
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