summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/architecture.py59
-rw-r--r--python/basicblock.py129
-rw-r--r--python/binaryview.py37
-rw-r--r--python/function.py180
4 files changed, 404 insertions, 1 deletions
diff --git a/python/architecture.py b/python/architecture.py
index 29a393a8..8ce4b4f6 100644
--- a/python/architecture.py
+++ b/python/architecture.py
@@ -40,6 +40,7 @@ from . import typelibrary
from . import function
from . import binaryview
from . import deprecation
+from .variable import IndirectBranchInfo
RegisterIndex = NewType('RegisterIndex', int)
RegisterStackIndex = NewType('RegisterStackIndex', int)
@@ -66,6 +67,16 @@ IntrinsicType = Union[IntrinsicName, 'lowlevelil.ILIntrinsic', IntrinsicIndex]
@dataclass(frozen=True)
+class BasicBlockAnalysisContext:
+ """Used by ``analyze_basic_blocks`` and contains analysis settings and other contextual information."""
+ indirect_branches: List[IndirectBranchInfo]
+ analysis_skip_override: core.FunctionAnalysisSkipOverrideEnum
+ translate_tail_calls: bool
+ disallow_branch_to_string: bool
+ max_function_size: int
+
+
+@dataclass(frozen=True)
class RegisterInfo:
full_width_reg: RegisterName
size: int
@@ -246,6 +257,7 @@ class Architecture(metaclass=_ArchitectureMetaClass):
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.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)
@@ -711,6 +723,25 @@ class Architecture(metaclass=_ArchitectureMetaClass):
log_error(traceback.format_exc())
return False
+ def _analyze_basic_blocks(self, ctx, func, ptr_bn_bb_context):
+ try:
+ bn_bb_context = ptr_bn_bb_context.contents
+ indirect_branches = []
+ for i in range(0, bn_bb_context.indirectBranchesCount):
+ ibi = IndirectBranchInfo()
+ ibi.source_arch = CoreArchitecture._from_cache(bn_bb_context.indirectBranches[i].sourceArch)
+ ibi.source_addr = bn_bb_context.indirectBranches[i].sourceAddr
+ ibi.dest_arch = CoreArchitecture._from_cache(bn_bb_context.indirectBranches[i].destArch)
+ ibi.dest_addr = bn_bb_context.indirectBranches[i].destAddr
+ ibi.auto_defined = bn_bb_context.indirectBranches[i].autoDefined
+ indirect_branches.append(ibi)
+
+ context = BasicBlockAnalysisContext(indirect_branches, bn_bb_context.analysisSkipOverride,
+ bn_bb_context.translateTailCalls, bn_bb_context.disallowBranchToString, bn_bb_context.maxFunctionSize)
+ self.analyze_basic_blocks(function.Function(handle=core.BNNewFunctionReference(func)), context)
+ except:
+ log_error(traceback.format_exc())
+
def _get_register_name(self, ctxt, reg):
try:
if reg in self._regs_by_index:
@@ -1426,6 +1457,34 @@ class Architecture(metaclass=_ArchitectureMetaClass):
"""
raise NotImplementedError
+ def analyze_basic_blocks(self, func: 'function.Function', context: BasicBlockAnalysisContext) -> None:
+ """
+ ``analyze_basic_blocks`` performs basic block recovery and commits the results to the function analysis
+
+ .. note:: Architecture subclasses should only implement this method if function-level analysis is required
+
+ :param Function func: the function to analyze
+ :param BNBasicBlockAnalysisContext context: the analysis context
+ """
+
+ try:
+ bn_bb_context = core.BNBasicBlockAnalysisContext()
+ bn_bb_context.indirectBranchesCount = len(context.indirect_branches)
+ bn_bb_context.analysisSkipOverride = context.analysis_skip_override
+ bn_bb_context.translateTailCalls = context.translate_tail_calls
+ bn_bb_context.disallowBranchToString = context.disallow_branch_to_string
+ bn_bb_context.maxFunctionSize = context.max_function_size
+ bn_bb_context.indirectBranches = (core.BNIndirectBranchInfo * len(context.indirect_branches))()
+ for i in range(0, len(context.indirect_branches)):
+ bn_bb_context.indirectBranches[i].sourceArch = context.indirect_branches[i].source_arch.handle
+ bn_bb_context.indirectBranches[i].sourceAddr = context.indirect_branches[i].source_addr
+ bn_bb_context.indirectBranches[i].destArch = context.indirect_branches[i].dest_arch.handle
+ bn_bb_context.indirectBranches[i].destAddr = context.indirect_branches[i].dest_addr
+ bn_bb_context.indirectBranches[i].autoDefined = context.indirect_branches[i].auto_defined
+ core.BNArchitectureDefaultAnalyzeBasicBlocks(func.handle, ctypes.byref(bn_bb_context))
+ except:
+ log_error(traceback.format_exc())
+
def get_low_level_il_from_bytes(self, data: bytes, addr: int) -> 'lowlevelil.LowLevelILInstruction':
"""
``get_low_level_il_from_bytes`` converts the instruction in bytes to ``il`` at the given virtual address
diff --git a/python/basicblock.py b/python/basicblock.py
index cd9227d5..1148d887 100644
--- a/python/basicblock.py
+++ b/python/basicblock.py
@@ -61,6 +61,22 @@ class BasicBlockEdge:
return f"<{self.type.name}: {self.target.start:#x}>"
+@dataclass(frozen=True)
+class PendingBasicBlockEdge:
+ """
+ ``class PendingBasicBlockEdge`` represents a pending edge that has not yet been resolved.
+
+ :cvar type: The edge branch type.
+ :cvar arch: The architecture of the target basic block.
+ :cvar target: The address of the target basic block.
+ :cvar fall_through: Whether this edge is a fallthrough edge.
+ """
+ type: BranchType
+ arch: 'architecture.Architecture'
+ target: int
+ fallthrough: bool
+
+
class BasicBlock:
"""
The ``class BasicBlock`` object is returned during analysis and should not be directly instantiated.
@@ -361,6 +377,14 @@ class BasicBlock:
"""Basic block end (read-only)"""
return core.BNGetBasicBlockEnd(self.handle)
+ @end.setter
+ def end(self, value: int) -> None:
+ """Sets the end of the basic block
+
+ .. note:: This setter is intended for use by architecture plugins only.
+ """
+ core.BNSetBasicBlockEnd(self.handle, value)
+
@property
def length(self) -> int:
"""Basic block length (read-only)"""
@@ -408,6 +432,11 @@ class BasicBlock:
"""Whether basic block has undetermined outgoing edges (read-only)"""
return core.BNBasicBlockHasUndeterminedOutgoingEdges(self.handle)
+ @has_undetermined_outgoing_edges.setter
+ def has_undetermined_outgoing_edges(self, value: bool) -> None:
+ """Sets whether basic block has undetermined outgoing edges"""
+ core.BNBasicBlockSetUndeterminedOutgoingEdges(self.handle, value)
+
@property
def can_exit(self) -> bool:
"""Whether basic block can return or is tagged as 'No Return' (read-only)"""
@@ -423,6 +452,106 @@ class BasicBlock:
"""Whether basic block has any invalid instructions (read-only)"""
return core.BNBasicBlockHasInvalidInstructions(self.handle)
+ @has_invalid_instructions.setter
+ def has_invalid_instructions(self, value: bool) -> None:
+ """Sets whether basic block has any invalid instructions"""
+ core.BNBasicBlockSetHasInvalidInstructions(self.handle, value)
+
+ def add_pending_outgoing_edge(self, typ: BranchType, addr: int, arch: 'architecture.Architecture', fallthrough: bool = False) -> None:
+ """
+ Adds a pending outgoing edge to the basic block. This is used to add edges that are not yet resolved.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param BranchType typ: The type of the branch.
+ :param int addr: The address of the target basic block.
+ :param Architecture arch: The architecture of the target basic block.
+ :param bool fallthrough: Whether this edge is a fallthrough edge.
+ """
+
+ core.BNBasicBlockAddPendingOutgoingEdge(self.handle, typ.value, addr, arch.handle, fallthrough)
+
+ def get_pending_outgoing_edges(self) -> list[PendingBasicBlockEdge]:
+ """
+ Returns a list of pending outgoing edges for the basic block. These are edges that have not yet been resolved.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :return: List of PendingBasicBlockEdge objects.
+ :rtype: list[PendingBasicBlockEdge]
+ """
+ count = ctypes.c_ulonglong(0)
+ pending_edges = core.BNGetBasicBlockPendingOutgoingEdges(self.handle, ctypes.byref(count))
+ if pending_edges is None:
+ return []
+
+ result: List[PendingBasicBlockEdge] = []
+ try:
+ for i in range(count.value):
+ result.append(PendingBasicBlockEdge(
+ type=BranchType(pending_edges[i].type),
+ arch=architecture.CoreArchitecture._from_cache(pending_edges[i].arch),
+ target=pending_edges[i].target,
+ fallthrough=pending_edges[i].fallThrough
+ ))
+ return result
+ finally:
+ core.BNFreePendingBasicBlockEdgeList(pending_edges)
+
+ def clear_pending_outgoing_edges(self) -> None:
+ """
+ Clears all pending outgoing edges for the basic block. This is used to remove edges that have not yet been resolved.
+
+ .. note:: This method is intended for use by architecture plugins only.
+ """
+ core.BNClearBasicBlockPendingOutgoingEdges(self.handle)
+
+ def get_instruction_data(self, addr: int) -> bytes:
+ """
+ Returns the raw instruction data for the basic block at the specified address.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :return: Raw instruction data as bytes.
+ :rtype: bytes
+ """
+
+ size = ctypes.c_ulonglong(0)
+ data = core.BNBasicBlockGetInstructionData(self.handle, addr, ctypes.byref(size))
+ if data is None:
+ return b''
+
+ return ctypes.string_at(data, size.value)
+
+ def add_instruction_data(self, data: bytes) -> None:
+ """
+ Adds raw instruction data to the basic block.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param bytes data: Raw instruction data to add to the basic block.
+ """
+ if not isinstance(data, bytes):
+ raise TypeError("data must be of type bytes")
+
+ core.BNBasicBlockAddInstructionData(self.handle, data, len(data))
+
+ @property
+ def fallthrough_to_function(self) -> bool:
+ """Whether the basic block has a fallthrough edge to a function."""
+
+ return core.BNBasicBlockIsFallThroughToFunction(self.handle)
+
+ @fallthrough_to_function.setter
+ def fallthrough_to_function(self, value: bool) -> None:
+ """Sets whether the basic block has a fallthrough edge to a function.
+
+ .. note:: This setter is intended for use by architecture plugins only.
+ """
+ if not isinstance(value, bool):
+ raise TypeError("value must be of type bool")
+ core.BNBasicBlockSetFallThroughToFunction(self.handle, value)
+
def _make_blocks(self, blocks, count: int) -> List['BasicBlock']:
assert blocks is not None, "core returned empty block list"
try:
diff --git a/python/binaryview.py b/python/binaryview.py
index a03cb7bc..4761758a 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -4972,6 +4972,43 @@ class BinaryView:
"""
core.BNAbortAnalysis(self.handle)
+ @property
+ def analysis_is_aborted(self) -> bool:
+ """
+ ``analysis_is_aborted`` checks if the analysis has been aborted.
+
+ .. note:: This property is intended for use by architecture plugins only.
+
+ :return: True if the analysis has been aborted, False otherwise
+ :rtype: bool
+ """
+
+ return core.BNAnalysisIsAborted(self.handle)
+
+ def should_skip_target_analysis(self, source_location: '_function.ArchAndAddr', source_function: '_function.Function',
+ end: int, target_location: '_function.ArchAndAddr') -> bool:
+ """
+ ``should_skip_target_analysis`` checks if target analysis should be skipped.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param _function.ArchAndAddr source_location: The source location.
+ :param _function.Function source_function: The source function.
+ :param int end: The end address of the source branch instruction.
+ :param _function.ArchAndAddr target_location: The target location.
+ :return: True if the target analysis should be skipped, False otherwise
+ :rtype: bool
+ """
+
+ bn_src_arch_and_addr = core.BNArchitectureAndAddress()
+ bn_src_arch_and_addr.arch = source_location.arch.handle
+ bn_src_arch_and_addr.address = source_location.addr
+ bn_target_arch_and_addr = core.BNArchitectureAndAddress()
+ bn_target_arch_and_addr.arch = target_location.arch.handle
+ bn_target_arch_and_addr.address = target_location.addr
+ return core.BNShouldSkipTargetAnalysis(self.handle, bn_src_arch_and_addr, source_function.handle, end,
+ bn_target_arch_and_addr)
+
def define_data_var(
self, addr: int, var_type: StringOrType, name: Optional[Union[str, '_types.CoreSymbol']] = None
) -> None:
diff --git a/python/function.py b/python/function.py
index 126892ec..2750e40a 100644
--- a/python/function.py
+++ b/python/function.py
@@ -85,7 +85,6 @@ FunctionViewTypeOrName = Union['FunctionViewType', FunctionGraphType, str]
def _function_name_():
return inspect.stack()[1][0].f_code.co_name
-
@dataclass(frozen=True)
class ArchAndAddr:
arch: 'architecture.Architecture'
@@ -2614,6 +2613,185 @@ class Function:
return None
return basicblock.BasicBlock(block, self._view)
+ def create_basic_block(self, arch: 'architecture.Architecture', addr: int) -> 'basicblock.BasicBlock':
+ """
+ ``create_basic_block`` creates a new BasicBlock at the specified address for the given Architecture.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param Architecture arch: Architecture of the BasicBlock to create
+ :param int addr: Address of the BasicBlock to create
+ :rtype: basicblock.BasicBlock
+ """
+ if not isinstance(arch, architecture.Architecture):
+ raise TypeError("arch must be an instance of architecture.Architecture")
+
+ bnblock = core.BNCreateFunctionBasicBlock(self.handle, arch.handle, addr)
+ if not bnblock:
+ return None
+
+ return basicblock.BasicBlock(bnblock, self._view)
+
+ def add_basic_block(self, block: 'basicblock.BasicBlock') -> None:
+ """
+ ``add_basic_block`` adds the specified BasicBlock to the Function.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param basicblock.BasicBlock block: BasicBlock to add to the Function
+ :rtype: None
+ """
+ if not isinstance(block, basicblock.BasicBlock):
+ raise TypeError("block must be an instance of basicblock.BasicBlock")
+
+ core.BNAddFunctionBasicBlock(self.handle, block.handle)
+
+ def finalize_basic_blocks(self) -> None:
+ """
+ ``finalize_basic_blocks`` finalizes the BasicBlocks in the Function, ensuring they are committed to analysis.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :rtype: None
+ """
+ core.BNFinalizeFunctionBasicBlocks(self.handle)
+
+ def add_direct_code_reference(self, source: ArchAndAddr, target: int) -> None:
+ """
+ ``add_direct_code_reference`` adds a direct code reference from the source to the target address.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param ArchAndAddr source: Source address and architecture of the reference
+ :param int target: Target address of the reference
+ :rtype: None
+ """
+ bn_arch_and_addr = core.BNArchitectureAndAddress()
+ bn_arch_and_addr.arch = source.arch.handle
+ bn_arch_and_addr.address = source.addr
+ core.BNFunctionAddDirectCodeReference(
+ self.handle, bn_arch_and_addr, target
+ )
+
+ def add_direct_no_return_call(self, location: ArchAndAddr) -> None:
+ """
+ ``add_direct_no_return_call`` adds a direct no return call site to the function at the specified location.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param ArchAndAddr location: Location of the call site
+ :rtype: None
+ """
+ bn_arch_and_addr = core.BNArchitectureAndAddress()
+ bn_arch_and_addr.arch = location.arch.handle
+ bn_arch_and_addr.address = location.addr
+ core.BNFunctionAddDirectNoReturnCall(self.handle, bn_arch_and_addr)
+
+ def location_has_no_return_calls(self, location: ArchAndAddr) -> bool:
+ """
+ ``location_has_no_return_calls`` checks if the specified location has any no return calls sites.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param ArchAndAddr location: Location to check for no return calls
+ :return: True if there are no return calls at the specified location, False otherwise
+ :rtype: bool
+ """
+ bn_arch_and_addr = core.BNArchitectureAndAddress()
+ bn_arch_and_addr.arch = location.arch.handle
+ bn_arch_and_addr.address = location.addr
+ return core.BNFunctionLocationHasNoReturnCalls(self.handle, bn_arch_and_addr)
+
+ def get_callee_for_analysis(self, platform: 'platform.Platform', addr: int, exact: bool = False) -> Optional['Function']:
+ """
+ ``get_callee_for_analysis`` retrieves the callee function for the specified address and platform.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param platform.Platform platform: Platform of the callee function
+ :param int addr: Address of the callee function
+ :param bool exact: If True, only return a function if it exactly matches the address and platform
+ :return: The callee function or None if not found
+ :rtype: Optional[Function]
+ """
+ func = core.BNGetCalleeForAnalysis(self.handle, platform.handle, addr, exact)
+ if func is None:
+ return None
+ return Function(func, self._view)
+
+ def add_temp_outgoing_reference(self, target: 'Function') -> None:
+ """
+ ``add_temp_outgoing_reference`` adds a temporary outgoing reference to the specified target function.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param Function target: Target function to add a temporary outgoing reference to
+ :rtype: None
+ """
+ if not isinstance(target, Function):
+ raise TypeError("target must be an instance of Function")
+ core.BNFunctionAddTempOutgoingReference(self.handle, target.handle)
+
+ def has_temp_outgoing_reference(self, target: 'Function') -> bool:
+ """
+ ``has_temp_outgoing_reference`` checks if the function has a temporary outgoing reference to the specified target function.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param Function target: Target function to check for a temporary outgoing reference
+ :return: True if there is a temporary outgoing reference to the target function, False otherwise
+ :rtype: bool
+ """
+ if not isinstance(target, Function):
+ raise TypeError("target must be an instance of Function")
+ return core.BNFunctionHasTempOutgoingReference(self.handle, target.handle)
+
+ def add_temp_incoming_reference(self, source: 'Function') -> None:
+ """
+ ``add_temp_incoming_reference`` adds a temporary incoming reference from the specified source function.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param Function source: Source function to add a temporary incoming reference from
+ :rtype: None
+ """
+ if not isinstance(source, Function):
+ raise TypeError("source must be an instance of Function")
+ core.BNFunctionAddTempIncomingReference(self.handle, source.handle)
+
+ def get_contextual_function_return(self, location: ArchAndAddr) -> tuple[bool, bool]:
+ """
+ ``get_contextual_function_return`` checks if the specified location has a contextual function return.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param ArchAndAddr location: Location to check for a contextual function return
+ :return: A tuple containing a bool indicating whether the location has a contextual function return, and a bool containing the value that was set
+ :rtype: tuple[bool, bool]
+ """
+ bn_arch_and_addr = core.BNArchitectureAndAddress()
+ bn_arch_and_addr.arch = location.arch.handle
+ bn_arch_and_addr.address = location.addr
+ result = ctypes.c_bool()
+ if not core.BNFunctionGetContextualFunctionReturn(self.handle, bn_arch_and_addr, ctypes.byref(result)):
+ return (False, False)
+ return (True, result.value)
+
+ def set_contextual_function_return(self, location: ArchAndAddr, value: bool) -> None:
+ """
+ ``set_contextual_function_return`` sets a contextual function return for the specified location.
+
+ .. note:: This method is intended for use by architecture plugins only.
+
+ :param ArchAndAddr location: Location to set the contextual function return for
+ :param bool value: Value to set for the contextual function return
+ :rtype: None
+ """
+ bn_arch_and_addr = core.BNArchitectureAndAddress()
+ bn_arch_and_addr.arch = location.arch.handle
+ bn_arch_and_addr.address = location.addr
+ core.BNFunctionSetContextualFunctionReturn(self.handle, bn_arch_and_addr, value)
+
def get_instr_highlight(
self, addr: int, arch: Optional['architecture.Architecture'] = None
) -> '_highlight.HighlightColor':