def get_file_offset(self, addr: int) -> int: memory = self._program.getMemory() info = memory.getAddressSourceInfo(self._to_addr(addr)) if not info or info.getFileOffset() == -1: raise NotExistError( f"Cannot get file offset for address: {hex(addr)}") return info.getFileOffset()
def get_virtual_address(self, file_offset: int) -> int: addr = self._ida_loader.get_fileregion_ea(file_offset) if addr == self._idc.BADADDR: raise NotExistError( f"Cannot get linear address for file offset: {hex(file_offset)}" ) return addr
def get_string_bytes(self, addr: int, length: int = None, bit_width: int = None) -> bytes: addr_obj = self._to_addr(addr) # First check if a string or other data type is set here. string_obj = self._flatapi.getDataAt(addr_obj) if string_obj: if not string_obj.hasStringValue(): # TODO: Create a different custom exception here? raise NotExistError( f"Data type conflict at {hex(addr)}. Expected 'string', got '{string_obj.getDataType()}'" ) return bytes(string_obj.getBytes()).rstrip(b"\x00") from ghidra.app.util.bin import MemoryByteProvider, BinaryReader mem = self._program.getMemory() provider = MemoryByteProvider(mem, addr_obj) reader = BinaryReader(provider, not mem.isBigEndian()) if bit_width is None: bit_width = 8 # TODO: Determine how to auto detect string type. try: if bit_width == 8: return reader.readNextAsciiString().encode('ascii') elif bit_width == 16: return reader.readNextUnicodeString().encode('utf-16') else: raise ValueError(f"Invalid bit width: {bit_width}") except Exception as e: raise RuntimeError( f"Failed to create a string at {hex(addr)} with error: {e}")
def get_byte(self, addr: int) -> int: from ghidra.program.model.mem import MemoryAccessException try: # Mask necessary because jpype bytes are signed. return self._flatapi.getByte(self._to_addr(addr)) & 0xff except MemoryAccessException: raise NotExistError(f"Cannot get byte at {hex(addr)}")
def remove_parameter(self, ordinal: int): num_parameters = self._function.getParameterCount() if ordinal < 0: ordinal += num_parameters if ordinal not in range(num_parameters): raise NotExistError(f"Invalid ordinal for parameter deletion: {ordinal}") self._function.removeParameter(ordinal)
def get_segment(self, addr_or_name: Union[int, str]) -> IDASegment: if isinstance(addr_or_name, str): name = addr_or_name segment_t = self._ida_segment.get_segm_by_name(name) if not segment_t: raise NotExistError( f"Could not find segment with name: {name}") elif isinstance(addr_or_name, int): addr = addr_or_name segment_t = self._ida_segment.getseg(addr) if not segment_t: raise NotExistError( f"Could not find segment containing address: 0x{addr:08x}") else: raise ValueError(f"Invalid input: {addr_or_name!r}") return IDASegment(self, segment_t)
def __init__(self, ida: IDA, addr: int): super().__init__(ida) self._ida = ida self._addr = addr insn_t = self._ida._ida_helpers.get_instruction(addr) if not insn_t: raise NotExistError(f"Instruction does not exist at: {hex(addr)}") self._insn_t = insn_t
def get_virtual_address(self, file_offset: int) -> int: memory = self._program.getMemory() addresses = memory.locateAddressesForFileOffset(file_offset) if not addresses: raise NotExistError( f"Cannot get virtual address for file offset: {hex(file_offset)}" ) for address in addresses: return address.getOffset()
def __init__(self, ghidra: Ghidra, instruction: GhidraInstruction, index: int): super().__init__(instruction) self._ghidra = ghidra # Ghidra doesn't necessarily have an "Operand" type. # Characteristics are pulled from the originating Instruction object. self._instruction = instruction._instruction self._index = index # TODO: Ignore validation in interest of speed? if index >= self._instruction.getNumOperands(): raise NotExistError(f"The instruction at {hex(self.address)} as no operand at {index}")
def get_data_type(self, name: str) -> GhidraDataType: from ghidra.util.data import DataTypeParser from ghidra.program.model.data import InvalidDataTypeException # Must use .valueOf() because jpype doesn't handle enums well. parser = DataTypeParser(None, DataTypeParser.AllowedDataTypes.valueOf("ALL")) try: data_type = parser.parse(name) except InvalidDataTypeException as e: raise NotExistError(e) return GhidraDataType(data_type)
def get_export(self, name: str) -> Export: """ Gets export symbol by name. :param name: Name of import function :return: Export symbol :raises NotExistError: If export by the given name doesn't exist. """ for export in self.exports: if export.name == name: return export raise NotExistError(f"Export with name '{name}' doesn't exist.")
def _to_addr(self, addr: int) -> "Address": """ Internal function used to generate Ghidra Address object from integer. :raises NotExistError: If overflow error occurs. """ try: return self._flatapi.toAddr(hex(addr)) except OverflowError: raise NotExistError( f"Invalid address {hex(addr)}. Expect 32 bit integer, got {addr.bit_length()}" )
def get_block(self, addr: int) -> BasicBlock: """ Gets BasicBlock containing given address. Defaults to iterating each block and checking if address is contained within. :raises NotExistError: If block doesn't exist with in the flowchart for the given address. """ for block in self.blocks: if addr in block: return block raise NotExistError(f"Unable to find block containing address 0x{addr:08x} within flowchart.")
def get_import(self, name: str) -> Import: """ Gets import symbol by name. :param name: Name of import function :return: Import symbol :raises NotExistError: If import by the given name doesn't exist. """ for import_ in self.imports: if import_.name == name: return import_ raise NotExistError(f"Import with name '{name}' doesn't exist.")
def __init__(self, ida: IDA, addr: int): super().__init__(ida) self._ida = ida # IDA has no concept of a "line", so we'll keep track of the start address, # which IDA refers to as the "head". start_addr = self._ida._ida_bytes.get_item_head(addr) # If ida returns the unsigned value of -1 for some bit length if (start_addr - (1 << start_addr.bit_length())) == -1: raise NotExistError(f"Line at {hex(addr)} does not exist") self._addr = start_addr self._name = None
def get_variable(self, addr: int) -> IDAGlobalVariable: start_address = self._ida_bytes.get_item_head(addr) # Don't count code as "variables". Otherwise we get all the # loop labels as variables. flags = self._ida_bytes.get_flags(addr) is_code = self._ida_bytes.is_code(flags) # Only count as variable if item has a name. if not is_code and self._ida_name.get_name(start_address): return IDAGlobalVariable(self, start_address) else: raise NotExistError(f"Variable doesn't exist at {hex(addr)}")
def get_operand(self, addr: int, index: int) -> Operand: """ Returns an Operand object based on the operand at a given address. """ instruction = self.get_instruction(addr) operands = instruction.operands try: return operands[index] except IndexError: raise NotExistError( f"Instruction at {hex(instruction.address)} does not have an operand at index {index}" )
def get_function_signature(self, addr: int) -> GhidraFunctionSignature: address = self._to_addr(addr) function = self._flatapi.getFunctionAt(address) # If we don't find a function, address might be pointing to an external function pointer. (ie. import) if not function: for ref in self._flatapi.getReferencesFrom(address): if ref.isExternalReference(): function = ref.getExternalLocation().getFunction() break if not function: raise NotExistError( f"Function signature at {hex(addr)} does not exist.") return GhidraFunctionSignature(self, function)
def get_segment(self, addr_or_name: Union[int, str]) -> GhidraSegment: memory = self._program.getMemory() if isinstance(addr_or_name, str): name = addr_or_name memory_block = memory.getBlock(name) if not memory_block: raise NotExistError( f"Could not find segment with name: {name}") elif isinstance(addr_or_name, int): addr = addr_or_name memory_block = memory.getBlock(self._to_addr(addr)) if not memory_block: raise NotExistError( f"Could not find segment containing address 0x{addr:08x}") else: raise ValueError(f"Invalid input: {addr_or_name!r}") if not memory_block.isInitialized(): raise NotExistError( f"Memory block at {memory_block.getStart()} is not initialized." ) return GhidraSegment(self, memory_block)
def get_block(self, addr: int) -> GhidraBasicBlock: """ Gets BasicBlock containing given address. Defaults to iterating each block and checking if address is contained within. :raises NotExistError: If block doesn't exist with in the flowchart for the given address. """ block = self._ghidra._basic_block_model.getFirstCodeBlockContaining( self._ghidra._to_addr(addr), self._ghidra._monitor) if block: return GhidraBasicBlock(self._ghidra, block) else: raise NotExistError( f"Unable to find block containing address 0x{addr:08x} within flowchart." )
def get_instruction(self, addr: int) -> Instruction: """ Returns an Instruction object for the given any address contained within the instruction. :param int addr: Address of the instruction :return: Instruction object :raises NotExistError: If instruction isn't present at given address. """ line = self.get_line(addr) instruction = line.instruction if instruction: return instruction else: raise NotExistError(f"Instruction not found at {hex(addr)}")
def get(self, address: int, size: int, fill_pattern=b"\x00") -> bytes: """ Obtains bytes from given address range. Non-initialized bytes are filled with 0 bytes. :param address: Start address. :param size: Number of bytes to obtain. :param fill_pattern: byte value to fill non-initialized bytes. If set to None, a NotExistError will be thrown if data is not contiguous. """ self._obtain_uncached_chunks(address, size) if fill_pattern: return bytes(self._memory[address:address+size:fill_pattern]) else: try: return bytes(self._memory[address:address+size]) except ValueError as e: raise NotExistError(f"Unable to obtain {size} bytes from 0x{address:08X}: {e}")
def get_bytes(self, addr: int, length: int, default: int = None) -> bytes: from ghidra.program.model.mem import MemoryAccessException if default is None: try: return memoryview( self._flatapi.getBytes(self._to_addr(addr), length)).tobytes() except MemoryAccessException: raise NotExistError(f"Cannot get bytes at {hex(addr)}") else: data = bytearray() for _addr in range(addr, addr + length): try: data.append( self._flatapi.getByte(self._to_addr(_addr)) & 0xFF) except MemoryAccessException: data.append(default) return bytes(data)
def get_data_type(self, name: str) -> IDADataType: is_ptr = name.endswith("*") name = name.strip(" *") # Name has to be uppercase for get_named_type() to work. name = name.upper() # Create new tinfo object of type. tif = self._ida_typeinf.tinfo_t() success = tif.get_named_type(self._ida_typeinf.get_idati(), name) if not success: raise NotExistError(f"Invalid data type: {name}") # If a pointer, create another tinfo object that is the pointer of the first. if is_ptr: tif2 = self._ida_typeinf.tinfo_t() tif2.create_ptr(tif) tif = tif2 return IDADataType(self, tif)
def lines(self, start=None, reverse=False) -> Iterable[Line]: """ Iterates Lines within the block. :param start: Start address (defaults to start or end address) :param reverse: Direction to iterate :yields: Line objects :raises NotExistError: If given start address is not in block """ if start is not None and start not in self: raise NotExistError(f"Start address {hex(start)} is not in block.") if reverse: start = start or (self.end - 1) end = self.start - 1 else: start = start or self.start end = self.end yield from self._api.lines(start=start, end=end, reverse=reverse)
def get_bytes(self, addr: int, length: int, default: int = None) -> bytes: if default is None and not self._ida_helpers.is_loaded(addr, length): raise NotExistError( f"Unable to obtain {length} bytes from 0x{addr:08X}: " f"Address range not fully loaded.") return self._ida_helpers.get_bytes(addr, length, default=default or 0)
def get_register(self, name: str) -> GhidraRegister: reg = self._program.getRegister(name) if not reg: raise NotExistError(f"Invalid register name: {name}") return GhidraRegister(reg)
def get_function(self, addr: int) -> GhidraFunction: function = self._flatapi.getFunctionContaining(self._to_addr(addr)) if not function: raise NotExistError( f"Function containing {hex(addr)} does not exist.") return GhidraFunction(self, function)
def get_variable(self, addr: int) -> GhidraGlobalVariable: data = self._flatapi.getDataContaining(self._to_addr(addr)) if not data: raise NotExistError(f"Variable doesn't exist at {hex(addr)}") return GhidraGlobalVariable(self, data)
def get_byte(self, addr: int) -> int: if not self._bytes_loaded(addr, 1): raise NotExistError(f"Cannot get byte at {hex(addr)}") return self._ida_bytes.get_wide_byte(addr)