summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2025-12-23 13:12:02 -0700
committerRusty Wagner <rusty.wagner@gmail.com>2026-05-22 16:30:56 -0400
commit8d621c51b2797fda7b1dc22243dde611cfc04f68 (patch)
treeba5e6a90e644d21d13e75dabcbd0cc747444443a /python
parent08e34ac325743085911f96b62c81d9a1f2127806 (diff)
Refactor calling conventions to support correct representation of structures
Diffstat (limited to 'python')
-rw-r--r--python/architecture.py10
-rw-r--r--python/binaryview.py18
-rw-r--r--python/callingconvention.py968
-rw-r--r--python/examples/pseudo_python.py25
-rw-r--r--python/function.py200
-rw-r--r--python/highlevelil.py42
-rw-r--r--python/mediumlevelil.py262
-rw-r--r--python/platform.py12
-rw-r--r--python/types.py394
-rw-r--r--python/variable.py238
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