summaryrefslogtreecommitdiff
path: root/python/architecture.py
diff options
context:
space:
mode:
authorBrandon Miller <brandon@vector35.com>2026-02-16 14:16:25 -0500
committerBrandon Miller <brandon@vector35.com>2026-02-16 14:16:25 -0500
commit2a4c7d5d89907497e029337bbaf6f7e467bcde98 (patch)
treec36bd1c4f3c477a5a8f7cce5f8ea5fd8a133d02f /python/architecture.py
parentc9dbb48365bf1a0c5c06daa2234e21e64d9bc2f7 (diff)
Share context between BB recovery, lifing, and disassembly
Adds a GetInstructionTextWithContext callback to the architecture class that can be used to pass data from AnalyzeBasicBlocks. This same context is also supplied to LiftFunction and allows for supplying shared function and/or binary view level information across basic block analysis, function lifting, and disassembly text rendering
Diffstat (limited to 'python/architecture.py')
-rw-r--r--python/architecture.py584
1 files changed, 320 insertions, 264 deletions
diff --git a/python/architecture.py b/python/architecture.py
index 83a7cef7..9d27e177 100644
--- a/python/architecture.py
+++ b/python/architecture.py
@@ -69,347 +69,360 @@ IntrinsicType = Union[IntrinsicName, 'lowlevelil.ILIntrinsic', IntrinsicIndex]
@dataclass
class BasicBlockAnalysisContext:
- """Used by ``analyze_basic_blocks`` and contains analysis settings and other contextual information.
+ """Used by ``analyze_basic_blocks`` and contains analysis settings and other contextual information.
.. note:: This class is meant to be used by Architecture plugins only
"""
- _handle: core.BNBasicBlockAnalysisContext
- _function: "function.Function"
- _contextual_returns_dirty: bool
+ _handle: core.BNBasicBlockAnalysisContext
+ _function: "function.Function"
+ _contextual_returns_dirty: bool
- # In
- _indirect_branches: List["variable.IndirectBranchInfo"]
- _indirect_no_return_calls: Set["function.ArchAndAddr"]
- _analysis_skip_override: core.FunctionAnalysisSkipOverride
- _guided_analysis_mode: bool
- _trigger_guided_on_invalid_instruction: bool
- _translate_tail_calls: bool
- _disallow_branch_to_string: bool
- _max_function_size: int
+ # In
+ _indirect_branches: List["variable.IndirectBranchInfo"]
+ _indirect_no_return_calls: Set["function.ArchAndAddr"]
+ _analysis_skip_override: core.FunctionAnalysisSkipOverride
+ _guided_analysis_mode: bool
+ _trigger_guided_on_invalid_instruction: bool
+ _translate_tail_calls: bool
+ _disallow_branch_to_string: bool
+ _max_function_size: int
- # In/Out
- _max_size_reached: bool
- _contextual_returns: Dict["function.ArchAndAddr", bool]
+ # In/Out
+ _max_size_reached: bool
+ _contextual_returns: Dict["function.ArchAndAddr", bool]
- # Out
- _direct_code_references: Dict[int, "function.ArchAndAddr"]
- _direct_no_return_calls: Set["function.ArchAndAddr"]
- _halted_disassembly_addresses: Set["function.ArchAndAddr"]
+ # Out
+ _direct_code_references: Dict[int, "function.ArchAndAddr"]
+ _direct_no_return_calls: Set["function.ArchAndAddr"]
+ _halted_disassembly_addresses: Set["function.ArchAndAddr"]
- @staticmethod
- def from_core_struct(bn_bb_context: core.BNBasicBlockAnalysisContext) -> "BasicBlockAnalysisContext":
- """Create a BasicBlockAnalysisContext from a core.BNBasicBlockAnalysisContext structure."""
+ @staticmethod
+ def from_core_struct(bn_bb_context: core.BNBasicBlockAnalysisContext) -> "BasicBlockAnalysisContext":
+ """Create a BasicBlockAnalysisContext from a core.BNBasicBlockAnalysisContext structure."""
- indirect_branches = []
- for i in range(0, bn_bb_context.indirectBranchesCount):
- ibi = variable.IndirectBranchInfo(
- source_arch=CoreArchitecture._from_cache(bn_bb_context.indirectBranches[i].sourceArch),
- source_addr=bn_bb_context.indirectBranches[i].sourceAddr,
- dest_arch=CoreArchitecture._from_cache(bn_bb_context.indirectBranches[i].destArch),
- dest_addr=bn_bb_context.indirectBranches[i].destAddr,
- auto_defined=bn_bb_context.indirectBranches[i].autoDefined,
- )
- indirect_branches.append(ibi)
+ indirect_branches = []
+ for i in range(0, bn_bb_context.indirectBranchesCount):
+ ibi = variable.IndirectBranchInfo(
+ source_arch=CoreArchitecture._from_cache(bn_bb_context.indirectBranches[i].sourceArch),
+ source_addr=bn_bb_context.indirectBranches[i].sourceAddr,
+ dest_arch=CoreArchitecture._from_cache(bn_bb_context.indirectBranches[i].destArch),
+ dest_addr=bn_bb_context.indirectBranches[i].destAddr,
+ auto_defined=bn_bb_context.indirectBranches[i].autoDefined,
+ )
+ indirect_branches.append(ibi)
- indirect_no_return_calls = set()
- for i in range(0, bn_bb_context.indirectNoReturnCallsCount):
- loc = function.ArchAndAddr(
- CoreArchitecture._from_cache(bn_bb_context.indirectNoReturnCalls[i].arch),
- bn_bb_context.indirectNoReturnCalls[i].address,
- )
- indirect_no_return_calls.add(loc)
+ indirect_no_return_calls = set()
+ for i in range(0, bn_bb_context.indirectNoReturnCallsCount):
+ loc = function.ArchAndAddr(
+ CoreArchitecture._from_cache(bn_bb_context.indirectNoReturnCalls[i].arch),
+ bn_bb_context.indirectNoReturnCalls[i].address,
+ )
+ indirect_no_return_calls.add(loc)
- contextual_returns = {}
- for i in range(0, bn_bb_context.contextualFunctionReturnCount):
- loc = function.ArchAndAddr(
- CoreArchitecture._from_cache(bn_bb_context.contextualFunctionReturnLocations[i].arch),
- bn_bb_context.contextualFunctionReturnLocations[i].address,
- )
- contextual_returns[loc] = bn_bb_context._contextualFunctionReturnValues[i]
+ contextual_returns = {}
+ for i in range(0, bn_bb_context.contextualFunctionReturnCount):
+ loc = function.ArchAndAddr(
+ CoreArchitecture._from_cache(bn_bb_context.contextualFunctionReturnLocations[i].arch),
+ bn_bb_context.contextualFunctionReturnLocations[i].address,
+ )
+ contextual_returns[loc] = bn_bb_context._contextualFunctionReturnValues[i]
- direct_code_references = {}
- for i in range(0, bn_bb_context.directRefCount):
- src = function.ArchAndAddr(
- CoreArchitecture._from_cache(bn_bb_context.directRefSources[i].arch),
- bn_bb_context.directRefSources[i].address,
- )
- direct_code_references[bn_bb_context.directRefTargets[i]] = src
+ direct_code_references = {}
+ for i in range(0, bn_bb_context.directRefCount):
+ src = function.ArchAndAddr(
+ CoreArchitecture._from_cache(bn_bb_context.directRefSources[i].arch),
+ bn_bb_context.directRefSources[i].address,
+ )
+ direct_code_references[bn_bb_context.directRefTargets[i]] = src
- direct_no_return_calls = set()
- for i in range(0, bn_bb_context.directNoReturnCallsCount):
- loc = function.ArchAndAddr(
- CoreArchitecture._from_cache(bn_bb_context.directNoReturnCallLocations[i].arch),
- bn_bb_context.directNoReturnCallLocations[i].address,
- )
- direct_no_return_calls.add(loc)
+ direct_no_return_calls = set()
+ for i in range(0, bn_bb_context.directNoReturnCallsCount):
+ loc = function.ArchAndAddr(
+ CoreArchitecture._from_cache(bn_bb_context.directNoReturnCallLocations[i].arch),
+ bn_bb_context.directNoReturnCallLocations[i].address,
+ )
+ direct_no_return_calls.add(loc)
- halted_disassembly_addresses = set()
- for i in range(0, bn_bb_context.haltedDisassemblyAddressesCount):
- addr = function.ArchAndAddr(
- CoreArchitecture._from_cache(bn_bb_context.haltedDisassemblyAddresses[i].arch),
- bn_bb_context.haltedDisassemblyAddresses[i].address,
- )
- halted_disassembly_addresses.add(addr)
+ halted_disassembly_addresses = set()
+ for i in range(0, bn_bb_context.haltedDisassemblyAddressesCount):
+ addr = function.ArchAndAddr(
+ CoreArchitecture._from_cache(bn_bb_context.haltedDisassemblyAddresses[i].arch),
+ bn_bb_context.haltedDisassemblyAddresses[i].address,
+ )
+ halted_disassembly_addresses.add(addr)
- view = binaryview.BinaryView(handle=core.BNGetFunctionData(bn_bb_context.function))
- return BasicBlockAnalysisContext(
- _handle=bn_bb_context,
- _function=function.Function(view, core.BNNewFunctionReference(bn_bb_context.function)),
- _indirect_branches=indirect_branches,
- _indirect_no_return_calls=indirect_no_return_calls,
- _analysis_skip_override=bn_bb_context.analysisSkipOverride,
- _guided_analysis_mode=bn_bb_context.guidedAnalysisMode,
- _trigger_guided_on_invalid_instruction=bn_bb_context.triggerGuidedOnInvalidInstruction,
- _translate_tail_calls=bn_bb_context.translateTailCalls,
- _disallow_branch_to_string=bn_bb_context.disallowBranchToString,
- _max_function_size=bn_bb_context.maxFunctionSize,
- _max_size_reached=bn_bb_context.maxSizeReached,
- _contextual_returns=contextual_returns,
- _contextual_returns_dirty=False,
- _direct_code_references=direct_code_references,
- _direct_no_return_calls=direct_no_return_calls,
- _halted_disassembly_addresses=halted_disassembly_addresses,
- )
+ view = binaryview.BinaryView(handle=core.BNGetFunctionData(bn_bb_context.function))
+ return BasicBlockAnalysisContext(
+ _handle=bn_bb_context,
+ _function=function.Function(view, core.BNNewFunctionReference(bn_bb_context.function)),
+ _indirect_branches=indirect_branches, _indirect_no_return_calls=indirect_no_return_calls,
+ _analysis_skip_override=bn_bb_context.analysisSkipOverride,
+ _guided_analysis_mode=bn_bb_context.guidedAnalysisMode,
+ _trigger_guided_on_invalid_instruction=bn_bb_context.triggerGuidedOnInvalidInstruction,
+ _translate_tail_calls=bn_bb_context.translateTailCalls,
+ _disallow_branch_to_string=bn_bb_context.disallowBranchToString,
+ _max_function_size=bn_bb_context.maxFunctionSize, _max_size_reached=bn_bb_context.maxSizeReached,
+ _contextual_returns=contextual_returns, _contextual_returns_dirty=False,
+ _direct_code_references=direct_code_references, _direct_no_return_calls=direct_no_return_calls,
+ _halted_disassembly_addresses=halted_disassembly_addresses,
+ )
- @property
- def indirect_branches(self) -> List["variable.IndirectBranchInfo"]:
- """Get the list of indirect branches in this context."""
+ @property
+ def indirect_branches(self) -> List["variable.IndirectBranchInfo"]:
+ """Get the list of indirect branches in this context."""
- return self._indirect_branches
+ return self._indirect_branches
- @property
- def indirect_no_return_calls(self) -> Set["function.ArchAndAddr"]:
- """Get the set of indirect no-return calls in this context."""
+ @property
+ def indirect_no_return_calls(self) -> Set["function.ArchAndAddr"]:
+ """Get the set of indirect no-return calls in this context."""
- return self._indirect_no_return_calls
+ return self._indirect_no_return_calls
- @property
- def analysis_skip_override(self) -> core.FunctionAnalysisSkipOverride:
- """Get the analysis skip override setting for this context."""
+ @property
+ def analysis_skip_override(self) -> core.FunctionAnalysisSkipOverride:
+ """Get the analysis skip override setting for this context."""
- return self._analysis_skip_override
+ return self._analysis_skip_override
- @property
- def guided_analysis_mode(self) -> bool:
- """Get the setting that determines if functions start in guided analysis mode."""
+ @property
+ def guided_analysis_mode(self) -> bool:
+ """Get the setting that determines if functions start in guided analysis mode."""
- return self._guided_analysis_mode
+ return self._guided_analysis_mode
- @property
- def trigger_guided_on_invalid_instruction(self) -> bool:
- """Get the setting that determines if guided mode should be triggered on invalid instructions."""
+ @property
+ def trigger_guided_on_invalid_instruction(self) -> bool:
+ """Get the setting that determines if guided mode should be triggered on invalid instructions."""
- return self._trigger_guided_on_invalid_instruction
+ return self._trigger_guided_on_invalid_instruction
- @property
- def translate_tail_calls(self) -> bool:
- """Get setting from context that determines if tail calls should be translated."""
+ @property
+ def translate_tail_calls(self) -> bool:
+ """Get setting from context that determines if tail calls should be translated."""
- return self._translate_tail_calls
+ return self._translate_tail_calls
- @property
- def disallow_branch_to_string(self) -> bool:
- """Get setting from context that determines if branches to string addresses should be disallowed."""
+ @property
+ def disallow_branch_to_string(self) -> bool:
+ """Get setting from context that determines if branches to string addresses should be disallowed."""
- return self._disallow_branch_to_string
+ return self._disallow_branch_to_string
- @property
- def max_function_size(self) -> int:
- """Get the maximum function size setting for this context."""
+ @property
+ def max_function_size(self) -> int:
+ """Get the maximum function size setting for this context."""
- return self._max_function_size
+ return self._max_function_size
- @property
- def halt_on_invalid_instruction(self) -> bool:
- """Get the setting from context that determines if analysis should halt on invalid instructions."""
+ @property
+ def halt_on_invalid_instruction(self) -> bool:
+ """Get the setting from context that determines if analysis should halt on invalid instructions."""
- return self._halt_on_invalid_instruction
+ return self._halt_on_invalid_instruction
- @property
- def max_size_reached(self) -> bool:
- """Get boolean that indicates if the maximum function size has been reached."""
+ @property
+ def max_size_reached(self) -> bool:
+ """Get boolean that indicates if the maximum function size has been reached."""
- return self._max_size_reached
+ return self._max_size_reached
- @max_size_reached.setter
- def max_size_reached(self, value: bool) -> None:
- """Set boolean that indicates if the maximum function size has been reached.
+ @max_size_reached.setter
+ def max_size_reached(self, value: bool) -> None:
+ """Set boolean that indicates if the maximum function size has been reached.
:param bool value: The new value for max_size_reached
"""
- if not isinstance(value, bool):
- raise TypeError("value must be a boolean")
+ if not isinstance(value, bool):
+ raise TypeError("value must be a boolean")
- self._max_size_reached = value
+ self._max_size_reached = value
- @property
- def contextual_returns(self) -> Dict["function.ArchAndAddr", bool]:
- """Get the mapping of contextual function return locations to their values."""
+ @property
+ def contextual_returns(self) -> Dict["function.ArchAndAddr", bool]:
+ """Get the mapping of contextual function return locations to their values."""
- return self._contextual_returns
+ return self._contextual_returns
- def add_contextual_return(self, loc: "function.ArchAndAddr", value: bool) -> None:
- """
+ def add_contextual_return(self, loc: "function.ArchAndAddr", value: bool) -> None:
+ """
``add_contextual_return`` adds a contextual function return location and its value to the current function.
:param function.ArchAndAddr loc: The location of the contextual function return
:param bool value: The value of the contextual function return
"""
- if not isinstance(value, bool):
- raise TypeError("value must be a boolean")
+ if not isinstance(value, bool):
+ raise TypeError("value must be a boolean")
- if not isinstance(loc, function.ArchAndAddr):
- raise TypeError("loc must be an instance of function.ArchAndAddr")
+ if not isinstance(loc, function.ArchAndAddr):
+ raise TypeError("loc must be an instance of function.ArchAndAddr")
- # Update existing value if it exists
- if loc in self._contextual_returns:
- if self._contextual_returns[loc] == value:
- return
+ # Update existing value if it exists
+ if loc in self._contextual_returns:
+ if self._contextual_returns[loc] == value:
+ return
- self._contextual_returns[loc] = value
- self._contextual_returns_dirty = True
+ self._contextual_returns[loc] = value
+ self._contextual_returns_dirty = True
- @property
- def direct_code_references(self) -> Dict[int, "function.ArchAndAddr"]:
- """Get the mapping of direct code reference targets to their source locations."""
+ @property
+ def direct_code_references(self) -> Dict[int, "function.ArchAndAddr"]:
+ """Get the mapping of direct code reference targets to their source locations."""
- return self._direct_code_references
+ return self._direct_code_references
- def add_direct_code_reference(self, target: int, source: "function.ArchAndAddr") -> None:
- """
+ def add_direct_code_reference(self, target: int, source: "function.ArchAndAddr") -> None:
+ """
``add_direct_code_reference`` adds a direct code reference to the current function.
:param int target: The target address of the direct code reference
:param function.ArchAndAddr source: The source location of the direct code reference
"""
- if not isinstance(target, int):
- raise TypeError("target must be an integer")
+ if not isinstance(target, int):
+ raise TypeError("target must be an integer")
- if not isinstance(source, function.ArchAndAddr):
- raise TypeError("source must be an instance of function.ArchAndAddr")
+ if not isinstance(source, function.ArchAndAddr):
+ raise TypeError("source must be an instance of function.ArchAndAddr")
- self._direct_code_references[target] = source
+ self._direct_code_references[target] = source
- @property
- def direct_no_return_calls(self) -> Set["function.ArchAndAddr"]:
- """Get the set of direct no-return call locations in this context."""
+ @property
+ def direct_no_return_calls(self) -> Set["function.ArchAndAddr"]:
+ """Get the set of direct no-return call locations in this context."""
- return self._direct_no_return_calls
+ return self._direct_no_return_calls
- def add_direct_no_return_call(self, loc: "function.ArchAndAddr") -> None:
- """
+ def add_direct_no_return_call(self, loc: "function.ArchAndAddr") -> None:
+ """
``add_direct_no_return_call`` adds a direct no-return call location to the current function.
:param function.ArchAndAddr loc: The location of the direct no-return call
"""
- if not isinstance(loc, function.ArchAndAddr):
- raise TypeError("loc must be an instance of function.ArchAndAddr")
+ if not isinstance(loc, function.ArchAndAddr):
+ raise TypeError("loc must be an instance of function.ArchAndAddr")
- self._direct_no_return_calls.add(loc)
+ self._direct_no_return_calls.add(loc)
- @property
- def halted_disassembly_addresses(self) -> Set["function.ArchAndAddr"]:
- """Get the set of addresses where disassembly has been halted."""
+ @property
+ def halted_disassembly_addresses(self) -> Set["function.ArchAndAddr"]:
+ """Get the set of addresses where disassembly has been halted."""
- return self._halted_disassembly_addresses
+ return self._halted_disassembly_addresses
- def add_halted_disassembly_address(self, loc: "function.ArchAndAddr") -> None:
- """
+ def add_halted_disassembly_address(self, loc: "function.ArchAndAddr") -> None:
+ """
``add_halted_disassembly_address`` adds an address to the set of halted disassembly addresses.
:param function.ArchAndAddr loc: The location of the halted disassembly address
"""
- if not isinstance(loc, function.ArchAndAddr):
- raise TypeError("loc must be an instance of function.ArchAndAddr")
+ if not isinstance(loc, function.ArchAndAddr):
+ raise TypeError("loc must be an instance of function.ArchAndAddr")
- self._halted_disassembly_addresses.add(loc)
+ self._halted_disassembly_addresses.add(loc)
- def create_basic_block(self, arch: "Architecture", start: int) -> Optional["basicblock.BasicBlock"]:
- """
+ @property
+ def function_arch_context(self) -> Any:
+ """Get the function architecture context"""
+
+ tok = int(self._handle.functionArchContext or 0)
+ if tok == 0:
+ return None
+ return self._function.arch.function_arch_contexts.get(tok, None)
+
+ @function_arch_context.setter
+ def function_arch_context(self, value: Any) -> None:
+ """Set the function architecture context"""
+
+ if self._handle.functionArchContext:
+ raise ValueError("Function architecture context has already been set")
+ token = self._function.start
+ self._function.arch.function_arch_contexts[token] = value
+ self._handle.functionArchContext = ctypes.c_void_p(token)
+
+ def create_basic_block(self, arch: "Architecture", start: int) -> Optional["basicblock.BasicBlock"]:
+ """
``create_basic_block`` creates a new BasicBlock at the specified address for the given Architecture.
:param Architecture arch: Architecture of the BasicBlock to create
:param int start: Address of the BasicBlock to create
"""
- if not isinstance(arch, Architecture):
- raise TypeError("arch must be an instance of architecture.Architecture")
+ if not isinstance(arch, Architecture):
+ raise TypeError("arch must be an instance of architecture.Architecture")
- bnblock = core.BNAnalyzeBasicBlocksContextCreateBasicBlock(self._handle, arch.handle, start)
- if not bnblock:
- return None
+ bnblock = core.BNAnalyzeBasicBlocksContextCreateBasicBlock(self._handle, arch.handle, start)
+ if not bnblock:
+ return None
- view = binaryview.BinaryView(handle=core.BNGetFunctionData(self._function.handle))
- return basicblock.BasicBlock(bnblock, view)
+ view = binaryview.BinaryView(handle=core.BNGetFunctionData(self._function.handle))
+ return basicblock.BasicBlock(bnblock, view)
- def add_basic_block(self, block: "basicblock.BasicBlock") -> None:
- """
+ def add_basic_block(self, block: "basicblock.BasicBlock") -> None:
+ """
``add_basic_block`` adds a BasicBlock to the current function.
:param basicblock.BasicBlock block: The BasicBlock to add
"""
- if not isinstance(block, basicblock.BasicBlock):
- raise TypeError("block must be an instance of basicblock.BasicBlock")
+ if not isinstance(block, basicblock.BasicBlock):
+ raise TypeError("block must be an instance of basicblock.BasicBlock")
- core.BNAnalyzeBasicBlocksContextAddBasicBlockToFunction(self._handle, block.handle)
+ core.BNAnalyzeBasicBlocksContextAddBasicBlockToFunction(self._handle, block.handle)
- def add_temp_outgoing_reference(self, target: "function.Function") -> None:
- """
+ def add_temp_outgoing_reference(self, target: "function.Function") -> None:
+ """
``add_temp_outgoing_reference`` adds a temporary outgoing reference to the specified function.
:param function.Function target: The target function to add a temporary outgoing reference to
"""
- if not isinstance(target, function.Function):
- raise TypeError("target must be an instance of function.Function")
+ if not isinstance(target, function.Function):
+ raise TypeError("target must be an instance of function.Function")
- core.BNAnalyzeBasicBlocksContextAddTempReference(self._handle, target.handle)
+ core.BNAnalyzeBasicBlocksContextAddTempReference(self._handle, target.handle)
- def finalize(self) -> None:
- """
+ def finalize(self) -> None:
+ """
``finalize`` finalizes the function's basic block analysis
"""
- if self._direct_code_references:
- total = len(self._direct_code_references)
- sources = (core.BNArchitectureAndAddress * total)()
- targets = (ctypes.c_ulonglong * total)()
- for i, (target, src) in enumerate(self._direct_code_references.items()):
- sources[i].arch = src.arch.handle
- sources[i].address = src.addr
- targets[i] = target
-
- core.BNAnalyzeBasicBlocksContextSetDirectCodeReferences(self._handle, sources, targets, total)
+ if self._direct_code_references:
+ total = len(self._direct_code_references)
+ sources = (core.BNArchitectureAndAddress * total)()
+ targets = (ctypes.c_ulonglong * total)()
+ for i, (target, src) in enumerate(self._direct_code_references.items()):
+ sources[i].arch = src.arch.handle
+ sources[i].address = src.addr
+ targets[i] = target
- if self._direct_no_return_calls:
- total = len(self._direct_no_return_calls)
- direct_no_return_calls = (core.BNArchitectureAndAddress * total)()
- for i, loc in enumerate(self._direct_no_return_calls):
- direct_no_return_calls[i].arch = loc.arch.handle
- direct_no_return_calls[i].address = loc.addr
- core.BNAnalyzeBasicBlocksContextSetDirectNoReturnCalls(self._handle, direct_no_return_calls, total)
+ core.BNAnalyzeBasicBlocksContextSetDirectCodeReferences(self._handle, sources, targets, total)
- if self._halted_disassembly_addresses:
- total = len(self._halted_disassembly_addresses)
- halted_addresses = (core.BNArchitectureAndAddress * total)()
- for i, loc in enumerate(self._halted_disassembly_addresses):
- halted_addresses[i].arch = loc.arch.handle
- halted_addresses[i].address = loc.addr
- core.BNAnalyzeBasicBlocksContextSetHaltedDisassemblyAddresses(self._handle, halted_addresses, total)
+ if self._direct_no_return_calls:
+ total = len(self._direct_no_return_calls)
+ direct_no_return_calls = (core.BNArchitectureAndAddress * total)()
+ for i, loc in enumerate(self._direct_no_return_calls):
+ direct_no_return_calls[i].arch = loc.arch.handle
+ direct_no_return_calls[i].address = loc.addr
+ core.BNAnalyzeBasicBlocksContextSetDirectNoReturnCalls(self._handle, direct_no_return_calls, total)
- self._handle.maxSizeReached = ctypes.c_bool(self._max_size_reached)
- if self._contextual_returns_dirty:
- total = len(self._contextual_returns)
- values = (ctypes.c_bool * total)()
- returns = (core.BNArchitectureAndAddress * total)()
- for i, (loc, value) in enumerate(self._contextual_returns.items()):
- returns[i].arch = loc.arch.handle
- returns[i].address = loc.addr
- values[i] = value
- core.BNAnalyzeBasicBlocksContextSetContextualFunctionReturns(self._handle, returns, values, total)
+ if self._halted_disassembly_addresses:
+ total = len(self._halted_disassembly_addresses)
+ halted_addresses = (core.BNArchitectureAndAddress * total)()
+ for i, loc in enumerate(self._halted_disassembly_addresses):
+ halted_addresses[i].arch = loc.arch.handle
+ halted_addresses[i].address = loc.addr
+ core.BNAnalyzeBasicBlocksContextSetHaltedDisassemblyAddresses(self._handle, halted_addresses, total)
- core.BNAnalyzeBasicBlocksContextFinalize(self._handle)
+ self._handle.maxSizeReached = ctypes.c_bool(self._max_size_reached)
+ if self._contextual_returns_dirty:
+ total = len(self._contextual_returns)
+ values = (ctypes.c_bool * total)()
+ returns = (core.BNArchitectureAndAddress * total)()
+ for i, (loc, value) in enumerate(self._contextual_returns.items()):
+ returns[i].arch = loc.arch.handle
+ returns[i].address = loc.addr
+ values[i] = value
+ core.BNAnalyzeBasicBlocksContextSetContextualFunctionReturns(self._handle, returns, values, total)
@dataclass
@@ -429,10 +442,12 @@ class FunctionLifterContext:
_user_indirect_branches: Dict["function.ArchAndAddr", Set["function.ArchAndAddr"]]
_auto_indirect_branches: Dict["function.ArchAndAddr", Set["function.ArchAndAddr"]]
_inlined_calls: Set[int]
+ _function_arch_context_token: int
@staticmethod
- def from_core_struct(func: core.BNLowLevelILFunction,
- bn_fl_context: core.BNFunctionLifterContext) -> "FunctionLifterContext":
+ def from_core_struct(
+ func: core.BNLowLevelILFunction, bn_fl_context: core.BNFunctionLifterContext
+ ) -> "FunctionLifterContext":
"""Create a FunctionLifterContext from a core.BNFunctionLifterContext structure."""
session_id = core.BNLoggerGetSessionId(bn_fl_context.logger)
@@ -442,17 +457,13 @@ class FunctionLifterContext:
plat = platform.CorePlatform._from_cache(core.BNNewPlatformReference(bn_fl_context.platform))
blocks = []
for i in range(0, bn_fl_context.basicBlockCount):
- blocks.append(
- basicblock.BasicBlock(
- core.BNNewBasicBlockReference(bn_fl_context.basicBlocks[i])
- )
- )
+ blocks.append(basicblock.BasicBlock(core.BNNewBasicBlockReference(bn_fl_context.basicBlocks[i])))
contextual_returns = {}
for i in range(0, bn_fl_context.contextualFunctionReturnCount):
loc = function.ArchAndAddr(
- CoreArchitecture._from_cache(bn_fl_context.contextualFunctionReturnLocations[i].arch),
- bn_fl_context.contextualFunctionReturnLocations[i].address,
+ CoreArchitecture._from_cache(bn_fl_context.contextualFunctionReturnLocations[i].arch),
+ bn_fl_context.contextualFunctionReturnLocations[i].address,
)
contextual_returns[loc] = bn_fl_context._contextualFunctionReturnValues[i]
@@ -460,12 +471,12 @@ class FunctionLifterContext:
inline_remapping = {}
for i in range(0, bn_fl_context.inlinedRemappingEntryCount):
key = function.ArchAndAddr(
- CoreArchitecture._from_cache(bn_fl_context.inlinedRemappingKeys[i].arch),
- bn_fl_context.inlinedRemappingKeys[i].address,
+ CoreArchitecture._from_cache(bn_fl_context.inlinedRemappingKeys[i].arch),
+ bn_fl_context.inlinedRemappingKeys[i].address,
)
dest = function.ArchAndAddr(
- CoreArchitecture._from_cache(bn_fl_context.inlinedRemappingEntries[i].destination.arch),
- bn_fl_context.inlinedRemappingEntries[i].destination.address,
+ CoreArchitecture._from_cache(bn_fl_context.inlinedRemappingEntries[i].destination.arch),
+ bn_fl_context.inlinedRemappingEntries[i].destination.address,
)
inline_remapping[src] = dest
@@ -473,8 +484,8 @@ class FunctionLifterContext:
auto_indirect_branches = {}
for i in range(0, bn_fl_context.indirectBranchesCount):
src = function.ArchAndAddr(
- CoreArchitecture._from_cache(bn_fl_context.indirectBranches[i].sourceArch),
- bn_fl_context.indirectBranches[i].sourceAddr,
+ CoreArchitecture._from_cache(bn_fl_context.indirectBranches[i].sourceArch),
+ bn_fl_context.indirectBranches[i].sourceAddr,
)
dest = function.ArchAndAddr(
@@ -491,37 +502,35 @@ class FunctionLifterContext:
user_indirect_branches[src] = set()
user_indirect_branches[src].add(dest)
-
inlined_calls = set()
for i in range(0, bn_fl_context.inlinedCallsCount):
inlined_calls.add(bn_fl_context.inlinedCalls[i])
return FunctionLifterContext(
- _handle=bn_fl_context,
- _function=lowlevelil.LowLevelILFunction(
- plat.arch, core.BNNewLowLevelILFunctionReference(func)
- ),
- _platform=plat,
- _logger=logger,
- _blocks=blocks,
- _contextual_returns=contextual_returns,
- _inline_remapping=inline_remapping,
- _user_indirect_branches=user_indirect_branches,
- _auto_indirect_branches=auto_indirect_branches,
- _inlined_calls=inlined_calls,
+ _handle=bn_fl_context,
+ _function=lowlevelil.LowLevelILFunction(plat.arch,
+ core.BNNewLowLevelILFunctionReference(func)), _platform=plat,
+ _logger=logger, _blocks=blocks, _contextual_returns=contextual_returns, _inline_remapping=inline_remapping,
+ _user_indirect_branches=user_indirect_branches, _auto_indirect_branches=auto_indirect_branches,
+ _inlined_calls=inlined_calls, _function_arch_context_token=bn_fl_context.functionArchContext,
)
def prepare_block_translation(self, function, arch, address):
- """Prepare block for translation"""
+ """Prepare the basic block for translation"""
core.BNPrepareBlockTranslation(function.handle, arch.handle, address)
@property
def blocks(self) -> List["basicblock.BasicBlock"]:
- """Get the list of basic blocks in this context."""
+ """Get the list of basic blocks in this context"""
return self._blocks
+ @property
+ def function_arch_context(self) -> Any:
+ """Get the function architecture context"""
+
+ return self._function.arch.function_arch_contexts.get(self._function_arch_context_token, None)
@dataclass(frozen=True)
class RegisterInfo:
@@ -696,6 +705,7 @@ class Architecture(metaclass=_ArchitectureMetaClass):
reg_stacks: Dict[RegisterStackName, RegisterStackInfo] = {}
intrinsics = {}
next_address = 0
+ function_arch_contexts: Dict[int, Any] = {}
def __init__(self):
binaryninja._init_plugins()
@@ -717,12 +727,16 @@ class Architecture(metaclass=_ArchitectureMetaClass):
)
self._cb.getInstructionInfo = self._cb.getInstructionInfo.__class__(self._get_instruction_info)
self._cb.getInstructionText = self._cb.getInstructionText.__class__(self._get_instruction_text)
+ self._cb.getInstructionTextWithContext = self._cb.getInstructionTextWithContext.__class__(
+ self._get_instruction_text_with_context
+ )
self._cb.freeInstructionText = self._cb.freeInstructionText.__class__(self._free_instruction_text)
self._cb.getInstructionLowLevelIL = self._cb.getInstructionLowLevelIL.__class__(
self._get_instruction_low_level_il
)
self._cb.analyzeBasicBlocks = self._cb.analyzeBasicBlocks.__class__(self._analyze_basic_blocks)
self._cb.liftFunction = self._cb.liftFunction.__class__(self._lift_function)
+ self._cb.freeFunctionArchContext = self._cb.freeFunctionArchContext.__class__(self._free_function_arch_context)
self._cb.getRegisterName = self._cb.getRegisterName.__class__(self._get_register_name)
self._cb.getFlagName = self._cb.getFlagName.__class__(self._get_flag_name)
self._cb.getFlagWriteTypeName = self._cb.getFlagWriteTypeName.__class__(self._get_flag_write_type_name)
@@ -1164,6 +1178,26 @@ class Architecture(metaclass=_ArchitectureMetaClass):
log_error_for_exception("Unhandled Python exception in Architecture._get_instruction_text")
return False
+ def _get_instruction_text_with_context(self, ctxt, data, addr, length, context_token, result, count):
+ try:
+ buf = ctypes.create_string_buffer(length[0])
+ ctypes.memmove(buf, data, length[0])
+ context = self.function_arch_contexts.get(context_token, None)
+ info = self.get_instruction_text_with_context(buf.raw, addr, context)
+ if info is None:
+ return False
+ tokens = info[0]
+ length[0] = info[1]
+ count[0] = len(tokens)
+ token_buf = function.InstructionTextToken._get_core_struct(tokens)
+ result[0] = token_buf
+ ptr = ctypes.cast(token_buf, ctypes.c_void_p)
+ self._pending_token_lists[ptr.value] = (ptr.value, token_buf)
+ return True
+ except:
+ log_error_for_exception("Unhandled Python exception in Architecture._get_instruction_text_with_context")
+ return False
+
def _free_instruction_text(self, tokens, count):
try:
buf = ctypes.cast(tokens, ctypes.c_void_p)
@@ -1205,6 +1239,12 @@ class Architecture(metaclass=_ArchitectureMetaClass):
log_error_for_exception("Unhandled Python exception in Architecture._lift_function")
return False
+ def _free_function_arch_context(self, ctx, context_token):
+ try:
+ self.function_arch_contexts.pop(context_token, None)
+ except:
+ log_error_for_exception("Unhandled Python exception in Architecture._free_function_arch_context")
+
def _get_register_name(self, ctxt, reg):
try:
if reg in self._regs_by_index:
@@ -1894,6 +1934,20 @@ class Architecture(metaclass=_ArchitectureMetaClass):
"""
raise NotImplementedError
+ def get_instruction_text_with_context(self, data: bytes, addr: int, context: Any) -> Optional[Tuple[List['function.InstructionTextToken'], int]]:
+ """
+ ``get_instruction_text`` returns a tuple containing a list of decoded InstructionTextToken objects and the bytes used at the given virtual
+ address ``addr`` with data ``data``.
+
+ .. note:: Architecture subclasses should implement this method if they require context from analyze_basic_blocks for instruction decoding.
+
+ :param str data: a maximum of max_instruction_length bytes from the binary at virtual address ``addr``
+ :param int addr: virtual address of bytes in ``data``
+ :param Any context: function architecture context
+ :return: a tuple containing the InstructionTextToken list and length of bytes decoded
+ """
+ return self.get_instruction_text(data, addr)
+
def get_instruction_low_level_il_instruction(
self, bv: 'binaryview.BinaryView', addr: int
) -> 'lowlevelil.LowLevelILInstruction':
@@ -3283,6 +3337,8 @@ class ArchitectureHook(CoreArchitecture):
self._cb.getInstructionInfo = self._cb.getInstructionInfo.__class__()
if self.get_instruction_text.__code__ == CoreArchitecture.get_instruction_text.__code__:
self._cb.getInstructionText = self._cb.getInstructionText.__class__()
+ if self.get_instruction_text_with_context.__code__ == CoreArchitecture.get_instruction_text_with_context.__code__:
+ self._cb.getInstructionTextWithContext = self._cb.getInstructionTextWithContext.__class__()
if self.__class__.stack_pointer is None:
self._cb.getStackPointerRegister = self._cb.getStackPointerRegister.__class__()
if self.__class__.link_reg is None: