diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2025-12-23 13:12:02 -0700 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2026-05-22 16:30:56 -0400 |
| commit | 8d621c51b2797fda7b1dc22243dde611cfc04f68 (patch) | |
| tree | ba5e6a90e644d21d13e75dabcbd0cc747444443a /python | |
| parent | 08e34ac325743085911f96b62c81d9a1f2127806 (diff) | |
Refactor calling conventions to support correct representation of structures
Diffstat (limited to 'python')
| -rw-r--r-- | python/architecture.py | 10 | ||||
| -rw-r--r-- | python/binaryview.py | 18 | ||||
| -rw-r--r-- | python/callingconvention.py | 968 | ||||
| -rw-r--r-- | python/examples/pseudo_python.py | 25 | ||||
| -rw-r--r-- | python/function.py | 200 | ||||
| -rw-r--r-- | python/highlevelil.py | 42 | ||||
| -rw-r--r-- | python/mediumlevelil.py | 262 | ||||
| -rw-r--r-- | python/platform.py | 12 | ||||
| -rw-r--r-- | python/types.py | 394 | ||||
| -rw-r--r-- | python/variable.py | 238 |
10 files changed, 1903 insertions, 266 deletions
diff --git a/python/architecture.py b/python/architecture.py index c11007df..f00b1cbe 100644 --- a/python/architecture.py +++ b/python/architecture.py @@ -1051,7 +1051,7 @@ class Architecture(metaclass=_ArchitectureMetaClass): result = {} try: for i in range(0, count.value): - obj = callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i])) + obj = callingconvention.CoreCallingConvention(handle=core.BNNewCallingConventionReference(cc[i])) result[obj.name] = obj finally: core.BNFreeCallingConventionList(cc, count.value) @@ -2613,7 +2613,7 @@ class Architecture(metaclass=_ArchitectureMetaClass): cc_handle = core.BNGetArchitectureDefaultCallingConvention(self.handle) if cc_handle is None: return None - return callingconvention.CallingConvention(handle=cc_handle) + return callingconvention.CoreCallingConvention(handle=cc_handle) @default_calling_convention.setter def default_calling_convention(self, cc: 'callingconvention.CallingConvention'): @@ -2633,7 +2633,7 @@ class Architecture(metaclass=_ArchitectureMetaClass): cc_handle = core.BNGetArchitectureCdeclCallingConvention(self.handle) if cc_handle is None: return None - return callingconvention.CallingConvention(handle=cc_handle) + return callingconvention.CoreCallingConvention(handle=cc_handle) @cdecl_calling_convention.setter def cdecl_calling_convention(self, cc: 'callingconvention.CallingConvention'): @@ -2653,7 +2653,7 @@ class Architecture(metaclass=_ArchitectureMetaClass): cc_handle = core.BNGetArchitectureStdcallCallingConvention(self.handle) if cc_handle is None: return None - return callingconvention.CallingConvention(handle=cc_handle) + return callingconvention.CoreCallingConvention(handle=cc_handle) @stdcall_calling_convention.setter def stdcall_calling_convention(self, cc: 'callingconvention.CallingConvention'): @@ -2673,7 +2673,7 @@ class Architecture(metaclass=_ArchitectureMetaClass): cc_handle = core.BNGetArchitectureFastcallCallingConvention(self.handle) if cc_handle is None: return None - return callingconvention.CallingConvention(handle=cc_handle) + return callingconvention.CoreCallingConvention(handle=cc_handle) @fastcall_calling_convention.setter def fastcall_calling_convention(self, cc: 'callingconvention.CallingConvention'): diff --git a/python/binaryview.py b/python/binaryview.py index 039c94ee..5594b066 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -11047,6 +11047,24 @@ to a the type "tagRECT" found in the typelibrary "winX64common" core.free_string(string) return result, StringType(string_type.value) + def deref_parameter_named_type_references(self, params: List['_types.FunctionParameter']): + result = [] + for param in params: + if param.type is None: + ty = None + else: + ty = param.type.deref_named_type_reference(self).with_confidence(param.type.confidence) + result.append(_types.FunctionParameter(ty, param.name, param.location, param.location_source)) + return result + + def deref_return_value_named_type_references(self, value: '_types.ReturnValue'): + if value.type is None: + ty = None + else: + ty = value.type.deref_named_type_reference(self).with_confidence(value.type.confidence) + return _types.ReturnValue(ty, value.location) + + class BinaryReader: """ ``class BinaryReader`` is a convenience class for reading binary data. diff --git a/python/callingconvention.py b/python/callingconvention.py index 94aeb699..4f17d1cf 100644 --- a/python/callingconvention.py +++ b/python/callingconvention.py @@ -20,7 +20,8 @@ import traceback import ctypes -from typing import Optional, Union +from typing import Optional, Union, List, Dict, Tuple +from dataclasses import dataclass # Binary Ninja components from . import _binaryninjacore as core @@ -28,12 +29,62 @@ from .log import log_error_for_exception from . import variable from . import function from . import architecture +from . import types +from . import binaryview FunctionOrILFunction = Union["binaryninja.function.Function", "binaryninja.lowlevelil.LowLevelILFunction", "binaryninja.mediumlevelil.MediumLevelILFunction", "binaryninja.highlevelil.HighLevelILFunction"] +@dataclass +class CallLayout: + parameters: List['types.ValueLocation'] + return_value: Optional['types.ValueLocation'] + stack_adjustment: int + reg_stack_adjustments: Dict['architecture.RegisterIndex', int] + + @staticmethod + def _from_core_struct(struct: core.BNCallLayout, func: Optional['function.Function'] = None) -> 'CallLayout': + params = [] + if func is None: + arch = None + else: + arch = func.arch + for i in range(struct.parameterCount): + params.append(types.ValueLocation._from_core_struct(struct.parameters[i], arch)) + if struct.returnValueValid: + return_value = types.ValueLocation._from_core_struct(struct.returnValue, arch) + else: + return_value = None + stack_adjust = struct.stackAdjustment + reg_stack_adjust = dict() + for i in range(struct.registerStackAdjustmentCount): + reg = architecture.RegisterIndex(struct.registerStackAdjustmentRegisters[i]) + reg_stack_adjust[reg] = struct.registerStackAdjustmentAmounts[i] + return CallLayout(params, return_value, stack_adjust, reg_stack_adjust) + + def _to_core_struct(self): + struct = core.BNCallLayout() + struct.parameters = (core.BNValueLocation * len(self.parameters))() + struct.parameterCount = len(self.parameters) + for i in range(len(self.parameters)): + struct.parameters[i] = self.parameters[i]._to_core_struct() + if self.return_value is None: + struct.returnValueValid = False + else: + struct.returnValue = self.return_value._to_core_struct() + struct.returnValueValid = True + struct.stackAdjustment = self.stack_adjustment + struct.registerStackAdjustmentRegisters = (ctypes.c_uint * len(self.reg_stack_adjustments))() + struct.registerStackAdjustmentAmounts = (ctypes.c_int * len(self.reg_stack_adjustments))() + struct.registerStackAdjustmentCount = len(self.reg_stack_adjustments) + for i, (reg, amount) in enumerate(self.reg_stack_adjustments.items()): + struct.registerStackAdjustmentRegisters[i] = reg + struct.registerStackAdjustmentAmounts[i] = amount + return struct + + class CallingConvention: name = None caller_saved_regs = [] @@ -52,8 +103,13 @@ class CallingConvention: float_return_reg = None global_pointer_reg = None implicitly_defined_regs = [] + stack_args_naturally_aligned = False _registered_calling_conventions = [] + _pending_value_locations = {} + _pending_value_location_lists = {} + _pending_reg_stack_adjustment_reg_lists = {} + _pending_reg_stack_adjustment_amount_lists = {} def __init__( self, arch: Optional['architecture.Architecture'] = None, name: Optional[str] = None, handle=None, @@ -117,111 +173,43 @@ class CallingConvention: self._cb.getParameterVariableForIncomingVariable = self._cb.getParameterVariableForIncomingVariable.__class__( self._get_parameter_var_for_incoming_var ) + self._cb.isReturnTypeRegisterCompatible = self._cb.isReturnTypeRegisterCompatible.__class__( + self._is_return_type_reg_compatible + ) + self._cb.getIndirectReturnValueLocation = self._cb.getIndirectReturnValueLocation.__class__( + self._get_indirect_return_value_location + ) + self._cb.getReturnedIndirectReturnValuePointer = self._cb.getReturnedIndirectReturnValuePointer.__class__( + self._get_returned_indirect_return_value_pointer + ) + self._cb.isArgumentTypeRegisterCompatible = self._cb.isArgumentTypeRegisterCompatible.__class__( + self._is_arg_type_reg_compatible + ) + self._cb.isNonRegisterArgumentIndirect = self._cb.isNonRegisterArgumentIndirect.__class__( + self._is_non_reg_arg_indirect + ) + self._cb.areStackArgumentsNaturallyAligned = self._cb.areStackArgumentsNaturallyAligned.__class__( + self._are_stack_args_naturally_aligned + ) + self._cb.getCallLayout = self._cb.getCallLayout.__class__(self._get_call_layout) + self._cb.freeCallLayout = self._cb.freeCallLayout.__class__(self._free_call_layout) + self._cb.getReturnValueLocation = self._cb.getReturnValueLocation.__class__(self._get_return_value_location) + self._cb.freeValueLocation = self._cb.freeValueLocation.__class__(self._free_value_location) + self._cb.getParameterLocations = self._cb.getParameterLocations.__class__(self._get_parameter_locations) + self._cb.freeParameterLocations = self._cb.freeParameterLocations.__class__(self._free_parameter_locations) + self._cb.getStackAdjustmentForLocations = self._cb.getStackAdjustmentForLocations.__class__( + self._get_stack_adjustment_for_locations + ) + self._cb.getRegisterStackAdjustments = self._cb.getRegisterStackAdjustments.__class__( + self._get_register_stack_adjustments + ) + self._cb.freeRegisterStackAdjustments = self._cb.freeRegisterStackAdjustments.__class__( + self._free_register_stack_adjustments + ) _handle = core.BNCreateCallingConvention(arch.handle, name, self._cb) self.__class__._registered_calling_conventions.append(self) else: _handle = handle - self.arch = architecture.CoreArchitecture._from_cache(core.BNGetCallingConventionArchitecture(_handle)) - self.__dict__["name"] = core.BNGetCallingConventionName(_handle) - self.__dict__["arg_regs_share_index"] = core.BNAreArgumentRegistersSharedIndex(_handle) - self.__dict__["arg_regs_for_varargs"] = core.BNAreArgumentRegistersUsedForVarArgs(_handle) - self.__dict__["stack_reserved_for_arg_regs"] = core.BNIsStackReservedForArgumentRegisters(_handle) - self.__dict__["stack_adjusted_on_return"] = core.BNIsStackAdjustedOnReturn(_handle) - self.__dict__["eligible_for_heuristics"] = core.BNIsEligibleForHeuristics(_handle) - - count = ctypes.c_ulonglong() - regs = core.BNGetCallerSavedRegisters(_handle, count) - assert regs is not None, "core.BNGetCallerSavedRegisters returned None" - result = [] - arch = self.arch - for i in range(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs) - self.__dict__["caller_saved_regs"] = result - - count = ctypes.c_ulonglong() - regs = core.BNGetCalleeSavedRegisters(_handle, count) - assert regs is not None, "core.BNGetCalleeSavedRegisters returned None" - result = [] - arch = self.arch - for i in range(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs) - self.__dict__["callee_saved_regs"] = result - - count = ctypes.c_ulonglong() - regs = core.BNGetIntegerArgumentRegisters(_handle, count) - assert regs is not None, "core.BNGetIntegerArgumentRegisters returned None" - result = [] - arch = self.arch - for i in range(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs) - self.__dict__["int_arg_regs"] = result - - count = ctypes.c_ulonglong() - regs = core.BNGetFloatArgumentRegisters(_handle, count) - assert regs is not None, "core.BNGetFloatArgumentRegisters returned None" - result = [] - arch = self.arch - for i in range(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs) - self.__dict__["float_arg_regs"] = result - - count = ctypes.c_ulonglong() - regs = core.BNGetRequiredArgumentRegisters(handle, count) - assert regs is not None, "core.BNGetRequiredArgumentRegisters returned None" - result = [] - arch = self.arch - for i in range(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs) - self.__dict__["required_arg_regs"] = result - - count = ctypes.c_ulonglong() - regs = core.BNGetRequiredClobberedRegisters(handle, count) - assert regs is not None, "core.BNGetRequiredClobberedRegisters returned None" - result = [] - arch = self.arch - for i in range(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs) - self.__dict__["required_clobbered_regs"] = result - - reg = core.BNGetIntegerReturnValueRegister(_handle) - if reg == 0xffffffff: - self.__dict__["int_return_reg"] = None - else: - self.__dict__["int_return_reg"] = self.arch.get_reg_name(reg) - - reg = core.BNGetHighIntegerReturnValueRegister(_handle) - if reg == 0xffffffff: - self.__dict__["high_int_return_reg"] = None - else: - self.__dict__["high_int_return_reg"] = self.arch.get_reg_name(reg) - - reg = core.BNGetFloatReturnValueRegister(_handle) - if reg == 0xffffffff: - self.__dict__["float_return_reg"] = None - else: - self.__dict__["float_return_reg"] = self.arch.get_reg_name(reg) - - reg = core.BNGetGlobalPointerRegister(_handle) - if reg == 0xffffffff: - self.__dict__["global_pointer_reg"] = None - else: - self.__dict__["global_pointer_reg"] = self.arch.get_reg_name(reg) - - count = ctypes.c_ulonglong() - regs = core.BNGetImplicitlyDefinedRegisters(_handle, count) - assert regs is not None, "core.BNGetImplicitlyDefinedRegisters returned None" - result = [] - arch = self.arch - for i in range(0, count.value): - result.append(arch.get_reg_name(regs[i])) - core.BNFreeRegisterList(regs) - self.__dict__["implicitly_defined_regs"] = result assert _handle is not None self.handle = _handle self.confidence = confidence @@ -492,9 +480,309 @@ class CallingConvention: result[0].index = in_var[0].index result[0].storage = in_var[0].storage + def _is_return_type_reg_compatible(self, ctxt, view, type): + try: + if type: + if view: + view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + else: + view_obj = None + type_obj = types.Type.create(handle=core.BNNewTypeReference(type)) + return self.is_return_type_reg_compatible(view_obj, type_obj) + else: + return False + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._is_return_type_reg_compatible") + return False + + def _get_indirect_return_value_location(self, ctxt, out_var): + try: + out_var[0] = self.get_indirect_return_value_location().to_BNVariable() + except: + log_error_for_exception( + "Unhandled Python exception in CallingConvention._get_indirect_return_value_location") + + def _get_returned_indirect_return_value_pointer(self, ctxt, out_var): + try: + result = self.get_returned_indirect_return_value_pointer() + if result is None: + return False + out_var[0] = result.to_BNVariable() + return True + except: + log_error_for_exception( + "Unhandled Python exception in CallingConvention._get_returned_indirect_return_value_pointer") + return False + + def _is_arg_type_reg_compatible(self, ctxt, view, type): + try: + if type: + if view: + view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + else: + view_obj = None + type_obj = types.Type.create(handle=core.BNNewTypeReference(type)) + return self.is_arg_type_reg_compatible(view_obj, type_obj) + else: + return False + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._is_arg_type_reg_compatible") + return False + + def _is_non_reg_arg_indirect(self, ctxt, view, type): + try: + if view: + view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + else: + view_obj = None + if type: + type_obj = types.Type.create(handle=core.BNNewTypeReference(type)) + return self.is_non_reg_arg_indirect(view_obj, type_obj) + else: + return self.is_non_reg_arg_indirect(view_obj, None) + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._is_non_reg_arg_indirect") + return False + + def _are_stack_args_naturally_aligned(self, ctxt): + try: + return self.__class__.stack_args_naturally_aligned + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._are_stack_args_naturally_aligned") + return False + + def _get_call_layout( + self, ctxt, view, ret_value, params, param_count, has_permitted_regs, permitted_regs, + permitted_reg_count, out_layout + ): + try: + if view: + view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + else: + view_obj = None + if ret_value: + ret_value_obj = types.ReturnValue._from_core_struct(ret_value[0]) + else: + ret_value_obj = None + param_objs = [] + for i in range(param_count): + param_objs.append(types.FunctionParameter._from_core_struct(params[i])) + if has_permitted_regs: + reg_objs = [] + for i in range(permitted_reg_count): + reg_objs.append(architecture.RegisterIndex(permitted_regs[i])) + else: + reg_objs = None + layout = self.get_call_layout(view_obj, ret_value_obj, param_objs, permitted_regs = reg_objs) + + result = layout._to_core_struct() + + param_ptr = ctypes.cast(result.parameters, ctypes.c_void_p) + self._pending_value_location_lists[param_ptr.value] = (param_ptr.value, result.parameters) + + if result.returnValueValid: + ret_ptr = ctypes.cast(result.returnValue.components, ctypes.c_void_p) + self._pending_value_locations[ret_ptr.value] = (ret_ptr.value, result.returnValue) + + reg_ptr = ctypes.cast(result.registerStackAdjustmentRegisters, ctypes.c_void_p) + self._pending_reg_stack_adjustment_reg_lists[reg_ptr.value] = (reg_ptr.value, result.registerStackAdjustmentRegisters) + amount_ptr = ctypes.cast(result.registerStackAdjustmentAmounts, ctypes.c_void_p) + self._pending_reg_stack_adjustment_amount_lists[amount_ptr.value] = (amount_ptr.value, result.registerStackAdjustmentAmounts) + + out_layout[0] = result + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._get_call_layout") + result = core.BNCallLayout() + result.parameterCount = 0 + result.returnValueValid = False + result.stackAdjustment = 0 + result.registerStackAdjustmentCount = 0 + out_layout[0] = result + + def _free_call_layout(self, ctxt, layout_ptr): + try: + layout = layout_ptr[0] + param_ptr = ctypes.cast(layout.parameters, ctypes.c_void_p) + if param_ptr.value is not None: + if param_ptr.value not in self._pending_value_location_lists: + raise ValueError("freeing parameter location list that wasn't allocated") + del self._pending_value_location_lists[param_ptr.value] + + if layout.returnValueValid: + ret_ptr = ctypes.cast(layout.returnValue.components, ctypes.c_void_p) + if ret_ptr.value is not None: + if ret_ptr.value not in self._pending_value_locations: + raise ValueError("freeing return value location that wasn't allocated") + del self._pending_value_locations[ret_ptr.value] + + reg_ptr = ctypes.cast(layout.registerStackAdjustmentRegisters, ctypes.c_void_p) + if reg_ptr.value is not None: + if reg_ptr.value not in self._pending_reg_stack_adjustment_reg_lists: + raise ValueError("freeing register list that wasn't allocated") + del self._pending_reg_stack_adjustment_reg_lists[reg_ptr.value] + + amount_ptr = ctypes.cast(layout.registerStackAdjustmentAmounts, ctypes.c_void_p) + if amount_ptr.value is not None: + if amount_ptr.value not in self._pending_reg_stack_adjustment_amount_lists: + raise ValueError("freeing adjustment list that wasn't allocated") + del self._pending_reg_stack_adjustment_amount_lists[amount_ptr.value] + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._free_call_layout") + + def _get_return_value_location(self, ctxt, view, ret_value, out_location): + try: + if view: + view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + else: + view_obj = None + ret = types.ReturnValue._from_core_struct(ret_value[0]) + location = self.get_return_value_location(view_obj, ret) + if location is None: + location = types.ValueLocation([]) + result = location._to_core_struct() + result_ptr = ctypes.cast(result.components, ctypes.c_void_p) + self._pending_value_locations[result_ptr.value] = (result_ptr.value, result) + out_location[0] = result + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._get_return_value_location") + result = core.BNValueLocation() + result.count = 0 + result.components = None + out_location[0] = result + + def _free_value_location(self, ctxt, location_ptr): + try: + location = location_ptr[0] + loc_ptr = ctypes.cast(location.components, ctypes.c_void_p) + if loc_ptr.value is not None: + if loc_ptr.value not in self._pending_value_locations: + raise ValueError("freeing value location that wasn't allocated") + del self._pending_value_locations[loc_ptr.value] + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._free_value_location") + + def _get_parameter_locations( + self, ctxt, view, ret_value, params, param_count, has_permitted_regs, permitted_regs, permitted_reg_count, + out_location_count + ): + try: + if view: + view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + else: + view_obj = None + if ret_value: + ret_value_obj = types.ValueLocation._from_core_struct(ret_value[0]) + else: + ret_value_obj = None + param_objs = [] + for i in range(param_count): + param_objs.append(types.FunctionParameter._from_core_struct(params[i])) + if has_permitted_regs: + reg_objs = [] + for i in range(permitted_reg_count): + reg_objs.append(architecture.RegisterIndex(permitted_regs[i])) + else: + reg_objs = None + locations = self.get_parameter_locations(view_obj, ret_value_obj, param_objs, permitted_regs = reg_objs) + + out_location_count[0] = len(locations) + result = (core.BNValueLocation * len(locations))() + for i, location in enumerate(locations): + result[i] = location._to_core_struct() + + result_ptr = ctypes.cast(result, ctypes.c_void_p) + self._pending_value_location_lists[result_ptr.value] = (result_ptr.value, result) + + return result_ptr.value + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._get_parameter_locations") + out_location_count[0] = 0 + return None + + def _free_parameter_locations(self, ctxt, locations, count): + try: + location_ptr = ctypes.cast(locations, ctypes.c_void_p) + if location_ptr.value is not None: + if location_ptr.value not in self._pending_value_location_lists: + raise ValueError("freeing parameter location list that wasn't allocated") + del self._pending_value_location_lists[location_ptr.value] + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._free_parameter_locations") + + def _get_stack_adjustment_for_locations(self, ctxt, view, ret_value, locations, type_list, param_count): + try: + if view: + view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + else: + view_obj = None + if ret_value: + ret_value_obj = types.ValueLocation._from_core_struct(ret_value[0]) + else: + ret_value_obj = None + params = [] + for i in range(param_count): + loc = types.ValueLocation._from_core_struct(locations[i]) + ty = types.Type.from_core_struct(type_list[i]) + params.append((loc, ty)) + return self.get_stack_adjustment_for_locations(view_obj, ret_value_obj, params) + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._get_stack_adjustment_for_locations") + return 0 + + def _get_register_stack_adjustments(self, ctxt, view, ret_value, params, param_count, out_regs, out_adjust): + try: + if view: + view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + else: + view_obj = None + if ret_value: + ret_value_obj = types.ValueLocation._from_core_struct(ret_value[0]) + else: + ret_value_obj = None + param_objs = [] + for i in range(param_count): + param_objs.append(types.ValueLocation._from_core_struct(params[i])) + adjustment = self.get_register_stack_adjustments(view_obj, ret_value_obj, param_objs) + + regs = (ctypes.c_uint * len(adjustment))() + adjust = (ctypes.c_int * len(adjustment))() + for i, (reg, adj) in enumerate(adjustment.items()): + regs[i] = int(reg) + adjust[i] = adj + + reg_ptr = ctypes.cast(regs, ctypes.c_void_p) + self._pending_reg_stack_adjustment_reg_lists[reg_ptr.value] = (reg_ptr.value, regs) + adjust_ptr = ctypes.cast(adjust, ctypes.c_void_p) + self._pending_reg_stack_adjustment_amount_lists[adjust_ptr.value] = (adjust_ptr.value, adjust) + + out_regs[0] = regs + out_adjust[0] = adjust + return len(adjustment) + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._get_register_stack_adjustments") + out_regs[0] = None + out_adjust[0] = None + return 0 + + def _free_register_stack_adjustments(self, ctxt, regs, adjust, count): + try: + reg_ptr = ctypes.cast(regs, ctypes.c_void_p) + if reg_ptr.value is not None: + if reg_ptr.value not in self._pending_reg_stack_adjustment_reg_lists: + raise ValueError("freeing register list that wasn't allocated") + del self._pending_reg_stack_adjustment_reg_lists[reg_ptr.value] + adjust_ptr = ctypes.cast(adjust, ctypes.c_void_p) + if adjust_ptr.value is not None: + if adjust_ptr.value not in self._pending_reg_stack_adjustment_amount_lists: + raise ValueError("freeing adjustment list that wasn't allocated") + del self._pending_reg_stack_adjustment_amount_lists[adjust_ptr.value] + except: + log_error_for_exception("Unhandled Python exception in CallingConvention._free_register_stack_adjustments") + def perform_get_incoming_reg_value( self, reg: 'architecture.RegisterName', func: 'function.Function' ) -> 'variable.RegisterValue': + """Deprecated, override `get_incoming_reg_value` instead.""" reg_stack = self.arch.get_reg_stack_for_reg(reg) if reg_stack is not None: if reg == self.arch.reg_stacks[reg_stack].stack_top_reg: @@ -504,25 +792,335 @@ class CallingConvention: def perform_get_incoming_flag_value( self, flag: 'architecture.FlagName', func: 'function.Function' ) -> 'variable.RegisterValue': + """Deprecated, override `get_incoming_flag_value` instead.""" return variable.Undetermined() def perform_get_incoming_var_for_parameter_var( self, in_var: 'variable.CoreVariable', func: Optional['function.Function'] = None ) -> 'variable.CoreVariable': + """Deprecated, override `get_incoming_var_for_parameter_var` instead.""" out_var = core.BNGetDefaultIncomingVariableForParameterVariable(self.handle, in_var.to_BNVariable()) return variable.CoreVariable.from_BNVariable(out_var) def perform_get_parameter_var_for_incoming_var( self, in_var: 'variable.CoreVariable', func: Optional['function.Function'] = None ) -> 'variable.CoreVariable': + """Deprecated, override `get_parameter_var_for_incoming_var` instead.""" out_var = core.BNGetDefaultParameterVariableForIncomingVariable(self.handle, in_var.to_BNVariable()) return variable.CoreVariable.from_BNVariable(out_var) + def get_incoming_reg_value( + self, reg: 'architecture.RegisterName', func: 'function.Function' + ) -> 'variable.RegisterValue': + return self.perform_get_incoming_reg_value(reg, func) + + def get_incoming_flag_value( + self, reg: 'architecture.RegisterName', func: 'function.Function' + ) -> 'variable.RegisterValue': + return self.perform_get_incoming_flag_value(reg, func) + + def get_incoming_var_for_parameter_var( + self, in_var: 'variable.CoreVariable', func: Optional['function.Function'] = None + ) -> 'variable.CoreVariable': + return self.perform_get_incoming_var_for_parameter_var(in_var, func) + + def get_parameter_var_for_incoming_var( + self, in_var: 'variable.CoreVariable', func: Optional['function.Function'] = None + ) -> 'variable.CoreVariable': + return self.perform_get_incoming_var_for_parameter_var(in_var, func) + + def is_return_type_reg_compatible(self, view: Optional['binaryview.BinaryView'], type: 'types.Type') -> bool: + return self.default_is_return_type_reg_compatible(type) + + def is_non_reg_arg_indirect(self, view: Optional['binaryview.BinaryView'], type: Optional['types.Type']) -> bool: + return False + + def default_is_return_type_reg_compatible(self, type: 'types.Type') -> bool: + return core.BNDefaultIsReturnTypeRegisterCompatible(self.handle, type.handle) + + def get_indirect_return_value_location(self) -> 'variable.CoreVariable': + return self.get_default_indirect_return_value_location() + + def get_default_indirect_return_value_location(self) -> 'variable.CoreVariable': + result = core.BNGetDefaultIndirectReturnValueLocation(self.handle) + return variable.CoreVariable.from_BNVariable(result) + + def get_returned_indirect_return_value_pointer(self) -> Optional['variable.CoreVariable']: + return None + + def is_arg_type_reg_compatible(self, view: Optional['binaryview.BinaryView'], type: 'types.Type') -> bool: + return self.default_is_arg_type_reg_compatible(type) + + def default_is_arg_type_reg_compatible(self, type: 'types.Type') -> bool: + return core.BNDefaultIsArgumentTypeRegisterCompatible(self.handle, type.handle) + + def get_call_layout( + self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ReturnValueOrType'], + params: 'types.ParamsType', func: Optional['function.Function'] = None, + permitted_regs: Optional[List['architecture.RegisterIndex']] = None + ) -> 'CallLayout': + return self.get_default_call_layout(view, return_value, params, func, permitted_regs) + + def get_default_call_layout( + self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ReturnValueOrType'], + params: 'types.ParamsType', func: Optional['function.Function'] = None, + permitted_regs: Optional[List['architecture.RegisterIndex']] = None + ) -> 'CallLayout': + if view is None: + view_obj = None + else: + view_obj = view.handle + if return_value is None: + ret = types.ReturnValue(types.Type.void())._to_core_struct() + elif isinstance(return_value, types.ReturnValue): + ret = return_value._to_core_struct() + else: + ret = types.ReturnValue(return_value)._to_core_struct() + param_structs, type_list = types.FunctionBuilder._to_core_struct(params) + if permitted_regs is None: + layout = core.BNGetDefaultCallLayoutDefaultPermittedArgs(self.handle, view_obj, ret, param_structs, len(params)) + else: + regs = (ctypes.c_uint * len(permitted_regs))() + for i in range(len(permitted_regs)): + regs[i] = int(permitted_regs[i]) + layout = core.BNGetDefaultCallLayout(self.handle, view_obj, ret, param_structs, len(params), regs, + len(permitted_regs)) + result = CallLayout._from_core_struct(layout, func) + core.BNFreeCallLayout(layout) + return result + + def get_return_value_location( + self, view: Optional['binaryview.BinaryView'], return_value: 'types.ReturnValueOrType' + ) -> Optional['types.ValueLocation']: + return self.get_default_return_value_location(view, return_value) + + def get_default_return_value_location( + self, view: Optional['binaryview.BinaryView'], return_value: 'types.ReturnValueOrType' + ) -> Optional['types.ValueLocation']: + if view is None: + view_obj = None + else: + view_obj = view.handle + if return_value is None: + ret = types.ReturnValue(types.Type.void())._to_core_struct() + elif isinstance(return_value, types.ReturnValue): + ret = return_value._to_core_struct() + else: + ret = types.ReturnValue(return_value)._to_core_struct() + location = core.BNGetDefaultReturnValueLocation(self.handle, view_obj, ret) + if location.count == 0: + result = None + else: + result = types.ValueLocation._from_core_struct(location) + core.BNFreeValueLocation(location) + return result + + def get_parameter_locations( + self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'], + params: 'types.ParamsType', arch: Optional['architecture.Architecture'] = None, + permitted_regs: Optional[List['architecture.RegisterIndex']] = None + ) -> List['types.ValueLocation']: + return self.get_default_parameter_locations(view, return_value, params, arch, permitted_regs) + + def get_default_parameter_locations( + self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'], + params: 'types.ParamsType', arch: Optional['architecture.Architecture'] = None, + permitted_regs: Optional[List['architecture.RegisterIndex']] = None + ) -> List['types.ValueLocation']: + if view is None: + view_obj = None + else: + view_obj = view.handle + if return_value is None: + ret = None + else: + ret = return_value._to_core_struct() + param_structs, type_list = types.FunctionBuilder._to_core_struct(params) + count = ctypes.c_ulonglong() + if permitted_regs is None: + locations = core.BNGetDefaultParameterLocationsDefaultPermittedArgs(self.handle, view_obj, ret, param_structs, + len(params), count) + else: + regs = (ctypes.c_uint * len(permitted_regs))() + for i in range(len(permitted_regs)): + regs[i] = int(permitted_regs[i]) + locations = core.BNGetDefaultParameterLocations(self.handle, view_obj, ret, param_structs, len(params), regs, + len(permitted_regs), count) + result = [] + for i in range(count.value): + result.append(types.ValueLocation._from_core_struct(locations[i], arch)) + core.BNFreeValueLocationList(locations, count.value) + return result + + def get_stack_adjustment_for_locations( + self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'], + params: List[Tuple['types.ValueLocation', 'types.Type']] + ): + return self.get_default_stack_adjustment_for_locations(return_value, params) + + def get_default_stack_adjustment_for_locations( + self, return_value: Optional['types.ValueLocation'], + params: List[Tuple['types.ValueLocation', 'types.Type']] + ): + if return_value is None: + ret = None + else: + ret = return_value._to_core_struct() + locations = (core.BNValueLocation * len(params))() + type_list = (ctypes.POINTER(core.BNType) * len(params))() + for i, (loc, ty) in enumerate(params): + locations[i] = loc._to_core_struct() + type_list[i] = ty.handle + return core.BNGetDefaultStackAdjustmentForLocations(self.handle, ret, locations, type_list, len(params)) + + def get_register_stack_adjustments( + self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'], + params: List['types.ValueLocation'] + ) -> Dict['architecture.RegisterIndex', int]: + return self.get_default_register_stack_adjustments(return_value, params) + + def get_default_register_stack_adjustments( + self, return_value: Optional['types.ValueLocation'], params: List['types.ValueLocation'] + ) -> Dict['architecture.RegisterIndex', int]: + if return_value is None: + ret = None + else: + ret = return_value._to_core_struct() + locations = (core.BNValueLocation * len(params))() + for i, loc in enumerate(params): + locations[i] = loc._to_core_struct() + out_regs = ctypes.POINTER(ctypes.c_uint)() + out_adjust = ctypes.POINTER(ctypes.c_int)() + count = core.BNGetCallingConventionDefaultRegisterStackAdjustments(self.handle, ret, locations, len(params), + out_regs, out_adjust) + + result = {} + for i in range(count): + result[architecture.RegisterIndex(out_regs[i])] = out_adjust[i] + + core.BNFreeCallingConventionRegisterStackAdjustments(out_regs, out_adjust) + return result + def with_confidence(self, confidence: int) -> 'CallingConvention': return CallingConvention( self.arch, handle=core.BNNewCallingConventionReference(self.handle), confidence=confidence ) + @property + def arch(self) -> 'architecture.Architecture': + return self._arch + + @arch.setter + def arch(self, value: 'architecture.Architecture') -> None: + self._arch = value + + +class CoreCallingConvention(CallingConvention): + def __init__(self, handle, confidence: int = core.max_confidence): + super().__init__(handle=handle, confidence=confidence) + + self.arch = architecture.CoreArchitecture._from_cache(core.BNGetCallingConventionArchitecture(handle)) + self.__dict__["name"] = core.BNGetCallingConventionName(handle) + self.__dict__["arg_regs_share_index"] = core.BNAreArgumentRegistersSharedIndex(handle) + self.__dict__["arg_regs_for_varargs"] = core.BNAreArgumentRegistersUsedForVarArgs(handle) + self.__dict__["stack_reserved_for_arg_regs"] = core.BNIsStackReservedForArgumentRegisters(handle) + self.__dict__["stack_adjusted_on_return"] = core.BNIsStackAdjustedOnReturn(handle) + self.__dict__["eligible_for_heuristics"] = core.BNIsEligibleForHeuristics(handle) + + count = ctypes.c_ulonglong() + regs = core.BNGetCallerSavedRegisters(handle, count) + assert regs is not None, "core.BNGetCallerSavedRegisters returned None" + result = [] + arch = self.arch + for i in range(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + self.__dict__["caller_saved_regs"] = result + + count = ctypes.c_ulonglong() + regs = core.BNGetCalleeSavedRegisters(handle, count) + assert regs is not None, "core.BNGetCalleeSavedRegisters returned None" + result = [] + arch = self.arch + for i in range(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + self.__dict__["callee_saved_regs"] = result + + count = ctypes.c_ulonglong() + regs = core.BNGetIntegerArgumentRegisters(handle, count) + assert regs is not None, "core.BNGetIntegerArgumentRegisters returned None" + result = [] + arch = self.arch + for i in range(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + self.__dict__["int_arg_regs"] = result + + count = ctypes.c_ulonglong() + regs = core.BNGetFloatArgumentRegisters(handle, count) + assert regs is not None, "core.BNGetFloatArgumentRegisters returned None" + result = [] + arch = self.arch + for i in range(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + self.__dict__["float_arg_regs"] = result + + count = ctypes.c_ulonglong() + regs = core.BNGetRequiredArgumentRegisters(handle, count) + assert regs is not None, "core.BNGetRequiredArgumentRegisters returned None" + result = [] + arch = self.arch + for i in range(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + self.__dict__["required_arg_regs"] = result + + count = ctypes.c_ulonglong() + regs = core.BNGetRequiredClobberedRegisters(handle, count) + assert regs is not None, "core.BNGetRequiredClobberedRegisters returned None" + result = [] + arch = self.arch + for i in range(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + self.__dict__["required_clobbered_regs"] = result + + reg = core.BNGetIntegerReturnValueRegister(handle) + if reg == 0xffffffff: + self.__dict__["int_return_reg"] = None + else: + self.__dict__["int_return_reg"] = self.arch.get_reg_name(reg) + + reg = core.BNGetHighIntegerReturnValueRegister(handle) + if reg == 0xffffffff: + self.__dict__["high_int_return_reg"] = None + else: + self.__dict__["high_int_return_reg"] = self.arch.get_reg_name(reg) + + reg = core.BNGetFloatReturnValueRegister(handle) + if reg == 0xffffffff: + self.__dict__["float_return_reg"] = None + else: + self.__dict__["float_return_reg"] = self.arch.get_reg_name(reg) + + reg = core.BNGetGlobalPointerRegister(handle) + if reg == 0xffffffff: + self.__dict__["global_pointer_reg"] = None + else: + self.__dict__["global_pointer_reg"] = self.arch.get_reg_name(reg) + + count = ctypes.c_ulonglong() + regs = core.BNGetImplicitlyDefinedRegisters(handle, count) + assert regs is not None, "core.BNGetImplicitlyDefinedRegisters returned None" + result = [] + arch = self.arch + for i in range(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + self.__dict__["implicitly_defined_regs"] = result + def get_incoming_reg_value( self, reg: 'architecture.RegisterType', func: 'function.Function' ) -> 'variable.RegisterValue': @@ -546,7 +1144,7 @@ class CallingConvention: ) def get_incoming_var_for_parameter_var( - self, in_var: 'variable.CoreVariable', func: FunctionOrILFunction + self, in_var: 'variable.CoreVariable', func: FunctionOrILFunction = None ) -> 'variable.Variable': in_buf = in_var.to_BNVariable() if func is None: @@ -557,7 +1155,7 @@ class CallingConvention: return variable.Variable.from_BNVariable(func, out_var) def get_parameter_var_for_incoming_var( - self, in_var: 'variable.CoreVariable', func: FunctionOrILFunction + self, in_var: 'variable.CoreVariable', func: FunctionOrILFunction = None ) -> 'variable.Variable': in_buf = in_var.to_BNVariable() if func is None: @@ -567,10 +1165,160 @@ class CallingConvention: out_var = core.BNGetParameterVariableForIncomingVariable(self.handle, in_buf, func_obj) return variable.Variable.from_BNVariable(func, out_var) - @property - def arch(self) -> 'architecture.Architecture': - return self._arch + def is_return_type_reg_compatible(self, view: Optional['binaryview.BinaryView'], type: 'types.Type') -> bool: + if view is None: + view_obj = None + else: + view_obj = view.handle + return core.BNIsReturnTypeRegisterCompatible(self.handle, view_obj, type.handle) - @arch.setter - def arch(self, value: 'architecture.Architecture') -> None: - self._arch = value + def get_indirect_return_value_location(self) -> 'variable.CoreVariable': + result = core.BNGetIndirectReturnValueLocation(self.handle) + return variable.CoreVariable.from_BNVariable(result) + + def get_returned_indirect_return_value_pointer(self) -> Optional['variable.CoreVariable']: + var = core.BNVariable() + if core.BNGetReturnedIndirectReturnValuePointer(self.handle, var): + return variable.CoreVariable.from_BNVariable(var) + return None + + def is_arg_type_reg_compatible(self, view: Optional['binaryview.BinaryView'], type: 'types.Type') -> bool: + if view is None: + view_obj = None + else: + view_obj = view.handle + return core.BNIsArgumentTypeRegisterCompatible(self.handle, view_obj, type.handle) + + def is_non_reg_arg_indirect(self, view: Optional['binaryview.BinaryView'], type: Optional['types.Type']) -> bool: + if view is None: + view_obj = None + else: + view_obj = view.handle + if type is None: + return core.BNIsNonRegisterArgumentIndirect(self.handle, view_obj, None) + else: + return core.BNIsNonRegisterArgumentIndirect(self.handle, view_obj, type.handle) + + def get_call_layout( + self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ReturnValueOrType'], + params: 'types.ParamsType', func: Optional['function.Function'] = None, + permitted_regs: Optional[List['architecture.RegisterIndex']] = None + ) -> 'CallLayout': + if view is None: + view_obj = None + else: + view_obj = view.handle + if return_value is None: + ret = types.ReturnValue(types.Type.void())._to_core_struct() + elif isinstance(return_value, types.ReturnValue): + ret = return_value._to_core_struct() + else: + ret = types.ReturnValue(return_value)._to_core_struct() + param_structs, type_list = types.FunctionBuilder._to_core_struct(params) + if permitted_regs is None: + layout = core.BNGetCallLayoutDefaultPermittedArgs(self.handle, view_obj, ret, param_structs, len(params)) + else: + regs = (ctypes.c_uint * len(permitted_regs))() + for i in range(len(permitted_regs)): + regs[i] = int(permitted_regs[i]) + layout = core.BNGetCallLayout(self.handle, view_obj, ret, param_structs, len(params), regs, len(permitted_regs)) + result = CallLayout._from_core_struct(layout, func) + core.BNFreeCallLayout(layout) + return result + + def get_return_value_location( + self, view: Optional['binaryview.BinaryView'], return_value: 'types.ReturnValueOrType' + ) -> Optional['types.ValueLocation']: + if view is None: + view_obj = None + else: + view_obj = view.handle + if return_value is None: + ret = types.ReturnValue(types.Type.void())._to_core_struct() + elif isinstance(return_value, types.ReturnValue): + ret = return_value._to_core_struct() + else: + ret = types.ReturnValue(return_value)._to_core_struct() + location = core.BNGetReturnValueLocation(self.handle, view_obj, ret) + if location.count == 0: + result = None + else: + result = types.ValueLocation._from_core_struct(location) + core.BNFreeValueLocation(location) + return result + + def get_parameter_locations( + self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'], + params: 'types.ParamsType', arch: Optional['architecture.Architecture'] = None, + permitted_regs: Optional[List['architecture.RegisterIndex']] = None + ) -> List['types.ValueLocation']: + if view is None: + view_obj = None + else: + view_obj = view.handle + if return_value is None: + ret = None + else: + ret = return_value._to_core_struct() + param_structs, type_list = types.FunctionBuilder._to_core_struct(params) + count = ctypes.c_ulonglong() + if permitted_regs is None: + locations = core.BNGetParameterLocationsDefaultPermittedArgs(self.handle, view_obj, ret, param_structs, + len(params), count) + else: + regs = (ctypes.c_uint * len(permitted_regs))() + for i in range(len(permitted_regs)): + regs[i] = int(permitted_regs[i]) + locations = core.BNGetParameterLocations(self.handle, view_obj, ret, param_structs, len(params), regs, + len(permitted_regs), count) + result = [] + for i in range(count.value): + result.append(types.ValueLocation._from_core_struct(locations[i], arch)) + core.BNFreeValueLocationList(locations, count.value) + return result + + def get_stack_adjustment_for_locations( + self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'], + params: List[Tuple['types.ValueLocation', 'types.Type']] + ): + if view is None: + view_obj = None + else: + view_obj = view.handle + if return_value is None: + ret = None + else: + ret = return_value._to_core_struct() + locations = (core.BNValueLocation * len(params))() + type_list = (ctypes.POINTER(core.BNType) * len(params))() + for i, (loc, ty) in enumerate(params): + locations[i] = loc._to_core_struct() + type_list[i] = ty.handle + return core.BNGetStackAdjustmentForLocations(self.handle, view_obj, ret, locations, type_list, len(params)) + + def get_register_stack_adjustments( + self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'], + params: List['types.ValueLocation'] + ) -> Dict['architecture.RegisterIndex', int]: + if view is None: + view_obj = None + else: + view_obj = view.handle + if return_value is None: + ret = None + else: + ret = return_value._to_core_struct() + locations = (core.BNValueLocation * len(params))() + for i, loc in enumerate(params): + locations[i] = loc._to_core_struct() + out_regs = ctypes.POINTER(ctypes.c_uint)() + out_adjust = ctypes.POINTER(ctypes.c_int)() + count = core.BNGetCallingConventionRegisterStackAdjustments(self.handle, view_obj, ret, locations, len(params), + out_regs, out_adjust) + + result = {} + for i in range(count): + result[architecture.RegisterIndex(out_regs[i])] = out_adjust[i] + + core.BNFreeCallingConventionRegisterStackAdjustments(out_regs, out_adjust) + return result diff --git a/python/examples/pseudo_python.py b/python/examples/pseudo_python.py index d29d4e57..af06b461 100644 --- a/python/examples/pseudo_python.py +++ b/python/examples/pseudo_python.py @@ -517,6 +517,17 @@ class PseudoPythonFunction(LanguageRepresentationFunction): OperatorPrecedence.UnaryOperatorPrecedence) if parens: tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_PASS_BY_REF: + if instr.src.operation == HighLevelILOperation.HLIL_ADDRESS_OF: + self.perform_get_expr_text(instr.src, tokens, settings, + OperatorPrecedence.UnaryOperatorPrecedence) + else: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "*")) + self.perform_get_expr_text(instr.src, tokens, settings, + OperatorPrecedence.UnaryOperatorPrecedence) + elif instr.operation == HighLevelILOperation.HLIL_RETURN_BY_REF: + self.perform_get_expr_text(instr.src, tokens, settings, + OperatorPrecedence.UnaryOperatorPrecedence) elif instr.operation in [HighLevelILOperation.HLIL_CMP_E, HighLevelILOperation.HLIL_FCMP_E]: parens = precedence > OperatorPrecedence.EqualityOperatorPrecedence if parens: @@ -1136,23 +1147,33 @@ class PseudoPythonFunctionType(LanguageRepresentationFunctionType): tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "def ")) tokens.append(InstructionTextToken(InstructionTextTokenType.CodeSymbolToken, func.name, value=func.start)) tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, "(")) + params = func.type.parameters for (i, param) in enumerate(func.type.parameters_with_all_locations): if i > 0: tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + var = param.location.variable_for_parameter(i) tokens.append(InstructionTextToken(InstructionTextTokenType.ArgumentNameToken, param.name, context=InstructionTextTokenContext.LocalVariableTokenContext, - address=param.location.identifier)) + address=var.identifier)) tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ": ")) for token in param.type.get_tokens(): token.context = InstructionTextTokenContext.LocalVariableTokenContext - token.address = param.location.identifier + token.address = var.identifier tokens.append(token) + if i < len(params) and params[i].location is not None: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " @ ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.ValueLocationToken, + params[i].location.to_string(func.arch))) tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, ")")) if func.can_return.value and func.type.return_value is not None and not isinstance(func.type.return_value, VoidType): tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " -> ")) for token in func.type.return_value.get_tokens(): token.context = InstructionTextTokenContext.FunctionReturnTokenContext tokens.append(token) + if func.type.return_value_location is not None: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " @ ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.ValueLocationToken, + func.type.return_value_location.location.to_string(func.arch))) tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) return [DisassemblyTextLine(tokens, func.start)] diff --git a/python/function.py b/python/function.py index 8c9eaf1d..900aa0de 100644 --- a/python/function.py +++ b/python/function.py @@ -29,7 +29,7 @@ from . import _binaryninjacore as core from .enums import ( AnalysisSkipReason, FunctionGraphType, SymbolType, SymbolBinding, InstructionTextTokenType, HighlightStandardColor, HighlightColorStyle, DisassemblyOption, IntegerDisplayType, FunctionAnalysisSkipOverride, FunctionUpdateType, - BuiltinType, ExprFolding, EarlyReturn, SwitchRecovery + BuiltinType, ExprFolding, EarlyReturn, SwitchRecovery, VariableSourceType ) from . import associateddatastore # Required in the main scope due to being an argument for _FunctionAssociatedDataStore @@ -1361,8 +1361,49 @@ class Function: core.BNSetUserFunctionReturnType(self.handle, type_conf) @property + def return_value(self) -> 'types.ReturnValue': + """Return type and location""" + ret = core.BNGetFunctionReturnValue(self.handle) + result = types.ReturnValue._from_core_struct(ret, self.arch) + core.BNFreeReturnValue(ret) + return result + + @return_value.setter + def return_value(self, value: 'types.ReturnValue') -> None: # type: ignore + ret = value._to_core_struct() + core.BNSetUserFunctionReturnValue(self.handle, ret) + + @property + def return_value_location(self) -> Optional['types.ValueLocationWithConfidence']: + """ + The location of the return value, or None if there isn't a return value. If the return value has been + specified to be placed in the default location, this will return the default location. + """ + location = core.BNGetFunctionReturnValueLocation(self.handle) + if location.location.count == 0: + result = None + else: + result = types.ValueLocation._from_core_struct(location.location, self.arch).with_confidence(location.confidence) + core.BNFreeValueLocation(location.location) + return result + + @return_value_location.setter + def return_value_location(self, value: 'types.OptionalLocation'): + struct = core.BNValueLocationWithConfidence() + location = types.ValueLocationWithConfidence.from_optional_location(value) + if location is None: + struct.location.count = 0 + struct.confidence = 0 + else: + struct.location = location.location._to_core_struct() + struct.confidence = location.confidence + core.BNSetUserIsFunctionReturnValueDefaultLocation(self.handle, value is None) + if value is not None: + core.BNSetUserFunctionReturnValueLocation(self.handle, struct) + + @property def return_regs(self) -> 'types.RegisterSet': - """Registers that are used for the return value""" + """Registers that are used for the return value (read-only)""" result = core.BNGetFunctionReturnRegisters(self.handle) assert result is not None, "core.BNGetFunctionReturnRegisters returned None" try: @@ -1373,26 +1414,13 @@ class Function: finally: core.BNFreeRegisterSet(result) - @return_regs.setter - def return_regs(self, value: Union['types.RegisterSet', List['architecture.RegisterType']]) -> None: # type: ignore - regs = core.BNRegisterSetWithConfidence() - regs.regs = (ctypes.c_uint * len(value))() - regs.count = len(value) - for i in range(0, len(value)): - regs.regs[i] = self.arch.get_reg_index(value[i]) - if isinstance(value, types.RegisterSet): - regs.confidence = value.confidence - else: - regs.confidence = core.max_confidence - core.BNSetUserFunctionReturnRegisters(self.handle, regs) - @property def calling_convention(self) -> Optional['callingconvention.CallingConvention']: """Calling convention used by the function""" result = core.BNGetFunctionCallingConvention(self.handle) if not result.convention: return None - return callingconvention.CallingConvention(None, handle=result.convention, confidence=result.confidence) + return callingconvention.CoreCallingConvention(handle=result.convention, confidence=result.confidence) @calling_convention.setter def calling_convention(self, value: Optional['callingconvention.CallingConvention']) -> None: @@ -1424,20 +1452,60 @@ class Function: var_list = [] else: var_list = list(value) - var_conf = core.BNParameterVariablesWithConfidence() - var_conf.vars = (core.BNVariable * len(var_list))() - var_conf.count = len(var_list) - for i in range(0, len(var_list)): - var_conf.vars[i].type = var_list[i].source_type - var_conf.vars[i].index = var_list[i].index - var_conf.vars[i].storage = var_list[i].storage + locations = [] + for i in range(len(var_list)): + if (var_list[i].source_type != VariableSourceType.RegisterVariableSourceType and + var_list[i].source_type != VariableSourceType.StackVariableSourceType and + var_list[i].source_type != VariableSourceType.FlagVariableSourceType): + raise ValueError(f"Parameter {i} is a composite variable. Use parameter_locations instead.") + locations.append(types.ValueLocation([types.ValueLocationComponent(var_list[i])])) if value is None: - var_conf.confidence = 0 - elif isinstance(value, types.RegisterSet): - var_conf.confidence = value.confidence + conf = 0 + elif isinstance(value, variable.ParameterVariables): + conf = value.confidence else: - var_conf.confidence = core.max_confidence - core.BNSetUserFunctionParameterVariables(self.handle, var_conf) + conf = core.max_confidence + self.parameter_locations = variable.ParameterLocations(locations, conf, self) + + @property + def parameter_locations(self) -> 'variable.ParameterLocations': + """List of locations for the incoming function parameters""" + result = core.BNGetFunctionParameterLocations(self.handle) + location_list = [] + for i in range(0, result.count): + location_list.append(types.ValueLocation._from_core_struct(result.locations[i], self.arch)) + confidence = result.confidence + core.BNFreeParameterLocations(result) + return variable.ParameterLocations(location_list, confidence, self) + + @parameter_locations.setter + def parameter_locations( + self, value: Optional[Union[List[Union['types.ValueLocation', 'variable.CoreVariable']], + 'variable.CoreVariable', 'variable.ParameterLocations']] + ) -> None: # type: ignore + if value is None: + location_list = [] + elif isinstance(value, variable.CoreVariable): + location_list = [value] + elif isinstance(value, variable.ParameterLocations): + location_list = value.locations + else: + location_list = list(value) + location_conf = core.BNValueLocationListWithConfidence() + location_conf.locations = (core.BNValueLocation * len(location_list))() + location_conf.count = len(location_list) + for i in range(0, len(location_list)): + if isinstance(location_list[i], types.ValueLocation): + location_conf.locations[i] = location_list[i]._to_core_struct() + else: + location_conf.locations[i] = types.ValueLocation([types.ValueLocationComponent(location_list[i])])._to_core_struct() + if value is None: + location_conf.confidence = 0 + elif isinstance(value, variable.ParameterLocations): + location_conf.confidence = value.confidence + else: + location_conf.confidence = core.max_confidence + core.BNSetUserFunctionParameterLocations(self.handle, location_conf) @property def has_variable_arguments(self) -> 'types.BoolWithConfidence': @@ -2477,18 +2545,18 @@ class Function: type_conf.confidence = value.confidence core.BNSetAutoFunctionReturnType(self.handle, type_conf) - def set_auto_return_regs(self, value: Union['types.RegisterSet', List['architecture.RegisterType']]) -> None: - regs = core.BNRegisterSetWithConfidence() - regs.regs = (ctypes.c_uint * len(value))() - regs.count = len(value) - - for i in range(0, len(value)): - regs.regs[i] = self.arch.get_reg_index(value[i]) - if isinstance(value, types.RegisterSet): - regs.confidence = value.confidence + def set_auto_return_value_location(self, value: 'types.OptionalLocation'): + struct = core.BNValueLocationWithConfidence() + location = types.ValueLocationWithConfidence.from_optional_location(value) + if location is None: + struct.location.count = 0 + struct.confidence = 0 else: - regs.confidence = core.max_confidence - core.BNSetAutoFunctionReturnRegisters(self.handle, regs) + struct.location = location.location._to_core_struct() + struct.confidence = location.confidence + core.BNSetAutoIsFunctionReturnValueDefaultLocation(self.handle, value is None) + if value is not None: + core.BNSetAutoFunctionReturnValueLocation(self.handle, struct) def set_auto_calling_convention(self, value: 'callingconvention.CallingConvention') -> None: conv_conf = core.BNCallingConventionWithConfidence() @@ -2501,30 +2569,58 @@ class Function: core.BNSetAutoFunctionCallingConvention(self.handle, conv_conf) def set_auto_parameter_vars( - self, value: Optional[Union[List['variable.Variable'], 'variable.Variable', 'variable.ParameterVariables']] + self, value: Optional[Union[List['variable.CoreVariable'], 'variable.CoreVariable', 'variable.ParameterVariables']] ) -> None: if value is None: var_list = [] - elif isinstance(value, variable.Variable): + elif isinstance(value, variable.CoreVariable): var_list = [value] elif isinstance(value, variable.ParameterVariables): var_list = value.vars else: var_list = list(value) - var_conf = core.BNParameterVariablesWithConfidence() - var_conf.vars = (core.BNVariable * len(var_list))() - var_conf.count = len(var_list) - for i in range(0, len(var_list)): - var_conf.vars[i].type = var_list[i].source_type - var_conf.vars[i].index = var_list[i].index - var_conf.vars[i].storage = var_list[i].storage + locations = [] + for i in range(len(var_list)): + if (var_list[i].source_type != VariableSourceType.RegisterVariableSourceType and + var_list[i].source_type != VariableSourceType.StackVariableSourceType and + var_list[i].source_type != VariableSourceType.FlagVariableSourceType): + raise ValueError(f"Parameter {i} is a composite variable. Use set_auto_parameter_locations instead.") + locations.append(types.ValueLocation([types.ValueLocationComponent(var_list[i])])) + if value is None: + conf = 0 + elif isinstance(value, variable.ParameterVariables): + conf = value.confidence + else: + conf = core.max_confidence + self.set_auto_parameter_locations(variable.ParameterLocations(locations, conf, self)) + + def set_auto_parameter_locations( + self, value: Optional[Union[List[Union['variable.CoreVariable', 'types.ValueLocation']], + 'variable.CoreVariable', 'types.ValueLocation', 'variable.ParameterLocations']] + ) -> None: + if value is None: + location_list = [] + elif isinstance(value, variable.CoreVariable): + location_list = [value] + elif isinstance(value, variable.ParameterLocations): + location_list = value.locations + else: + location_list = list(value) + location_conf = core.BNValueLocationListWithConfidence() + location_conf.locations = (core.BNValueLocation * len(location_list))() + location_conf.count = len(location_list) + for i in range(0, len(location_list)): + if isinstance(location_list[i], types.ValueLocation): + location_conf.locations[i] = location_list[i]._to_core_struct() + else: + location_conf.locations[i] = types.ValueLocation([types.ValueLocationComponent(location_list[i])])._to_core_struct() if value is None: - var_conf.confidence = 0 + location_conf.confidence = 0 elif isinstance(value, variable.ParameterVariables): - var_conf.confidence = value.confidence + location_conf.confidence = value.confidence else: - var_conf.confidence = core.max_confidence - core.BNSetAutoFunctionParameterVariables(self.handle, var_conf) + location_conf.confidence = core.max_confidence + core.BNSetAutoFunctionParameterLocations(self.handle, location_conf) def set_auto_has_variable_arguments(self, value: Union[bool, 'types.BoolWithConfidence']) -> None: bc = core.BNBoolWithConfidence() diff --git a/python/highlevelil.py b/python/highlevelil.py index bb1f6129..4c81f687 100644 --- a/python/highlevelil.py +++ b/python/highlevelil.py @@ -210,7 +210,11 @@ class HighLevelILInstruction(BaseILInstruction): ], HighLevelILOperation.HLIL_DEREF_FIELD_SSA: [ ("src", "expr"), ("src_memory", "int"), ("offset", "int"), ("member_index", "member_index") - ], HighLevelILOperation.HLIL_ADDRESS_OF: [("src", "expr")], HighLevelILOperation.HLIL_CONST: [ + ], HighLevelILOperation.HLIL_ADDRESS_OF: [("src", "expr")], HighLevelILOperation.HLIL_PASS_BY_REF: [ + ("src", "expr") + ], HighLevelILOperation.HLIL_RETURN_BY_REF: [ + ("src", "expr") + ], HighLevelILOperation.HLIL_CONST: [ ("constant", "int") ], HighLevelILOperation.HLIL_CONST_PTR: [("constant", "int")], HighLevelILOperation.HLIL_EXTERN_PTR: [ ("constant", "int"), ("offset", "int") @@ -1744,6 +1748,16 @@ class HighLevelILAddressOf(HighLevelILUnaryBase): @dataclass(frozen=True, repr=False, eq=False) +class HighLevelILPassByRef(HighLevelILUnaryBase): + pass + + +@dataclass(frozen=True, repr=False, eq=False) +class HighLevelILReturnByRef(HighLevelILUnaryBase): + pass + + +@dataclass(frozen=True, repr=False, eq=False) class HighLevelILConst(HighLevelILInstruction, Constant): @property def constant(self) -> int: @@ -2429,6 +2443,8 @@ ILInstruction = { HighLevelILOperation.HLIL_DEREF_FIELD_SSA: HighLevelILDerefFieldSsa, # ("src", "expr"), ("src_memory", "int"), ("offset", "int"), ("member_index", "member_index"), HighLevelILOperation.HLIL_ADDRESS_OF: HighLevelILAddressOf, # ("src", "expr"), + HighLevelILOperation.HLIL_PASS_BY_REF: HighLevelILPassByRef, # ("src", "expr"), + HighLevelILOperation.HLIL_RETURN_BY_REF: HighLevelILReturnByRef, # ("src", "expr"), HighLevelILOperation.HLIL_CONST: HighLevelILConst, # ("constant", "int"), HighLevelILOperation.HLIL_CONST_PTR: HighLevelILConstPtr, # ("constant", "int"), HighLevelILOperation.HLIL_EXTERN_PTR: HighLevelILExternPtr, # ("constant", "int"), ("offset", "int"), @@ -3380,6 +3396,30 @@ class HighLevelILFunction: """ return self.expr(HighLevelILOperation.HLIL_ADDRESS_OF, src, size=0, source_location=loc) + def pass_by_ref(self, size: int, src: ExpressionIndex, loc: Optional['ILSourceLocation'] = None) -> ExpressionIndex: + """ + ``pass_by_ref`` indicates that ``value`` is being passed by reference to a call with a pointer size of ``size`` + + :param int size: the size of the pointer in bytes + :param ExpressionIndex src: the expression containing the reference being passed + :param ILSourceLocation loc: location of returned expression + :return: The expression ``ref *src`` + :rtype: ExpressionIndex + """ + return self.expr(HighLevelILOperation.HLIL_PASS_BY_REF, src, size, source_location=loc) + + def return_by_ref(self, size: int, dest: ExpressionIndex, loc: Optional['ILSourceLocation'] = None) -> ExpressionIndex: + """ + ``return_by_ref`` indicates that ``dest`` is being returned by passing a reference to a call + + :param int size: the size of the value in bytes + :param ExpressionIndex dest: the expression containing the target of the return value + :param ILSourceLocation loc: location of returned expression + :return: The expression ``ref dest`` + :rtype: ExpressionIndex + """ + return self.expr(HighLevelILOperation.HLIL_RETURN_BY_REF, dest, size, source_location=loc) + def const(self, size: int, value: int, loc: Optional['ILSourceLocation'] = None) -> ExpressionIndex: """ ``const`` returns an expression for the constant integer ``value`` of size ``size`` diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py index 1a42fe27..155a3751 100644 --- a/python/mediumlevelil.py +++ b/python/mediumlevelil.py @@ -219,7 +219,11 @@ class MediumLevelILInstruction(BaseILInstruction): MediumLevelILOperation.MLIL_VAR: [("src", "var")], MediumLevelILOperation.MLIL_VAR_FIELD: [ ("src", "var"), ("offset", "int") ], MediumLevelILOperation.MLIL_VAR_SPLIT: [("high", "var"), ("low", "var")], - MediumLevelILOperation.MLIL_ADDRESS_OF: [("src", "var")], MediumLevelILOperation.MLIL_ADDRESS_OF_FIELD: [ + MediumLevelILOperation.MLIL_ADDRESS_OF: [("src", "var")], MediumLevelILOperation.MLIL_PASS_BY_REF: [ + ("src", "expr") + ], MediumLevelILOperation.MLIL_RETURN_BY_REF: [ + ("src", "expr") + ], MediumLevelILOperation.MLIL_ADDRESS_OF_FIELD: [ ("src", "var"), ("offset", "int") ], MediumLevelILOperation.MLIL_CONST: [("constant", "int")], MediumLevelILOperation.MLIL_CONST_PTR: [ ("constant", "int") @@ -279,9 +283,15 @@ class MediumLevelILInstruction(BaseILInstruction): ("params", "expr_list") ], MediumLevelILOperation.MLIL_VAR_OUTPUT: [ ("dest", "var") + ], MediumLevelILOperation.MLIL_VAR_OUTPUT_FIELD: [ + ("dest", "var"), ("offset", "int") + ], MediumLevelILOperation.MLIL_STORE_OUTPUT: [ + ("dest", "expr") ], MediumLevelILOperation.MLIL_RET: [ ("src", "expr_list") - ], MediumLevelILOperation.MLIL_NORET: [], MediumLevelILOperation.MLIL_IF: [ + ], MediumLevelILOperation.MLIL_BLOCK_TO_EXPAND: [ + ("src", "expr_list") + ], MediumLevelILOperation.MLIL_NORET: [], MediumLevelILOperation.MLIL_IF: [ ("condition", "expr"), ("true", "int"), ("false", "int") ], MediumLevelILOperation.MLIL_GOTO: [("dest", "int")], MediumLevelILOperation.MLIL_CMP_E: [ ("left", "expr"), ("right", "expr") @@ -377,6 +387,12 @@ class MediumLevelILInstruction(BaseILInstruction): ("high", "var_ssa"), ("low", "var_ssa") ], MediumLevelILOperation.MLIL_VAR_OUTPUT_SSA: [ ("dest", "var_ssa") + ], MediumLevelILOperation.MLIL_VAR_OUTPUT_SSA_FIELD: [ + ("dest", "var_ssa_dest_and_src"), ("prev", "var_ssa_dest_and_src"), ("offset", "int") + ], MediumLevelILOperation.MLIL_VAR_OUTPUT_ALIASED: [ + ("dest", "var_ssa_dest_and_src"), ("prev", "var_ssa_dest_and_src") + ], MediumLevelILOperation.MLIL_VAR_OUTPUT_ALIASED_FIELD: [ + ("dest", "var_ssa_dest_and_src"), ("prev", "var_ssa_dest_and_src"), ("offset", "int") ], MediumLevelILOperation.MLIL_CALL_SSA: [ ("output", "expr"), ("output_dest_memory", "int"), ("dest", "expr"), ("params", "expr_list"), ("src_memory", "int") @@ -1158,13 +1174,25 @@ class MediumLevelILInstruction(BaseILInstruction): return result def _var_written_for_function_call_output(self) -> Optional[variable.Variable]: + if isinstance(self, MediumLevelILReturnByRef): + return self.src._var_written_for_function_call_output() if isinstance(self, MediumLevelILVarOutput): return self.dest + if isinstance(self, MediumLevelILVarOutputField): + return self.dest return None def _ssa_var_written_for_function_call_output(self) -> Optional[SSAVariable]: + if isinstance(self, MediumLevelILReturnByRef): + return self.src._ssa_var_written_for_function_call_output() if isinstance(self, MediumLevelILVarOutputSsa): return self.dest + if isinstance(self, MediumLevelILVarOutputSsaField): + return self.dest + if isinstance(self, MediumLevelILVarOutputAliased): + return self.dest + if isinstance(self, MediumLevelILVarOutputAliasedField): + return self.dest return None @@ -1324,6 +1352,16 @@ class MediumLevelILAddressOf(MediumLevelILInstruction): @dataclass(frozen=True, repr=False, eq=False) +class MediumLevelILPassByRef(MediumLevelILUnaryBase): + pass + + +@dataclass(frozen=True, repr=False, eq=False) +class MediumLevelILReturnByRef(MediumLevelILUnaryBase): + pass + + +@dataclass(frozen=True, repr=False, eq=False) class MediumLevelILConst(MediumLevelILConstBase): @property def constant(self) -> int: @@ -1503,6 +1541,37 @@ class MediumLevelILVarOutput(MediumLevelILInstruction, RegisterStack): @dataclass(frozen=True, repr=False, eq=False) +class MediumLevelILVarOutputField(MediumLevelILInstruction, SetVar): + @property + def dest(self) -> variable.Variable: + return self._get_var(0) + + @property + def offset(self) -> int: + return self._get_int(1) + + @property + def detailed_operands(self) -> List[Tuple[str, MediumLevelILOperandType, str]]: + return [ + ('dest', self.dest, 'Variable'), + ('offset', self.offset, 'int') + ] + + +@dataclass(frozen=True, repr=False, eq=False) +class MediumLevelILStoreOutput(MediumLevelILInstruction, Store): + @property + def dest(self) -> MediumLevelILInstruction: + return self._get_expr(0) + + @property + def detailed_operands(self) -> List[Tuple[str, MediumLevelILOperandType, str]]: + return [ + ("dest", self.dest, "MediumLevelILInstruction"), + ] + + +@dataclass(frozen=True, repr=False, eq=False) class MediumLevelILRet(MediumLevelILInstruction, Return): @property def src(self) -> List[MediumLevelILInstruction]: @@ -1667,6 +1736,94 @@ class MediumLevelILVarOutputSsa(MediumLevelILInstruction, SSAVariableInstruction @dataclass(frozen=True, repr=False, eq=False) +class MediumLevelILVarOutputSsaField(MediumLevelILInstruction, SetVar, SSA): + @property + def dest(self) -> SSAVariable: + return self._get_var_ssa_dest_and_src(0, 1) + + @property + def prev(self) -> SSAVariable: + return self._get_var_ssa_dest_and_src(0, 2) + + @property + def offset(self) -> int: + return self._get_int(3) + + @property + def detailed_operands(self) -> List[Tuple[str, MediumLevelILOperandType, str]]: + return [ + ('dest', self.dest, 'SSAVariable'), + ('prev', self.prev, 'SSAVariable'), + ('offset', self.offset, 'int') + ] + + @property + def vars_read(self) -> List[SSAVariable]: + return [self.prev] # type: ignore # we're guaranteed not to return non-SSAVariables here + + @property + def vars_written(self) -> List[SSAVariable]: + return [self.dest] + + +@dataclass(frozen=True, repr=False, eq=False) +class MediumLevelILVarOutputAliased(MediumLevelILInstruction, SetVar, SSA): + @property + def dest(self) -> SSAVariable: + return self._get_var_ssa_dest_and_src(0, 1) + + @property + def prev(self) -> SSAVariable: + return self._get_var_ssa_dest_and_src(0, 2) + + @property + def detailed_operands(self) -> List[Tuple[str, MediumLevelILOperandType, str]]: + return [ + ('dest', self.dest, 'SSAVariable'), + ('prev', self.prev, 'SSAVariable') + ] + + @property + def vars_read(self) -> List[Union[variable.Variable, SSAVariable]]: + return [] + + @property + def vars_written(self) -> List[SSAVariable]: + return [self.dest] + + +@dataclass(frozen=True, repr=False, eq=False) +class MediumLevelILVarOutputAliasedField(MediumLevelILInstruction, SetVar, SSA): + @property + def dest(self) -> SSAVariable: + return self._get_var_ssa_dest_and_src(0, 1) + + @property + def prev(self) -> SSAVariable: + return self._get_var_ssa_dest_and_src(0, 2) + + @property + def offset(self) -> int: + return self._get_int(3) + + @property + def detailed_operands(self) -> List[Tuple[str, MediumLevelILOperandType, str]]: + return [ + ('dest', self.dest, 'SSAVariable'), + ('prev', self.prev, 'SSAVariable'), + ('offset', self.offset, 'int') + ] + + @property + def vars_read(self) -> List[SSAVariable]: + return [self.prev] # type: ignore # we're guaranteed not to return non-SSAVariables here + + @property + def vars_written(self) -> List[SSAVariable]: + return [self.dest] + + +@dataclass(frozen=True, repr=False, eq=False) class MediumLevelILVarAliased(MediumLevelILInstruction, SSA, AliasedVariableInstruction): @property def src(self) -> SSAVariable: @@ -3230,6 +3387,15 @@ class MediumLevelILForceVerSsa(MediumLevelILInstruction, SSA): def src(self) -> SSAVariable: return self._get_var_ssa(2, 3) +@dataclass(frozen=True, repr=False, eq=False) +class MediumLevelILBlockToExpand(MediumLevelILInstruction): + @property + def exprs(self) -> List[MediumLevelILInstruction]: + return self._get_expr_list(0, 1) + + @property + def detailed_operands(self) -> List[Tuple[str, MediumLevelILOperandType, str]]: + return [("exprs", self.exprs, "List[MediumLevelILInstruction]")] ILInstruction = { @@ -3241,6 +3407,8 @@ ILInstruction = { MediumLevelILOperation.MLIL_LOAD: MediumLevelILLoad, # [("src", "expr")], MediumLevelILOperation.MLIL_VAR: MediumLevelILVar, # [("src", "var")], MediumLevelILOperation.MLIL_ADDRESS_OF: MediumLevelILAddressOf, # [("src", "var")], + MediumLevelILOperation.MLIL_PASS_BY_REF: MediumLevelILPassByRef, # [("src", "expr")], + MediumLevelILOperation.MLIL_RETURN_BY_REF: MediumLevelILReturnByRef, # [("src", "expr")], MediumLevelILOperation.MLIL_CONST: MediumLevelILConst, # [("constant", "int")], MediumLevelILOperation.MLIL_CONST_PTR: MediumLevelILConstPtr, # [("constant", "int")], MediumLevelILOperation.MLIL_FLOAT_CONST: MediumLevelILFloatConst, # [("constant", "float")], @@ -3285,6 +3453,8 @@ ILInstruction = { MediumLevelILOperation.MLIL_SEPARATE_PARAM_LIST: MediumLevelILSeparateParamList, # [("src", "expr_list")], MediumLevelILOperation.MLIL_SHARED_PARAM_SLOT: MediumLevelILSharedParamSlot, # [("src", "expr_list")], MediumLevelILOperation.MLIL_VAR_OUTPUT: MediumLevelILVarOutput, # [("dest", "var")], + MediumLevelILOperation.MLIL_VAR_OUTPUT_FIELD: MediumLevelILVarOutputField, # [("dest", "var"), ("offset", "int")], + MediumLevelILOperation.MLIL_STORE_OUTPUT: MediumLevelILStoreOutput, # [("dest", "expr")], MediumLevelILOperation.MLIL_RET: MediumLevelILRet, # [("src", "expr_list")], MediumLevelILOperation.MLIL_GOTO: MediumLevelILGoto, # [("dest", "int")], MediumLevelILOperation.MLIL_BOOL_TO_INT: MediumLevelILBoolToInt, # [("src", "expr")], @@ -3305,6 +3475,12 @@ ILInstruction = { MediumLevelILOperation.MLIL_VAR_SSA: MediumLevelILVarSsa, # [("src", "var_ssa")], MediumLevelILOperation.MLIL_VAR_ALIASED: MediumLevelILVarAliased, # [("src", "var_ssa")], MediumLevelILOperation.MLIL_VAR_OUTPUT_SSA: MediumLevelILVarOutputSsa, # [("dest", "var_ssa")], + MediumLevelILOperation.MLIL_VAR_OUTPUT_SSA_FIELD: + MediumLevelILVarOutputSsaField, # [("prev", "var_ssa_dest_and_src"), ("offset", "int")], + MediumLevelILOperation.MLIL_VAR_OUTPUT_ALIASED: + MediumLevelILVarOutputAliased, # [("prev", "var_ssa_dest_and_src")], + MediumLevelILOperation.MLIL_VAR_OUTPUT_ALIASED_FIELD: + MediumLevelILVarOutputAliasedField, # [("prev", "var_ssa_dest_and_src"), ("offset", "int")], MediumLevelILOperation.MLIL_CMP_E: MediumLevelILCmpE, # [("left", "expr"), ("right", "expr")], MediumLevelILOperation.MLIL_CMP_NE: MediumLevelILCmpNe, # [("left", "expr"), ("right", "expr")], MediumLevelILOperation.MLIL_CMP_SLT: MediumLevelILCmpSlt, # [("left", "expr"), ("right", "expr")], @@ -3399,6 +3575,7 @@ ILInstruction = { MediumLevelILOperation.MLIL_ASSERT_SSA: MediumLevelILAssertSsa, MediumLevelILOperation.MLIL_FORCE_VER: MediumLevelILForceVer, MediumLevelILOperation.MLIL_FORCE_VER_SSA: MediumLevelILForceVerSsa, + MediumLevelILOperation.MLIL_BLOCK_TO_EXPAND: MediumLevelILBlockToExpand, # [("exprs", "expr_list")], } @@ -3857,6 +4034,9 @@ class MediumLevelILFunction: if expr.operation == MediumLevelILOperation.MLIL_VAR_OUTPUT: expr: MediumLevelILVarOutput return dest.var_output(expr.size, expr.dest, loc) + if expr.operation == MediumLevelILOperation.MLIL_VAR_OUTPUT_FIELD: + expr: MediumLevelILVarOutputField + return dest.var_output_field(expr.size, expr.dest, expr.offset, loc) if expr.operation == MediumLevelILOperation.MLIL_FORCE_VER: expr: MediumLevelILForceVer return dest.force_ver(expr.size, expr.dest, expr.src, loc) @@ -3937,6 +4117,9 @@ class MediumLevelILFunction: if expr.operation == MediumLevelILOperation.MLIL_STORE_STRUCT: expr: MediumLevelILStoreStruct return dest.store_struct(expr.size, sub_expr_handler(expr.dest), expr.offset, sub_expr_handler(expr.src), loc) + if expr.operation == MediumLevelILOperation.MLIL_STORE_OUTPUT: + expr: MediumLevelILStoreOutput + return dest.store_output(expr.size, sub_expr_handler(expr.dest), loc) if expr.operation == MediumLevelILOperation.MLIL_LOAD: expr: MediumLevelILLoad return dest.load(expr.size, sub_expr_handler(expr.src), loc) @@ -3964,7 +4147,9 @@ class MediumLevelILFunction: MediumLevelILOperation.MLIL_ROUND_TO_INT, MediumLevelILOperation.MLIL_FLOOR, MediumLevelILOperation.MLIL_CEIL, - MediumLevelILOperation.MLIL_FTRUNC + MediumLevelILOperation.MLIL_FTRUNC, + MediumLevelILOperation.MLIL_PASS_BY_REF, + MediumLevelILOperation.MLIL_RETURN_BY_REF ]: expr: MediumLevelILUnaryBase return dest.expr(expr.operation, sub_expr_handler(expr.src), size=expr.size, source_location=loc) @@ -4097,6 +4282,10 @@ class MediumLevelILFunction: if expr.operation == MediumLevelILOperation.MLIL_UNIMPL: expr: MediumLevelILUnimpl return dest.unimplemented(loc) + if expr.operation == MediumLevelILOperation.MLIL_BLOCK_TO_EXPAND: + expr: MediumLevelILBlockToExpand + params = [sub_expr_handler(src) for src in expr.src] + return dest.block_to_expand(params, loc) raise NotImplementedError(f"unknown expr operation {expr.operation} in copy_expr_to") new_index = do_copy(expr, dest, sub_expr_handler) @@ -4410,6 +4599,20 @@ class MediumLevelILFunction: """ return self.expr(MediumLevelILOperation.MLIL_STORE_STRUCT, dest, offset, src, size=size, source_location=loc) + def store_output( + self, size: int, dest: ExpressionIndex, loc: Optional['ILSourceLocation'] = None + ) -> ExpressionIndex: + """ + ``store_output`` Outputs ``size`` bytes to expression ``dest`` as the result of a call + + :param int size: number of bytes to write + :param ExpressionIndex dest: the expression to write to + :param ILSourceLocation loc: location of returned expression + :return: The expression ``[dest].size`` + :rtype: ExpressionIndex + """ + return self.expr(MediumLevelILOperation.MLIL_STORE_OUTPUT, dest, size=size, source_location=loc) + def var(self, size: int, src: 'variable.CoreVariable', loc: Optional['ILSourceLocation'] = None) -> ExpressionIndex: """ ``var`` returns the variable ``src`` of size ``size`` @@ -4454,7 +4657,7 @@ class MediumLevelILFunction: def var_output(self, size: int, dest: 'variable.CoreVariable', loc: Optional['ILSourceLocation'] = None) -> ExpressionIndex: """ - ``var`` returns the output variable ``dest`` of size ``size`` + ``var_output`` returns the output variable ``dest`` of size ``size`` :param int size: the size of the variable in bytes :param Variable dest: the variable being written @@ -4464,6 +4667,21 @@ class MediumLevelILFunction: """ return self.expr(MediumLevelILOperation.MLIL_VAR_OUTPUT, dest.identifier, size=size, source_location=loc) + def var_output_field( + self, size: int, dest: 'variable.CoreVariable', offset: int, loc: Optional['ILSourceLocation'] = None + ) -> ExpressionIndex: + """ + ``var_output_field`` returns the output field at offset ``offset`` from variable ``dest`` of size ``size`` + + :param int size: the size of the field in bytes + :param Variable dest: the variable being written + :param int offset: offset of field in the variable + :param ILSourceLocation loc: location of returned expression + :return: The expression ``var:offset.size`` + :rtype: ExpressionIndex + """ + return self.expr(MediumLevelILOperation.MLIL_VAR_OUTPUT_FIELD, dest.identifier, offset, size=size, source_location=loc) + def assert_expr( self, size: int, @@ -4516,6 +4734,30 @@ class MediumLevelILFunction: """ return self.expr(MediumLevelILOperation.MLIL_ADDRESS_OF, var.identifier, size=0, source_location=loc) + def pass_by_ref(self, size: int, value: ExpressionIndex, loc: Optional['ILSourceLocation'] = None) -> ExpressionIndex: + """ + ``pass_by_ref`` indicates that ``value`` is being passed by reference to a call with a pointer size of ``size`` + + :param int size: the size of the pointer in bytes + :param ExpressionIndex value: the expression containing the reference being passed + :param ILSourceLocation loc: location of returned expression + :return: The expression ``ref *value`` + :rtype: ExpressionIndex + """ + return self.expr(MediumLevelILOperation.MLIL_PASS_BY_REF, value, size=size, source_location=loc) + + def return_by_ref(self, size: int, dest: ExpressionIndex, loc: Optional['ILSourceLocation'] = None) -> ExpressionIndex: + """ + ``return_by_ref`` indicates that ``dest`` is being returned by passing a reference to a call + + :param int size: the size of the value in bytes + :param ExpressionIndex dest: the expression containing the target of the return value + :param ILSourceLocation loc: location of returned expression + :return: The expression ``ref dest`` + :rtype: ExpressionIndex + """ + return self.expr(MediumLevelILOperation.MLIL_RETURN_BY_REF, dest, size=size, source_location=loc) + def address_of_field(self, var: 'variable.CoreVariable', offset: int, loc: Optional['ILSourceLocation'] = None) -> ExpressionIndex: """ ``address_of_field`` takes the address of ``var`` at the offset ``offset`` @@ -5956,6 +6198,18 @@ class MediumLevelILFunction: """ return self.expr(MediumLevelILOperation.MLIL_FCMP_UO, a, b, size=size, source_location=loc) + def block_to_expand(self, exprs: List[ExpressionIndex], loc: Optional['ILSourceLocation'] = None) -> ExpressionIndex: + """ + ``block_to_expand`` returns an expression to expand into multiple expressions. This expression must + be expanded by a future workflow step and is used temporarily to insert instructions. + + :param List[ExpressionIndex] exprs: list of expressions + :param ILSourceLocation loc: location of returned expression + :return: The expression ``{ exprs... }`` + :rtype: ExpressionIndex + """ + return self.expr(MediumLevelILOperation.MLIL_BLOCK_TO_EXPAND, len(exprs), self.add_operand_list(exprs), size=0, source_location=loc) + def goto( self, label: MediumLevelILLabel, loc: Optional['ILSourceLocation'] = None ) -> ExpressionIndex: diff --git a/python/platform.py b/python/platform.py index beaf5d75..2e8ad846 100644 --- a/python/platform.py +++ b/python/platform.py @@ -401,7 +401,7 @@ class Platform(metaclass=_PlatformMetaClass): result = core.BNGetPlatformDefaultCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(handle=result) + return callingconvention.CoreCallingConvention(handle=result) @default_calling_convention.setter def default_calling_convention(self, value): @@ -415,7 +415,7 @@ class Platform(metaclass=_PlatformMetaClass): result = core.BNGetPlatformCdeclCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(handle=result) + return callingconvention.CoreCallingConvention(handle=result) @cdecl_calling_convention.setter def cdecl_calling_convention(self, value): @@ -432,7 +432,7 @@ class Platform(metaclass=_PlatformMetaClass): result = core.BNGetPlatformStdcallCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(handle=result) + return callingconvention.CoreCallingConvention(handle=result) @stdcall_calling_convention.setter def stdcall_calling_convention(self, value): @@ -449,7 +449,7 @@ class Platform(metaclass=_PlatformMetaClass): result = core.BNGetPlatformFastcallCallingConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(handle=result) + return callingconvention.CoreCallingConvention(handle=result) @fastcall_calling_convention.setter def fastcall_calling_convention(self, value): @@ -466,7 +466,7 @@ class Platform(metaclass=_PlatformMetaClass): result = core.BNGetPlatformSystemCallConvention(self.handle) if result is None: return None - return callingconvention.CallingConvention(handle=result) + return callingconvention.CoreCallingConvention(handle=result) @system_call_convention.setter def system_call_convention(self, value): @@ -488,7 +488,7 @@ class Platform(metaclass=_PlatformMetaClass): assert cc is not None, "core.BNGetPlatformCallingConventions returned None" result = [] for i in range(0, count.value): - result.append(callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i]))) + result.append(callingconvention.CoreCallingConvention(handle=core.BNNewCallingConventionReference(cc[i]))) core.BNFreeCallingConventionList(cc, count.value) return result diff --git a/python/types.py b/python/types.py index 80c70e14..04ec5e78 100644 --- a/python/types.py +++ b/python/types.py @@ -28,7 +28,7 @@ import uuid from . import _binaryninjacore as core from .enums import ( InlineDuringAnalysis, StructureVariant, SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass, - ReferenceType, VariableSourceType, + ReferenceType, VariableSourceType, ValueLocationSource, TypeReferenceType, MemberAccess, MemberScope, TypeDefinitionLineType, TokenEscapingType, NameType, PointerSuffix, PointerBaseType, @@ -39,6 +39,7 @@ from . import function as _function from . import variable from . import architecture from . import binaryview +from . import function from . import platform as _platform from . import typecontainer from . import typelibrary @@ -51,11 +52,13 @@ ParamsType = Union[List['Type'], List['FunctionParameter'], List[Tuple[str, 'Typ MembersType = Union[List['StructureMember'], List['Type'], List[Tuple['Type', str]]] EnumMembersType = Union[List[Tuple[str, int]], List[str], List['EnumerationMember']] SomeType = Union['TypeBuilder', 'Type'] +ReturnValueOrType = Union['TypeBuilder', 'Type', 'ReturnValue'] TypeContainerType = Union['binaryview.BinaryView', 'typelibrary.TypeLibrary'] NameSpaceType = Optional[Union[str, List[str], 'NameSpace']] TypeParserResult = typeparser.TypeParserResult BasicTypeParserResult = typeparser.BasicTypeParserResult ResolveMemberCallback = Callable[['NamedTypeReferenceType', 'StructureType', int, int, int, 'StructureMember'], None] +OptionalLocation = Optional[Union['ValueLocation', 'ValueLocationWithConfidence', 'variable.CoreVariable']] # The following are needed to prevent the type checker from getting # confused as we have member functions in `Type` named the same thing _int = int @@ -437,22 +440,229 @@ class Symbol(CoreSymbol): @dataclass +class ValueLocationComponent: + var: 'variable.CoreVariable' + offset: int = 0 + size: Optional[int] = None + + @staticmethod + def _from_core_struct(struct: core.BNValueLocationComponent, arch: Optional['architecture.Architecture'] = None): + if arch is None: + var = variable.CoreVariable.from_BNVariable(struct.variable) + else: + var = variable.ArchitectureVariable.from_BNVariable(arch, struct.variable) + offset = struct.offset + size = None + if struct.sizeValid: + size = struct.size + return ValueLocationComponent(var, offset, size) + + def _to_core_struct(self) -> core.BNValueLocationComponent: + struct = core.BNValueLocationComponent() + struct.variable = self.var.to_BNVariable() + struct.offset = self.offset + struct.sizeValid = self.size is not None + if self.size is not None: + struct.size = self.size + return struct + + def to_string(self, arch: Optional['architecture.Architecture']): + if arch is None: + if isinstance(self.var, variable.ArchitectureVariable): + arch = self.var.arch + elif isinstance(self.var, variable.Variable): + arch = self.var.function.arch + if arch is None: + return f"{repr(self.var)} offset {hex(self.offset)} size {repr(self.size)}" + struct = self._to_core_struct() + return core.BNValueLocationComponentToString(struct, arch.handle) + + def __str__(self): + return self.to_string(None) + + def __repr__(self): + return f"<component {self.to_string(None)}>" + + +@dataclass +class ValueLocation: + components: List['ValueLocationComponent'] + indirect: bool = False + returned_pointer: Optional['variable.CoreVariable'] = None + + @staticmethod + def _from_core_struct(struct: core.BNValueLocation, arch: Optional['architecture.Architecture'] = None): + components = [] + for i in range(struct.count): + components.append(ValueLocationComponent._from_core_struct(struct.components[i], arch)) + indirect = struct.indirect + returned_pointer = None + if struct.returnedPointerValid: + if arch is None: + returned_pointer = variable.CoreVariable.from_BNVariable(struct.returnedPointer) + else: + returned_pointer = variable.ArchitectureVariable.from_BNVariable(arch, struct.returnedPointer) + return ValueLocation(components, indirect, returned_pointer) + + def _to_core_struct(self) -> core.BNValueLocation: + struct = core.BNValueLocation() + struct.count = len(self.components) + components = (core.BNValueLocationComponent * len(self.components))() + for i in range(len(self.components)): + components[i] = self.components[i]._to_core_struct() + struct.components = components + struct.indirect = self.indirect + struct.returnedPointerValid = self.returned_pointer is not None + if self.returned_pointer is not None: + struct.returnedPointer = self.returned_pointer.to_BNVariable() + return struct + + def with_confidence(self, confidence: int) -> 'ValueLocationWithConfidence': + return ValueLocationWithConfidence(self, confidence) + + def variable_for_parameter(self, idx: int) -> Optional['variable.CoreVariable']: + struct = self._to_core_struct() + var = core.BNVariable() + if core.BNGetValueLocationVariableForParameter(struct, var, idx): + return variable.CoreVariable.from_BNVariable(var) + return None + + @staticmethod + def parse(string: str, arch: 'architecture.Architecture') -> 'ValueLocation': + struct = core.BNValueLocation() + error = ctypes.c_char_p() + if not core.BNParseValueLocation(string, arch.handle, struct, error): + assert error.value is not None, "core.BNParseValueLocation returned 'error' set to None" + error_str = error.value.decode("utf-8") + core.free_string(error) + raise SyntaxError(error_str) + result = ValueLocation._from_core_struct(struct, arch) + core.BNFreeValueLocation(struct) + return result + + def to_string(self, arch: Optional['architecture.Architecture']): + if arch is None: + for component in self.components: + if isinstance(component.var, variable.ArchitectureVariable): + arch = component.var.arch + break + if isinstance(component.var, variable.Variable): + arch = component.var.function.arch + break + if arch is None: + if self.indirect: + indirect = " indirect" + else: + indirect = "" + if self.returned_pointer is None: + ret_ptr = "" + else: + ret_ptr = f" returned ptr {repr(self.returned_pointer)}" + return f"{repr(self.components)}{indirect}{ret_ptr}" + struct = self._to_core_struct() + return core.BNValueLocationToString(struct, arch.handle) + + def __str__(self): + return self.to_string(None) + + def __repr__(self): + return f"<value location {self.to_string(None)}>" + + +@dataclass +class ValueLocationWithConfidence: + location: 'ValueLocation' + confidence: int = core.max_confidence + + @staticmethod + def from_optional_location(location: OptionalLocation) -> Optional['ValueLocationWithConfidence']: + if isinstance(location, ValueLocation): + return location.with_confidence(core.max_confidence) + elif isinstance(location, ValueLocationWithConfidence): + return location + elif location is not None: + return ValueLocation([ValueLocationComponent(location)]).with_confidence(core.max_confidence) + return None + + def __repr__(self): + return f"<value location {self.location.to_string(None)} confidence {self.confidence}>" + + +@dataclass +class ReturnValue: + type: SomeType + location: Optional['ValueLocationWithConfidence'] + + def __init__(self, ty: SomeType, location: OptionalLocation = None): + self.type = ty.immutable_copy() + self.location = ValueLocationWithConfidence.from_optional_location(location) + + @staticmethod + def _from_core_struct(struct: core.BNReturnValue, arch: Optional['architecture.Architecture'] = None): + ty = Type.from_core_struct(struct.type).with_confidence(struct.typeConfidence) + if struct.defaultLocation: + location = None + else: + location = ValueLocation._from_core_struct(struct.location, arch).with_confidence(struct.locationConfidence) + return ReturnValue(ty, location) + + def _to_core_struct(self) -> core.BNReturnValue: + struct = core.BNReturnValue() + ic = self.type.immutable_copy() + struct.type = ic.handle + struct.typeConfidence = ic.confidence + struct.defaultLocation = self.location is None + if self.location is None: + struct.location.count = 0 + struct.locationConfidence = 0 + else: + struct.location = self.location.location._to_core_struct() + struct.locationConfidence = self.location.confidence + return struct + + +@dataclass class FunctionParameter: type: SomeType name: str = "" - location: Optional['variable.VariableNameAndType'] = None + location_source: ValueLocationSource = ValueLocationSource.DefaultLocationSource + location: Optional['ValueLocation'] = None + + def __init__(self, type: SomeType, name: str = "", location: OptionalLocation = None, source: Optional['ValueLocationSource'] = None): + self.type = type + self.name = name + location = ValueLocationWithConfidence.from_optional_location(location) + if location is not None: + self.location = location.location + self.location_source = ValueLocationSource.CustomLocationSource + else: + self.location = None + self.location_source = ValueLocationSource.DefaultLocationSource + if source is not None: + self.location_source = source def __repr__(self): ic = self.type.immutable_copy() - if (self.location is not None) and (self.location.name != self.name): - return f"{ic.get_string_before_name()} {self.name}{ic.get_string_after_name()} @ {self.location.name}" + if (self.location is not None) and (str(self.location) != self.name): + return f"{ic.get_string_before_name()} {self.name}{ic.get_string_after_name()} @ {self.location}" return f"{ic.get_string_before_name()} {self.name}{ic.get_string_after_name()}" def immutable_copy(self) -> 'FunctionParameter': - return FunctionParameter(self.type.immutable_copy(), self.name, self.location) + return FunctionParameter(self.type.immutable_copy(), self.name, self.location, self.location_source) def mutable_copy(self) -> 'FunctionParameter': - return FunctionParameter(self.type.mutable_copy(), self.name, self.location) + return FunctionParameter(self.type.mutable_copy(), self.name, self.location, self.location_source) + + @staticmethod + def _from_core_struct(struct: 'core.BNFunctionParameter', arch: Optional['architecture.Architecture'] = None) -> 'FunctionParameter': + name = struct.name + ty = Type.from_core_struct(struct.type).with_confidence(struct.typeConfidence) + source = ValueLocationSource(struct.locationSource) + if source == ValueLocationSource.CustomLocationSource: + location = ValueLocation._from_core_struct(struct.location, arch) + else: + location = None + return FunctionParameter(ty, name, location, source) @dataclass(frozen=True) @@ -760,7 +970,7 @@ class TypeBuilder: @staticmethod def function( - ret: Optional[SomeType] = None, params: Optional[ParamsType] = None, + ret: Optional[ReturnValueOrType] = None, params: Optional[ParamsType] = None, calling_convention: Optional['callingconvention.CallingConvention'] = None, variable_arguments: Optional[BoolWithConfidenceType] = None, stack_adjust: Optional[OffsetWithConfidenceType] = None @@ -1231,20 +1441,22 @@ class ArrayBuilder(TypeBuilder): class FunctionBuilder(TypeBuilder): @classmethod def create( - cls, return_type: Optional[SomeType] = None, + cls, return_type: Optional[ReturnValueOrType] = None, calling_convention: Optional['callingconvention.CallingConvention'] = None, params: Optional[ParamsType] = None, var_args: Optional[BoolWithConfidenceType] = None, stack_adjust: Optional[OffsetWithConfidenceType] = None, platform: Optional['_platform.Platform'] = None, confidence: int = core.max_confidence, can_return: Optional[BoolWithConfidence] = None, reg_stack_adjust: Optional[Dict['architecture.RegisterName', OffsetWithConfidenceType]] = None, - return_regs: Optional[Union['RegisterSet', List['architecture.RegisterType']]] = None, name_type: 'NameType' = NameType.NoNameType, pure: Optional[BoolWithConfidence] = None ) -> 'FunctionBuilder': param_buf, type_list = FunctionBuilder._to_core_struct(params) if return_type is None: - ret_conf = Type.void() + ret = ReturnValue(Type.void()) + elif isinstance(return_type, ReturnValue): + ret = return_type else: - ret_conf = return_type.immutable_copy() + ret = ReturnValue(return_type) + ret_conf = ret._to_core_struct() conv_conf = core.BNCallingConventionWithConfidence() if calling_convention is None: @@ -1264,18 +1476,6 @@ class FunctionBuilder(TypeBuilder): reg_stack_adjust_values[i].value = adjust.value reg_stack_adjust_values[i].confidence = adjust.confidence - return_regs_set = core.BNRegisterSetWithConfidence() - if return_regs is None or platform is None: - return_regs_set.count = 0 - return_regs_set.confidence = 0 - else: - return_regs_set.count = len(return_regs) - return_regs_set.confidence = 255 - return_regs_set.regs = (ctypes.c_uint32 * len(return_regs))() - - for i, reg in enumerate(return_regs): - return_regs_set[i] = platform.arch.get_reg_index(reg) - if var_args is None: vararg_conf = BoolWithConfidence.get_core_struct(False, 0) else: @@ -1298,9 +1498,9 @@ class FunctionBuilder(TypeBuilder): if params is None: params = [] handle = core.BNCreateFunctionTypeBuilder( - ret_conf._to_core_struct(), conv_conf, param_buf, len(params), vararg_conf, can_return_conf, stack_adjust_conf, + ret_conf, conv_conf, param_buf, len(params), vararg_conf, can_return_conf, stack_adjust_conf, reg_stack_adjust_regs, reg_stack_adjust_values, len(reg_stack_adjust), - return_regs_set, name_type, pure_conf + name_type, pure_conf ) assert handle is not None, "BNCreateFunctionTypeBuilder returned None" return cls(handle, platform, confidence) @@ -1317,6 +1517,31 @@ class FunctionBuilder(TypeBuilder): def return_value(self, value: SomeType) -> None: self.child = value + @property + def return_value_location(self) -> Optional[ValueLocationWithConfidence]: + location = core.BNGetTypeBuilderReturnValueLocation(self._handle) + if self.platform is None: + arch = None + else: + arch = self.platform.arch + result = ValueLocation._from_core_struct(location.location, arch).with_confidence(location.confidence) + core.BNFreeValueLocation(location.location) + return result + + @return_value_location.setter + def return_value_location(self, value: OptionalLocation): + struct = core.BNValueLocationWithConfidence() + location = ValueLocationWithConfidence.from_optional_location(value) + if location is None: + struct.location.count = 0 + struct.confidence = 0 + else: + struct.location = location.location._to_core_struct() + struct.confidence = location.confidence + core.BNTypeBuilderSetIsReturnValueDefaultLocation(self._handle, value is None) + if value is not None: + core.BNTypeBuilderSetReturnValueLocation(self._handle, struct) + def append(self, type: Union[SomeType, FunctionParameter], name: str = ""): if isinstance(type, FunctionParameter): self.parameters = [*self.parameters, type] @@ -1326,7 +1551,7 @@ class FunctionBuilder(TypeBuilder): @property def calling_convention(self) -> 'callingconvention.CallingConvention': cc = core.BNGetTypeBuilderCallingConvention(self._handle) - return callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc.convention)) + return callingconvention.CoreCallingConvention(handle=core.BNNewCallingConventionReference(cc.convention)) @property def can_return(self) -> BoolWithConfidence: @@ -1366,24 +1591,21 @@ class FunctionBuilder(TypeBuilder): count = ctypes.c_ulonglong() params = core.BNGetTypeBuilderParameters(self._handle, count) assert params is not None, "core.BNGetTypeBuilderParameters returned None" + if self.platform is None: + arch = None + else: + arch = self.platform.arch result = [] for i in range(0, count.value): param_type = Type.create( core.BNNewTypeReference(params[i].type), platform=self.platform, confidence=params[i].typeConfidence ) - if params[i].defaultLocation: - param_location = None + source = ValueLocationSource(params[i].locationSource) + if source == ValueLocationSource.CustomLocationSource: + param_location = ValueLocation._from_core_struct(params[i].location, arch) else: - name = params[i].name - if (params[i].location.type - == VariableSourceType.RegisterVariableSourceType) and (self.platform is not None): - name = self.platform.arch.get_reg_name(params[i].location.storage) - elif params[i].location.type == VariableSourceType.StackVariableSourceType: - name = "arg_%x" % params[i].location.storage - param_location = variable.VariableNameAndType( - params[i].location.type, params[i].location.index, params[i].location.storage, name, param_type - ) - result.append(FunctionParameter(param_type, params[i].name, param_location)) + param_location = None + result.append(FunctionParameter(param_type, params[i].name, param_location, source)) core.BNFreeTypeParameterList(params, count.value) return result @@ -1411,7 +1633,8 @@ class FunctionBuilder(TypeBuilder): core_param.name = "" core_param.type = param.handle core_param.typeConfidence = param.confidence - core_param.defaultLocation = True + core_param.locationSource = int(ValueLocationSource.DefaultLocationSource) + core_param.location.count = 0 elif isinstance(param, FunctionParameter): assert param.type is not None, "Attempting to construct function parameter without properly constructed type" param_type = param.type.immutable_copy() @@ -1419,13 +1642,16 @@ class FunctionBuilder(TypeBuilder): core_param.name = param.name core_param.type = param_type.handle core_param.typeConfidence = param_type.confidence + core_param.locationSource = int(param.location_source) if param.location is None: - core_param.defaultLocation = True + core_param.location.count = 0 else: - core_param.defaultLocation = False - core_param.location.type = param.location.source_type - core_param.location.index = param.location.index - core_param.location.storage = param.location.storage + if isinstance(param.location, ValueLocation): + core_param.location = param.location._to_core_struct() + elif isinstance(param.location, variable.CoreVariable): + core_param.location = ValueLocation([ValueLocationComponent(param.location)])._to_core_struct() + else: + raise ValueError(f"Conversion from unsupported parameter location type {type(param.location)}") elif isinstance(param, tuple): name, _type = param if not isinstance(name, str) or not isinstance(_type, (Type, TypeBuilder)): @@ -1435,7 +1661,8 @@ class FunctionBuilder(TypeBuilder): core_param.name = name core_param.type = _type.handle core_param.typeConfidence = _type.confidence - core_param.defaultLocation = True + core_param.locationSource = int(ValueLocationSource.DefaultLocationSource) + core_param.location.count = 0 else: raise ValueError(f"Conversion from unsupported function parameter type {type(param)}") return param_buf, type_list @@ -2556,7 +2783,7 @@ class Type: @staticmethod def function( - ret: Optional['Type'] = None, params: Optional[ParamsType] = None, + ret: Optional[Union['Type', 'ReturnValue']] = None, params: Optional[ParamsType] = None, calling_convention: Optional['callingconvention.CallingConvention'] = None, variable_arguments: BoolWithConfidenceType = False, stack_adjust: OffsetWithConfidence = OffsetWithConfidence(0) @@ -3227,18 +3454,19 @@ class ArrayType(Type): class FunctionType(Type): @classmethod def create( - cls, ret: Optional[Type] = None, params: Optional[ParamsType] = None, + cls, ret: Optional[Union[Type, ReturnValue]] = None, params: Optional[ParamsType] = None, calling_convention: Optional['callingconvention.CallingConvention'] = None, variable_arguments: BoolWithConfidenceType = BoolWithConfidence(False), stack_adjust: OffsetWithConfidence = OffsetWithConfidence(0), platform: Optional['_platform.Platform'] = None, confidence: int = core.max_confidence, can_return: Union[BoolWithConfidence, bool] = True, reg_stack_adjust: Optional[Dict['architecture.RegisterName', OffsetWithConfidenceType]] = None, - return_regs: Optional[Union['RegisterSet', List['architecture.RegisterType']]] = None, name_type: 'NameType' = NameType.NoNameType, pure: Union[BoolWithConfidence, bool] = False ) -> 'FunctionType': if ret is None: - ret = VoidType.create() + ret = ReturnValue(VoidType.create()) + elif not isinstance(ret, ReturnValue): + ret = ReturnValue(ret) if params is None: params = [] param_buf, type_list = FunctionBuilder._to_core_struct(params) @@ -3272,18 +3500,6 @@ class FunctionType(Type): reg_stack_adjust_values[i].value = adjust.value reg_stack_adjust_values[i].confidence = adjust.confidence - return_regs_set = core.BNRegisterSetWithConfidence() - if return_regs is None or platform is None: - return_regs_set.count = 0 - return_regs_set.confidence = 0 - else: - return_regs_set.count = len(return_regs) - return_regs_set.confidence = 255 - return_regs_set.regs = (ctypes.c_uint32 * len(return_regs))() - - for i, reg in enumerate(return_regs): - return_regs_set[i] = platform.arch.get_reg_index(reg) - _can_return = BoolWithConfidence.get_core_struct(can_return) _pure = BoolWithConfidence.get_core_struct(pure) if params is None: @@ -3291,7 +3507,7 @@ class FunctionType(Type): func_type = core.BNCreateFunctionType( ret_conf, conv_conf, param_buf, len(params), _variable_arguments, _can_return, _stack_adjust, reg_stack_adjust_regs, reg_stack_adjust_values, len(reg_stack_adjust), - return_regs_set, name_type, _pure + name_type, _pure ) assert func_type is not None, f"core.BNCreateFunctionType returned None {ret_conf} {conv_conf} {param_buf} {_variable_arguments} {_stack_adjust}" @@ -3312,12 +3528,26 @@ class FunctionType(Type): return Type.create(result.type, platform=self._platform, confidence=result.confidence) @property + def return_value_location(self) -> Optional[ValueLocationWithConfidence]: + """Return value location (read-only)""" + if core.BNIsTypeReturnValueDefaultLocation(self._handle): + return None + location = core.BNGetTypeReturnValueLocation(self._handle) + if self._platform is None: + arch = None + else: + arch = self._platform.arch + result = ValueLocation._from_core_struct(location.location, arch).with_confidence(location.confidence) + core.BNFreeValueLocation(location.location) + return result + + @property def calling_convention(self) -> Optional[callingconvention.CallingConvention]: """Calling convention (read-only)""" result = core.BNGetTypeCallingConvention(self._handle) if not result.convention: return None - return callingconvention.CallingConvention(None, handle=result.convention, confidence=result.confidence) + return callingconvention.CoreCallingConvention(handle=result.convention, confidence=result.confidence) @property def parameters(self) -> List[FunctionParameter]: @@ -3325,24 +3555,21 @@ class FunctionType(Type): count = ctypes.c_ulonglong() params = core.BNGetTypeParameters(self._handle, count) assert params is not None, "core.BNGetTypeParameters returned None" + if self._platform is None: + arch = None + else: + arch = self._platform.arch result = [] for i in range(0, count.value): param_type = Type.create( core.BNNewTypeReference(params[i].type), platform=self._platform, confidence=params[i].typeConfidence ) - if params[i].defaultLocation: - param_location = None + source = ValueLocationSource(params[i].locationSource) + if source == ValueLocationSource.CustomLocationSource: + param_location = ValueLocation._from_core_struct(params[i].location, arch) else: - name = params[i].name - if (params[i].location.type - == VariableSourceType.RegisterVariableSourceType) and (self._platform is not None): - name = self._platform.arch.get_reg_name(params[i].location.storage) - elif params[i].location.type == VariableSourceType.StackVariableSourceType: - name = "arg_%x" % params[i].location.storage - param_location = variable.VariableNameAndType( - params[i].location.type, params[i].location.index, params[i].location.storage, name, param_type - ) - result.append(FunctionParameter(param_type, params[i].name, param_location)) + param_location = None + result.append(FunctionParameter(param_type, params[i].name, param_location, source)) core.BNFreeTypeParameterList(params, count.value) return result @@ -3352,21 +3579,18 @@ class FunctionType(Type): count = ctypes.c_ulonglong() params = core.BNGetTypeParameters(self._handle, count) assert params is not None, "core.BNGetTypeParameters returned None" + if self._platform is None: + arch = None + else: + arch = self._platform.arch result = [] for i in range(0, count.value): param_type = Type.create( core.BNNewTypeReference(params[i].type), platform=self._platform, confidence=params[i].typeConfidence ) - name = params[i].name - if (params[i].location.type - == VariableSourceType.RegisterVariableSourceType) and (self._platform is not None): - name = self._platform.arch.get_reg_name(params[i].location.storage) - elif params[i].location.type == VariableSourceType.StackVariableSourceType: - name = "arg_%x" % params[i].location.storage - param_location = variable.VariableNameAndType( - params[i].location.type, params[i].location.index, params[i].location.storage, name, param_type - ) - result.append(FunctionParameter(param_type, params[i].name, param_location)) + source = ValueLocationSource(params[i].locationSource) + param_location = ValueLocation._from_core_struct(params[i].location, arch) + result.append(FunctionParameter(param_type, params[i].name, param_location, source)) core.BNFreeTypeParameterList(params, count.value) return result diff --git a/python/variable.py b/python/variable.py index 878c1cc7..b4861571 100644 --- a/python/variable.py +++ b/python/variable.py @@ -27,6 +27,7 @@ import binaryninja from . import _binaryninjacore as core from . import databuffer from . import decorators +from . import types from .enums import RegisterValueType, VariableSourceType, DeadStoreElimination, FunctionGraphType, BuiltinType FunctionOrILFunction = Union["binaryninja.function.Function", "binaryninja.lowlevelil.LowLevelILFunction", @@ -111,6 +112,10 @@ class RegisterValue: return ConstantPointerRegisterValue(reg_value.value, confidence=confidence) elif reg_value.state == RegisterValueType.StackFrameOffset: return StackFrameOffsetRegisterValue(reg_value.value, confidence=confidence) + elif reg_value.state == RegisterValueType.ResultPointerValue: + return ResultPointerRegisterValue(reg_value.value, confidence=confidence) + elif reg_value.state == RegisterValueType.ParameterPointerValue: + return ParameterPointerRegisterValue(reg_value.value, reg_value.offset, confidence=confidence) elif reg_value.state == RegisterValueType.ImportedAddressValue: return ImportedAddressRegisterValue(reg_value.value, confidence=confidence) elif reg_value.state == RegisterValueType.UndeterminedValue: @@ -197,6 +202,23 @@ class StackFrameOffsetRegisterValue(RegisterValue): @dataclass(frozen=True, eq=False) +class ResultPointerRegisterValue(RegisterValue): + offset: int = 0 + type: RegisterValueType = RegisterValueType.ResultPointerValue + + def __repr__(self): + return f"<result ptr offset {self.value:#x}>" + +@dataclass(frozen=True, eq=False) +class ParameterPointerRegisterValue(RegisterValue): + offset: int = 0 + type: RegisterValueType = RegisterValueType.ParameterPointerValue + + def __repr__(self): + return f"<parameter {self.value} ptr offset {self.offset:#x}>" + + +@dataclass(frozen=True, eq=False) class ExternalPointerRegisterValue(RegisterValue): type: RegisterValueType = RegisterValueType.ExternalPointerValue @@ -287,6 +309,11 @@ class PossibleValueSet: self._value = value.value elif value.state == RegisterValueType.StackFrameOffset: self._offset = value.value + elif value.state == RegisterValueType.ResultPointerValue: + self._offset = value.value + elif value.state == RegisterValueType.ParameterPointerValue: + self._value = value.value + self._offset = value.offset elif value.state & RegisterValueType.ConstantDataValue == RegisterValueType.ConstantDataValue: self._value = value.value self._size = value.size @@ -334,6 +361,10 @@ class PossibleValueSet: return f"<const ptr {self.value:#x}>" if self._type == RegisterValueType.StackFrameOffset: return f"<stack frame offset {self._offset:#x}>" + if self._type == RegisterValueType.ResultPointerValue: + return f"<result ptr offset {self._offset:#x}>" + if self._type == RegisterValueType.ParameterPointerValue: + return f"<parameter {self._value} ptr offset {self._offset:#x}>" if self._type == RegisterValueType.ConstantDataZeroExtendValue: return f"<const data {{zx.{self._size}({self.value:#x})}}>" if self._type == RegisterValueType.ConstantDataSignExtendValue: @@ -364,7 +395,7 @@ class PossibleValueSet: if not isinstance(other, int): return NotImplemented #Initial implementation only checks numbers, no set logic - if self.type == RegisterValueType.StackFrameOffset: + if self.type in [RegisterValueType.StackFrameOffset, RegisterValueType.ResultPointerValue, RegisterValueType.ParameterPointerValue]: return NotImplemented if self.type in [RegisterValueType.SignedRangeValue, RegisterValueType.UnsignedRangeValue]: for rng in self.ranges: @@ -395,6 +426,10 @@ class PossibleValueSet: return self.value == other.value elif self.type == RegisterValueType.StackFrameOffset: return self.offset == other.offset + elif self.type == RegisterValueType.ResultPointerValue: + return self.offset == other.offset + elif self.type == RegisterValueType.ParameterPointerValue: + return self.value == other.value and self.offset == other.offset elif self.type & RegisterValueType.ConstantDataValue == RegisterValueType.ConstantDataValue: return self.value == other.value and self._size == other._size elif self.type in [RegisterValueType.SignedRangeValue, RegisterValueType.UnsignedRangeValue]: @@ -423,6 +458,11 @@ class PossibleValueSet: result.value = self.value elif self.type == RegisterValueType.StackFrameOffset: result.offset = self.offset + elif self.type == RegisterValueType.ResultPointerValue: + result.value = self.offset + elif self.type == RegisterValueType.ParameterPointerValue: + result.value = self.value + result.offset = self.offset elif self.type & RegisterValueType.ConstantDataValue == RegisterValueType.ConstantDataValue: result.value = self.value result.size = self.size @@ -559,6 +599,39 @@ class PossibleValueSet: return result @staticmethod + def result_pointer(offset: int) -> 'PossibleValueSet': + """ + Create a PossibleValueSet object for a pointer to the return value when the return value + is stored at an unknown location in memory. This is typically used for calling conventions + that pass in a pointer to the storage location for the return value. + + :param int offset: Integer value of the offset + :rtype: PossibleValueSet + """ + result = PossibleValueSet() + result._type = RegisterValueType.ResultPointerValue + result._value = offset + return result + + @staticmethod + def parameter_pointer(idx: int, offset: int) -> 'PossibleValueSet': + """ + Create a PossibleValueSet object for a pointer to a parameter when the parameter is + stored at an unknown location in memory. This is typically used for calling conventions + that pass in a pointer to the storage location for parameters (usually larger than + can be held in a register). + + :param int idx: Index of the parameter + :param int offset: Integer value of the offset + :rtype: PossibleValueSet + """ + result = PossibleValueSet() + result._type = RegisterValueType.ParameterPointerValue + result._value = idx + result._offset = offset + return result + + @staticmethod def signed_range_value(ranges: List[ValueRange]) -> 'PossibleValueSet': """ Create a PossibleValueSet object for a signed range of values. @@ -846,6 +919,18 @@ class CoreVariable: var = core.BNFromVariableIdentifier(identifier) return cls(var.type, var.index, var.storage) + @classmethod + def reg(cls, reg: int): + return cls(VariableSourceType.RegisterVariableSourceType, 0, int(reg)) + + @classmethod + def flag(cls, flag: int): + return cls(VariableSourceType.FlagVariableSourceType, 0, int(flag)) + + @classmethod + def stack_offset(cls, offset: int): + return cls(VariableSourceType.StackVariableSourceType, 0, int(offset)) + @dataclass(frozen=True, order=True) class VariableNameAndType(CoreVariable): @@ -872,6 +957,110 @@ class VariableNameAndType(CoreVariable): return cls(var.type, var.index, var.storage, name, type) +class ArchitectureVariable(CoreVariable): + """ + ``class ArchitectureVariable`` is a wrapper around :py:meth:`CoreVariable` that + is bound to an architecture (for register/flag naming) but not a function. This + is typically used in calling conventions for specifying value locations. Calling + conventions can be used outside functions to resolve type information, so only + an architecture is required. + """ + def __init__( + self, arch: 'binaryninja.architecture.Architecture', source_type: VariableSourceType, index: int, + storage: int + ): + super(ArchitectureVariable, self).__init__(int(source_type), index, storage) + self._arch = arch + + @property + def arch(self) -> 'binaryninja.architecture.Architecture': + return self._arch + + @classmethod + def reg(cls, arch: 'binaryninja.architecture.Architecture', reg: Union[str, int]): + if isinstance(reg, str): + if reg not in arch.regs: + raise ValueError(f"Invalid register name: {reg}") + reg = arch.regs[reg].index + return cls(arch, VariableSourceType.RegisterVariableSourceType, 0, int(reg)) + + @classmethod + def flag(cls, arch: 'binaryninja.architecture.Architecture', flag: Union[str, int]): + if isinstance(flag, str): + flag = arch.get_flag_by_name(flag) + return cls(arch, VariableSourceType.FlagVariableSourceType, 0, int(flag)) + + @classmethod + def stack_offset(cls, arch: 'binaryninja.architecture.Architecture', offset: int): + return cls(arch, VariableSourceType.StackVariableSourceType, 0, int(offset)) + + @property + def name(self) -> str: + if self.source_type == VariableSourceType.RegisterVariableSourceType: + return str(self._arch.get_reg_name(binaryninja.architecture.RegisterIndex(self.storage))) + if self.source_type == VariableSourceType.FlagVariableSourceType: + return str(self._arch.get_flag_name(binaryninja.architecture.FlagIndex(self.storage))) + return hex(self.storage) + + @classmethod + def from_core_variable(cls, arch: 'binaryninja.architecture.Architecture', var: CoreVariable): + return cls(arch, var.source_type, var.index, var.storage) + + @classmethod + def from_BNVariable(cls, arch: 'binaryninja.architecture.Architecture', var: core.BNVariable): + return cls(arch, var.type, var.index, var.storage) + + @classmethod + def from_identifier(cls, arch: 'binaryninja.architecture.Architecture', identifier: int): + var = core.BNFromVariableIdentifier(identifier) + return cls(arch, VariableSourceType(var.type), var.index, var.storage) + + def _sort_key(self): + if self._arch is None: + arch_key = "" + else: + arch_key = self._arch.name + return arch_key, self._source_type, self.index, self.storage + + def __repr__(self): + if self.source_type == VariableSourceType.StackVariableSourceType: + return f"<var @ stack offset {self.storage:#x}>" + return f"<var @ {self.name}>" + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return super().__eq__(other) and (self._arch == other._arch) + + def __ne__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return not (self == other) + + def __lt__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self._sort_key() < other._sort_key() + + def __gt__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self._sort_key() > other._sort_key() + + def __le__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self._sort_key() <= other._sort_key() + + def __ge__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self._sort_key() >= other._sort_key() + + def __hash__(self): + return hash((self._arch, super().__hash__())) + + class Variable(CoreVariable): """ ``class Variable`` represents variables in Binary Ninja. Variables are resolved @@ -1151,6 +1340,53 @@ class ParameterVariables: return self._func +@decorators.passive +class ParameterLocations: + def __init__( + self, location_list: List['types.ValueLocation'], confidence: int = core.max_confidence, + func: Optional['binaryninja.function.Function'] = None + ): + self._locations = location_list + self._confidence = confidence + self._func = func + + def __repr__(self): + return f"<ParameterLocations: {str(self._locations)}>" + + def __len__(self): + return len(self._vars) + + def __iter__(self) -> Generator['types.ValueLocation', None, None]: + for location in self._locations: + yield location + + def __eq__(self, other) -> bool: + return (self._locations, self._confidence, self._func) == (other._locations, other._confidence, other._func) + + def __getitem__(self, idx) -> 'types.ValueLocation': + return self._locations[idx] + + def __setitem__(self, idx: int, value: 'types.ValueLocation'): + self._locations[idx] = value + if self._func is not None: + self._func.parameter_locations = self + + def with_confidence(self, confidence: int) -> 'ParameterLocations': + return ParameterLocations(list(self._locations), confidence, self._func) + + @property + def locations(self) -> List['types.ValueLocation']: + return self._locations + + @property + def confidence(self) -> int: + return self._confidence + + @property + def function(self) -> Optional['binaryninja.function.Function']: + return self._func + + @dataclass(frozen=True, order=True) class AddressRange: start: int # Inclusive starting address |
