diff options
Diffstat (limited to 'python')
| -rw-r--r-- | python/architecture.py | 59 | ||||
| -rw-r--r-- | python/basicblock.py | 129 | ||||
| -rw-r--r-- | python/binaryview.py | 37 | ||||
| -rw-r--r-- | python/function.py | 180 |
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': |
