Skip to main content
Glama
dflatline

HopperPyMCP

by dflatline
hopper_api.py66.7 kB
"""<p>Here are all the public classes, and methods that you can use to script Hopper using Python.""" """</p>""" """<p>Before using the scripting capabilities of Hopper, you need to understand the nature of a Hopper document.""" """The basic concept is that it is a document constructed from Segments, each containing Sections of typed bytes.""" """Let's consider a standard Mach-O file. It contains at least one segment of code (usually named TEXT)""" """containing many bytes. Hopper attaches information to each bytes of each segments. At load time, all""" """bytes are set to the type <b>TYPE_UNDEFINED</b>. Then, starting from the entry point, as Hopper follows the program flow it will set""" """instructions to the type <b>TYPE_CODE</b>; if an instruction needs more than one byte, the following are set to <b>TYPE_NEXT</b>.""" """</p>""" """<p>Using Python, you'll can manipulate the segments of disassembled files, retrieve information on bytes types, read or write data,""" """create or modify label names, etc...""" """You'll usually start by retrieving the current document, using the static method <b>Document.getCurrentDocument()</b>.<br/>""" """</p>""" """<hr/>""" # NOTE: THIS FILE IS FOR REFERENCE AND TYPE SAFETY ONLY! HOPPER INJECTS ITS OWN VERSION OF THIS FILE. DO NOT MODIFY! # # This file extracted from Hopper version 5.19.6, import os import sys import struct import HopperLowLevel class HopperStdRedirection: # NO_DOC def __init__(self,tag): self.tag = tag self.line_buffer = "" def __del__(self): if (len(self.line_buffer)): HopperLowLevel.outputString(self.tag, self.line_buffer) self.line_buffer = "" def flushLines(self): idx = self.line_buffer.find('\n') while idx != -1: flushed = self.line_buffer[:idx] self.line_buffer = self.line_buffer[idx+1:] HopperLowLevel.outputString(self.tag,flushed) idx = self.line_buffer.find('\n') def flush(self): self.flushLines() def write(self,str): self.line_buffer += str self.flushLines() class CallReference: """An object representing a call inside from / to a procedure.""" CALL_NONE = 0 CALL_UNKNOWN = 1 CALL_DIRECT = 2 CALL_OBJC = 3 def __init__(self,callType,fromAddress,toAddress): self.__type__ = callType self.__from__ = fromAddress self.__to__ = toAddress def type(self): """Returns the type of call. This is one of the value CALL_NONE, CALL_UNKNOWN, CALL_DIRECT, or CALL_OBJC.""" return self.__type__ def fromAddress(self): """Source of the reference""" return self.__from__ def toAddress(self): """Referenced address""" return self.__to__ class LocalVariable: """A procedure's local variable.""" def __init__(self,name,displacement): self.__name__ = name self.__displacement__ = displacement def name(self): """Name of the local variable""" return self.__name__ def displacement(self): """Displacement on the stack""" return self.__displacement__ class Tag: """A Tag that could be applied to a specific address, a BasicBlock or a Procedure.""" """Tags are built using the document.""" def __init__(self,tag_internal): self.__tag_internal__ = tag_internal def __eq__(self,other): return other.__class__ == self.__class__ and self.__tag_internal__ == other.__tag_internal__ def __ne__(self,other): return other.__class__ != self.__class__ or self.__tag_internal__ != other.__tag_internal__ def getName(self): """Returns a string with the tag name.""" return HopperLowLevel.getTagName(self.__tag_internal__) class Procedure: """This class represents a procedure, which is a collection of BasicBlocks.""" """Please note that modifying the document (creating new procedure, or deleting an existing one), will result in a possible""" """inconsistancy of the Python's Procedure object representation.<br/>""" """Procedure class defines some constants:<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_GENERAL_PURPOSE_REGISTER</b> = 2<br/>""" """<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_X86_FPU</b> = 3<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_X86_MMX</b> = 4<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_X86_SSE</b> = 5<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_X86_AVX</b> = 6<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_X86_CR</b> = 7<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_X86_DR</b> = 8<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_X86_SPECIAL</b> = 9<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_X86_MEMMGMT</b> = 10<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_X86_SEG</b> = 11<br/>""" """<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_ARM_VFP_SINGLE</b> = 3<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_ARM_VFP_DOUBLE</b> = 4<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_ARM_VFP_QUAD</b> = 5<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_ARM_MEDIA</b> = 6<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGCLS_ARM_SPECIAL</b> = 7<br/>""" """<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_RAX</b> = 0<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_RCX</b> = 1<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_RDX</b> = 2<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_RBX</b> = 3<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_RSP</b> = 4<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_RBP</b> = 5<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_RSI</b> = 6<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_RDI</b> = 7<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_R8</b> = 8<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_R9</b> = 9<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_R10</b> = 10<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_R11</b> = 11<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_R12</b> = 12<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_R13</b> = 13<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_R14</b> = 14<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_R15</b> = 15<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>REGIDX_X86_RIP</b> = 16<br/>""" REGCLS_GENERAL_PURPOSE_REGISTER = 2 REGCLS_X86_FPU = 3 REGCLS_X86_MMX = 4 REGCLS_X86_SSE = 5 REGCLS_X86_AVX = 6 REGCLS_X86_CR = 7 REGCLS_X86_DR = 8 REGCLS_X86_SPECIAL = 9 REGCLS_X86_MEMMGMT = 10 REGCLS_X86_SEG = 11 REGCLS_ARM_VFP_SINGLE = 3 REGCLS_ARM_VFP_DOUBLE = 4 REGCLS_ARM_VFP_QUAD = 5 REGCLS_ARM_MEDIA = 6 REGCLS_ARM_SPECIAL = 7 REGIDX_X86_RAX = 0 REGIDX_X86_RCX = 1 REGIDX_X86_RDX = 2 REGIDX_X86_RBX = 3 REGIDX_X86_RSP = 4 REGIDX_X86_RBP = 5 REGIDX_X86_RSI = 6 REGIDX_X86_RDI = 7 REGIDX_X86_R8 = 8 REGIDX_X86_R9 = 9 REGIDX_X86_R10 = 10 REGIDX_X86_R11 = 11 REGIDX_X86_R12 = 12 REGIDX_X86_R13 = 13 REGIDX_X86_R14 = 14 REGIDX_X86_R15 = 15 REGIDX_X86_RIP = 16 def __init__(self,segment_internal,procedure_index): self.__segment_internal__ = segment_internal self.__procedure_index__ = procedure_index def __eq__(self,other): return other.__class__ == self.__class__ and self.__segment_internal__ == other.__segment_internal__ and self.__procedure_index__ == other.__procedure_index__ def __ne__(self,other): return other.__class__ != self.__class__ or self.__segment_internal__ != other.__segment_internal__ or self.__procedure_index__ != other.__procedure_index__ def getSegment(self): """Returns the segment this procedure belongs to.""" return Segment(self.__segment_internal__) def getSection(self): """Returns the section this procedure belongs to.""" return self.getSegment().getSectionAtAddress(self.getEntryPoint()) def getEntryPoint(self): """Returns the address of the entry point.""" return HopperLowLevel.getProcedureEntryPoint(self.__segment_internal__,self.__procedure_index__) def getBasicBlockCount(self): """Returns the total number of basic blocks.""" return HopperLowLevel.getBasicBlockCount(self.__segment_internal__,self.__procedure_index__) def getBasicBlock(self,index): """Get a BasicBlock object by index.""" return BasicBlock(self,index) if index >= 0 and index < self.getBasicBlockCount() else None def getBasicBlockAtAddress(self,addr): """Returns the basic block which contains an instruction starting at the given address, or None.""" index = HopperLowLevel.getBasicBlockIndexAtAddress(self.__segment_internal__,self.__procedure_index__,addr) return BasicBlock(self,index) if index >= 0 and index < self.getBasicBlockCount() else None def basicBlockIterator(self): """Iterate over all basic blocks of the procedure""" for index in range(self.getBasicBlockCount()): yield self.getBasicBlock(index) def getHeapSize(self): """Returns the heap size of the procedure in bytes.""" return HopperLowLevel.getProcedureHeapSize(self.__segment_internal__,self.__procedure_index__) def getLocalVariableList(self): """Returns the list of all local variables.""" return HopperLowLevel.getLocalVariableList(self.__segment_internal__,self.__procedure_index__) def addTag(self,tag): """Add a tag to the procedure.""" HopperLowLevel.addTagToProcedure(self.__segment_internal__,self.__procedure_index__,tag.__tag_internal__) def removeTag(self,tag): """Remove the tag from the procedure.""" HopperLowLevel.removeTagFromProcedure(self.__segment_internal__,self.__procedure_index__,tag.__tag_internal__) def hasTag(self,tag): """Returns True if the procedure has this tag.""" return HopperLowLevel.procedureHasTag(self.__segment_internal__,self.__procedure_index__,tag.__tag_internal__) def getTagCount(self): """Returns the number of tags for this procedure.""" return HopperLowLevel.getProcedureTagCount(self.__segment_internal__,self.__procedure_index__) def getTagAtIndex(self,index): """Returns the Nth tag of the procedure.""" tag_ptr = HopperLowLevel.getProcedureTagAtIndex(self.__segment_internal__,self.__procedure_index__,index) return Tag(tag_ptr) if tag_ptr != 0 else None def tagIterator(self): """Iterate over all tags of the procedure""" for index in range(self.getTagCount()): yield self.getTagAtIndex(index) def getTagList(self): """Returns a list a all tags for this procedure.""" return [tag for tag in self.tagIterator()] def getAllCallers(self): """Returns a list of CallReference objects representing callers of this procedure.""" return HopperLowLevel.getAllCallers(self.__segment_internal__,self.__procedure_index__) def getAllCallees(self): """Returns a list of CallReference objects representing all the places called by this procedure.""" return HopperLowLevel.getAllCallees(self.__segment_internal__,self.__procedure_index__) def getAllCallerProcedures(self): """Returns a list of Procedure objects representing callers of this procedure.""" return HopperLowLevel.getAllCallerProcedures(self.__segment_internal__,self.__procedure_index__) def getAllCalleeProcedures(self): """Returns a list of CallReference objects representing all the procedures called by this procedure.""" return HopperLowLevel.getAllCalleeProcedures(self.__segment_internal__,self.__procedure_index__) def hasLocalLabelAtAddress(self,addr): """Return True if there is a local label at a given address.""" return HopperLowLevel.hasLocalLabelAtAddress(self.__segment_internal__,self.__procedure_index__,addr) def localLabelAtAddress(self,addr): """Return the local label name at a given address, or None if there is no label at this address.""" return HopperLowLevel.localLabelAtAddress(self.__segment_internal__,self.__procedure_index__,addr) def setLocalLabelAtAddress(self,label,addr): """Set the local label for a given address.""" return HopperLowLevel.setLocalLabelAtAddress(self.__segment_internal__,self.__procedure_index__,label,addr) def declareLocalLabelAt(self,addr): """Create a local label at a given address, and return its name.""" return HopperLowLevel.declareLocalLabelAt(self.__segment_internal__,self.__procedure_index__,addr) def removeLocalLabelAtAddress(self,addr): """Remove a local label.""" return HopperLowLevel.removeLocalLabelAtAddress(self.__segment_internal__,self.__procedure_index__,addr) def addressOfLocalLabel(self,label): """Return the address of the local label.""" return HopperLowLevel.addressOfLocalLabel(self.__segment_internal__,self.__procedure_index__,label) def decompile(self): """Returns a string containing the pseudo-code of the procedure, or None if the decompilation is not possible.""" return HopperLowLevel.decompile(self.__segment_internal__,self.__procedure_index__) def signatureString(self): """Returns a string containing the signature of the procedure.""" return HopperLowLevel.procedureSignature(self.__segment_internal__,self.__procedure_index__) def renameRegister(self,reg_cls,reg_idx,name): """Rename the register reg_idx, of class reg_cls to the provided name. The reg_cls argument is one of the REGCLS_* constants.""" return HopperLowLevel.renameRegister(self.__segment_internal__,self.__procedure_index__,reg_cls,reg_idx,name) def registerNameOverride(self,reg_cls,reg_idx): """Returns the name given to the register, if it has been previously renamed. Otherwise, returns None. The reg_cls argument is one of the REGCLS_* constants.""" return HopperLowLevel.registerNameOverride(self.__segment_internal__,self.__procedure_index__,reg_cls,reg_idx) def clearRegisterNameOverride(self,reg_cls,reg_idx): """Clear register renaming. The reg_cls argument is one of the REGCLS_* constants.""" return HopperLowLevel.clearRegisterNameOverride(self.__segment_internal__,self.__procedure_index__,reg_cls,reg_idx) class BasicBlock: """A BasicBlock is a set of instructions that is guaranteed to be executed in a whole, if the control flow reach the first instruction.""" def __init__(self,procedure,basic_block_index): self.__procedure__ = procedure self.__basic_block_index__ = basic_block_index def __eq__(self,other): return other.__class__ == self.__class__ and self.__procedure__ == other.__procedure__ and self.__basic_block_index__ == other.__basic_block_index__ def __ne__(self,other): return other.__class__ != self.__class__ or self.__procedure__ != other.__procedure__ or self.__basic_block_index__ != other.__basic_block_index__ def getProcedure(self): """Returns the Procedure object this BasicBlock belongs to.""" return self.__procedure__ def getStartingAddress(self): """Returns the address of the first instruction of the BasicBlock.""" return HopperLowLevel.getBasicBlockStartingAddress(self.__procedure__.__segment_internal__,self.__procedure__.__procedure_index__,self.__basic_block_index__) def getEndingAddress(self): """Returns the address following the last instruction of the BasicBlock.""" return HopperLowLevel.getBasicBlockEndingAddress(self.__procedure__.__segment_internal__,self.__procedure__.__procedure_index__,self.__basic_block_index__) def getSuccessorCount(self): """Returns the number of successors for this BasicBlock.""" return HopperLowLevel.getBasicBlockSuccessorCount(self.__procedure__.__segment_internal__,self.__procedure__.__procedure_index__,self.__basic_block_index__) def getSuccessorIndexAtIndex(self,index): """Returns the BasicBlock index of the Nth successors.""" return HopperLowLevel.getBasicBlockSuccessorIndex(self.__procedure__.__segment_internal__,self.__procedure__.__procedure_index__,self.__basic_block_index__,index) def getSuccessorAddressAtIndex(self,index): """Returns the BasicBlock address of the Nth successors.""" return HopperLowLevel.getBasicBlockSuccessorAddress(self.__procedure__.__segment_internal__,self.__procedure__.__procedure_index__,self.__basic_block_index__,index) def addTag(self,tag): """Add a tag to the basic block.""" HopperLowLevel.addTagToBasicBlock(self.__procedure__.__segment_internal__,self.__procedure__.__procedure_index__,self.__basic_block_index__,tag.__tag_internal__) def removeTag(self,tag): """Remove the tag from the basic block.""" HopperLowLevel.removeTagFromBasicBlock(self.__procedure__.__segment_internal__,self.__procedure__.__procedure_index__,self.__basic_block_index__,tag.__tag_internal__) def hasTag(self,tag): """Returns True if the basic block has this tag.""" return HopperLowLevel.basicBlockHasTag(self.__procedure__.__segment_internal__,self.__procedure__.__procedure_index__,self.__basic_block_index__,tag.__tag_internal__) def getTagCount(self): """Return the number of tags for this basic block.""" return HopperLowLevel.getBasicBlockTagCount(self.__procedure__.__segment_internal__,self.__procedure__.__procedure_index__,self.__basic_block_index__) def getTagAtIndex(self,index): """Return the Nth tag of the basic block.""" tag_ptr = HopperLowLevel.getBasicBlockTagAtIndex(self.__procedure__.__segment_internal__,self.__procedure__.__procedure_index__,self.__basic_block_index__,index) return Tag(tag_ptr) if tag_ptr != 0 else None def tagIterator(self): """Iterate over all tags of the basic block""" for index in range(self.getTagCount()): yield self.getTagAtIndex(index) def getTagList(self): """Return a list a all tags for this basic block.""" return [tag for tag in self.tagIterator()] class Instruction: """This class represents a disassembled instruction.""" """The class defines some constants, like <b>ARCHITECTURE_i386</b>, and <b>ARCHITECTURE_X86_64</b>""" ARCHITECTURE_UNKNOWN = 0 ARCHITECTURE_i386 = 1 ARCHITECTURE_X86_64 = 2 ARCHITECTURE_ARM = 3 ARCHITECTURE_ARM_THUMB = 4 ARCHITECTURE_AARCH64 = 5 ARCHITECTURE_OTHER = 99 def __init__(self,archi,instr,rawArgs,formattedArgs,cjmp,ijmp,instrLen): self.__archi__ = archi self.__instr__ = instr self.__rawArgs__ = rawArgs self.__formattedArgs__ = formattedArgs self.__cjmp__ = cjmp self.__ijmp__ = ijmp self.__instrLen__ = instrLen @staticmethod def stringForArchitecture(t): """Helper method which converts one of the architecture value (<b>ARCHITECTURE_UNKNOWN</b>, <b>ARCHITECTURE_i386</b>, <b>ARCHITECTURE_X86_64</b>, <b>ARCHITECTURE_ARM</b>, <b>ARCHITECTURE_ARM_THUMB</b>, or <b>ARCHITECTURE_AARCH64</b>) to a string value.""" return { Instruction.ARCHITECTURE_UNKNOWN : "unknown", Instruction.ARCHITECTURE_i386 : "i386", Instruction.ARCHITECTURE_X86_64 : "x86_64", Instruction.ARCHITECTURE_ARM : "ARM", Instruction.ARCHITECTURE_ARM_THUMB : "ARM (Thumb)", Instruction.ARCHITECTURE_AARCH64 : "AArch64", Instruction.ARCHITECTURE_OTHER : "Other (plugin)" }.get(t, "<unknown>") def getArchitecture(self): """Returns the architecture.""" return self.__archi__ def getInstructionString(self): """Return a strings representing the instruction.""" return self.__instr__ def getArgumentCount(self): """Returns the number of argument.""" return len(self.__rawArgs__) def getRawArgument(self,index): """Returns the instruction argument, identified by an index. The argument is not modified by Hopper, and represents the raw ASM argument.""" if index < 0 or index >= len(self.__rawArgs__): return None return self.__rawArgs__[index] def getFormattedArgument(self,index): """Returns the instruction argument, identified by an index. The argument may have been modified according to the user, or by Hopper if""" """ a specific pattern has been detected.""" if index < 0 or index >= len(self.__formattedArgs__): return None return self.__formattedArgs__[index] def isAnInconditionalJump(self): """Returns True if the instruction represents an inconditional jump.""" return self.__ijmp__ def isAConditionalJump(self): """Returns True if the instruction represents a conditional jump.""" return self.__cjmp__ def getInstructionLength(self): """Returns the instruction length in byte.""" return self.__instrLen__ class Section: """This class represents a section of a segment.""" def __init__(self,addr): self.__internal_section_addr__ = addr def __eq__(self,other): return other.__class__ == self.__class__ and self.__internal_section_addr__ == other.__internal_section_addr__ def __ne__(self,other): return other.__class__ != self.__class__ or self.__internal_section_addr__ != other.__internal_section_addr__ def getName(self): """Returns the name of the section.""" return HopperLowLevel.getSectionName(self.__internal_section_addr__) def getStartingAddress(self): """Returns the starting address of the section.""" return HopperLowLevel.getSectionStartingAddress(self.__internal_section_addr__) def getLength(self): """Returns the length, in bytes, of the section.""" return HopperLowLevel.getSectionLength(self.__internal_section_addr__) def getFlags(self): """Returns the flags of the section.""" return HopperLowLevel.getSectionFlags(self.__internal_section_addr__) class Segment: """This class represents a segment of a disassembled file.""" """The class defines some values that are used as the type""" """of bytes of the disassembled file.<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>TYPE_UNDEFINED</b> : an undefined byte<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>TYPE_OUTSIDE</b> : the byte is not in a mapped section<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>TYPE_NEXT</b> : a byte that is part of a larger data type (ex, the second byte of a 4 bytes integer...)<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>TYPE_INT8</b> : an integer of a single byte<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>TYPE_INT16</b> : an integer of 2 bytes<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>TYPE_INT32</b> : an integer of 4 bytes<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>TYPE_INT64</b> : an integer of 8 bytes<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>TYPE_ASCII</b> : an ASCII string<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>TYPE_ALIGN</b> : an alignment<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>TYPE_UNICODE</b> : an UNICODE string<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>TYPE_CODE</b> : an instruction<br/>""" """&nbsp;&nbsp;&nbsp;&nbsp;<b>TYPE_PROCEDURE</b> : a procedure<br/>""" """<br/>""" """The class defines the constant <b>BAD_ADDRESS</b> which is returned by some methods when the""" """requested information is incorrect.""" BAD_ADDRESS=-1 TYPE_UNDEFINED=0 TYPE_OUTSIDE=1 TYPE_NEXT=2 TYPE_INT8=3 TYPE_INT16=4 TYPE_INT32=5 TYPE_INT64=6 TYPE_ASCII=7 TYPE_UNICODE=8 TYPE_ALIGN=63 TYPE_CODE=65 TYPE_PROCEDURE=66 TYPE_STRUCTURE=67 @staticmethod def stringForType(t): """Helper method that converts one of the type value (<b>TYPE_UNDEFINED</b>, <b>TYPE_NEXT</b>, ...) to a string value.""" return { Segment.TYPE_UNDEFINED : "undefined", Segment.TYPE_NEXT : "next", Segment.TYPE_INT8 : "int8", Segment.TYPE_INT16 : "int16", Segment.TYPE_INT32 : "int32", Segment.TYPE_INT64 : "int64", Segment.TYPE_ASCII : "ascii", Segment.TYPE_UNICODE : "unicode", Segment.TYPE_ALIGN : "align", Segment.TYPE_CODE : "code", Segment.TYPE_PROCEDURE : "procedure", Segment.TYPE_STRUCTURE : "structure", }.get(t, "<unknown>") def __init__(self,addr): self.__internal_segment_addr__ = addr def __eq__(self,other): return other.__class__ == self.__class__ and self.__internal_segment_addr__ == other.__internal_segment_addr__ def __ne__(self,other): return other.__class__ != self.__class__ or self.__internal_segment_addr__ != other.__internal_segment_addr__ def getName(self): """Returns the name of the segment.""" return HopperLowLevel.getSegmentName(self.__internal_segment_addr__) def getStartingAddress(self): """Returns the starting address of the segment.""" return HopperLowLevel.getSegmentStartingAddress(self.__internal_segment_addr__) def getLength(self): """Returns the length, in bytes, of the segment.""" return HopperLowLevel.getSegmentLength(self.__internal_segment_addr__) def getFileOffset(self): """Returns the file offset of the beginning of the segment.""" return HopperLowLevel.getFileOffset(self.__internal_segment_addr__) def getFileOffsetForAddress(self,addr): """Returns the file offset of a particular address.""" return self.getFileOffset() + addr - self.getStartingAddress() def getSectionCount(self): """Returns the number of section this segment contains.""" return HopperLowLevel.getSectionCount(self.__internal_segment_addr__); def getSection(self,index): """Returns a section by index. The returned object is an instance of the Section class. If the index of not in the range [0;count[, the function returns None.""" addr = HopperLowLevel.getSectionAddress(self.__internal_segment_addr__, index) if addr == 0: return None return Section(addr) def getSectionsList(self): """Returns a list containing all the sections.""" return [self.getSection(x) for x in range(self.getSectionCount())] def getSectionIndexAtAddress(self,addr): """Returns the section index for a particular address.""" return HopperLowLevel.getSectionIndexAtAddress(self.__internal_segment_addr__,addr) def getSectionAtAddress(self,addr): """Returns the section for a particular address.""" idx=self.getSectionIndexAtAddress(addr) if idx == -1: return None return self.getSection(idx) def readBytes(self,addr,length): """Read bytes at a given address range. Returns False if the byte is read outside of the segment.""" return HopperLowLevel.readBytes(self.__internal_segment_addr__,addr,length) def readByte(self,addr): """Read a byte (between 0..255), read at a given address. Returns False if the byte is read outside of the segment.""" result = HopperLowLevel.readBytes(self.__internal_segment_addr__,addr,1) if result == False: return False return struct.unpack("B", result)[0] def readUInt16LE(self,addr): """Read a 16 bits little endian integer.""" return struct.unpack("<H", self.readBytes(addr, 2))[0] def readUInt32LE(self,addr): """Read a 32 bits little endian integer.""" return struct.unpack("<I", self.readBytes(addr, 4))[0] def readUInt64LE(self,addr): """Read a 64 bits little endian integer.""" return struct.unpack("<Q", self.readBytes(addr, 8))[0] def readUInt16BE(self,addr): """Read a 16 bits big endian integer.""" return struct.unpack(">H", self.readBytes(addr, 2))[0] def readUInt32BE(self,addr): """Read a 32 bits big endian integer.""" return struct.unpack(">I", self.readBytes(addr, 4))[0] def readUInt64BE(self,addr): """Read a 64 bits big endian integer.""" return struct.unpack(">Q", self.readBytes(addr, 8))[0] def writeBytes(self,addr,bytesStr): """Write bytes at a given address. Bytes are given as 'bytes'. Returns True if the writting has succeed.""" return HopperLowLevel.writeBytes(self.__internal_segment_addr__,addr,bytesStr) def writeByte(self,addr,value): """Write a byte at a given address. Returns True if the writting has succeed.""" return HopperLowLevel.writeBytes(self.__internal_segment_addr__,addr,struct.pack("B", value)) def writeUInt16LE(self,addr,value): """Write a 16 bits little endian integer. Returns True if succeeded.""" return self.writeBytes(addr,struct.pack("<H", value)) def writeUInt32LE(self,addr,value): """Write a 32 bits little endian integer. Returns True if succeeded.""" return self.writeBytes(addr,struct.pack("<I", value)) def writeUInt64LE(self,addr,value): """Write a 64 bits little endian integer. Returns True if succeeded.""" return self.writeBytes(addr,struct.pack("<Q", value)) def writeUInt16BE(self,addr,value): """Write a 16 bits big endian integer. Returns True if succeeded.""" return self.writeBytes(addr,struct.pack(">H", value)) def writeUInt32BE(self,addr,value): """Write a 32 bits big endian integer. Returns True if succeeded.""" return self.writeBytes(addr,struct.pack(">I", value)) def writeUInt64BE(self,addr,value): """Write a 64 bits big endian integer. Returns True if succeeded.""" return self.writeBytes(addr,struct.pack(">Q", value)) def markAsUndefined(self,addr): """Mark the address as being undefined.""" return HopperLowLevel.markAsUndefined(self.__internal_segment_addr__,addr) def markRangeAsUndefined(self,addr,length): """Mark the address range as being undefined.""" return HopperLowLevel.markRangeAsUndefined(self.__internal_segment_addr__,addr,length) def markAsCode(self,addr): """Mark the address as being code.""" return HopperLowLevel.markAsCode(self.__internal_segment_addr__,addr) def markAsProcedure(self,addr): """Mark the address as being a procedure.""" return HopperLowLevel.markAsProcedure(self.__internal_segment_addr__,addr) def markAsDataByteArray(self,addr,count): """Mark the address as being byte array.""" return HopperLowLevel.markAsDataByteArray(self.__internal_segment_addr__,addr,count) def markAsDataShortArray(self,addr,count): """Mark the address as being a short array.""" return HopperLowLevel.markAsDataShortArray(self.__internal_segment_addr__,addr,count) def markAsDataIntArray(self,addr,count): """Mark the address as being an int array.""" return HopperLowLevel.markAsDataIntArray(self.__internal_segment_addr__,addr,count) def isThumbAtAddress(self,addr): """Returns True is instruction at address addr is ARM Thumb mode.""" return HopperLowLevel.isThumbAtAddress(self.__internal_segment_addr__,addr) def setThumbModeAtAddress(self,addr): """Set the Thumb mode at the given address.""" return HopperLowLevel.setThumbModeAtAddress(self.__internal_segment_addr__,addr) def setARMModeAtAddress(self,addr): """Set the ARM mode at the given address.""" return HopperLowLevel.setARMModeAtAddress(self.__internal_segment_addr__,addr) def getTypeAtAddress(self,addr): """Returns the type of the byte at a given address.""" """The type can be <b>TYPE_UNDEFINED</b>, <b>TYPE_NEXT</b>, <b>TYPE_INT8</b>, ...""" """The method will returns None if the address is outside the segment.""" return HopperLowLevel.getTypeAtAddress(self.__internal_segment_addr__,addr) def setTypeAtAddress(self,addr,length,typeValue): """Set the type of a byte range.""" """The type must be <b>TYPE_UNDEFINED</b>, <b>TYPE_INT8</b>, ...""" """You cannot use this method for alignments. Please use <b>makeAlignment</b> instead.""" return HopperLowLevel.setTypeAtAddress(self.__internal_segment_addr__,addr,length,typeValue) def makeAlignment(self,addr,size): """Create an alignment at a given address so that the next address is a multiple of size.""" """Size can only be 2, 4, 8, 16, 32 or 64.""" return HopperLowLevel.makeAlignment(self.__internal_segment_addr__,addr,size) def getNextAddressWithType(self,addr,typeValue): """Returns the next address of a given type.""" """The search begins at the given address, so the returned value can be the given address itself.""" """If no address are found, the returned value is <b>BAD_ADDRESS</b>.""" return HopperLowLevel.getNextAddressWithType(self.__internal_segment_addr__,addr,typeValue) def disassembleWholeSegment(self): """Disassemble the whole segment.""" return HopperLowLevel.disassembleWholeSegment(self.__internal_segment_addr__) def setNameAtAddress(self,addr,name): """Set the label name at a given address.""" return (HopperLowLevel.setNameAtAddress(self.__internal_segment_addr__,addr,name) == 1) def getNameAtAddress(self,addr): """Get the label name at a given address.""" return HopperLowLevel.getNameAtAddress(self.__internal_segment_addr__,addr) def getDemangledNameAtAddress(self,addr): """Get the demangled label name at a given address.""" return HopperLowLevel.getDemangledNameAtAddress(self.__internal_segment_addr__,addr) def getCommentAtAddress(self,addr): """Get the prefix comment at a given address.""" return HopperLowLevel.getCommentAtAddress(self.__internal_segment_addr__,addr) def setCommentAtAddress(self,addr,comment): """Set the prefix comment at a given address.""" return HopperLowLevel.setCommentAtAddress(self.__internal_segment_addr__,addr,comment) def getInlineCommentAtAddress(self,addr): """Get the inline comment at a given address.""" return HopperLowLevel.getInlineCommentAtAddress(self.__internal_segment_addr__,addr) def setInlineCommentAtAddress(self,addr,comment): """Set the inline comment at a given address.""" return HopperLowLevel.setInlineCommentAtAddress(self.__internal_segment_addr__,addr,comment) def getInstructionAtAddress(self,addr): """Get the disassembled instruction at a given address.""" infos = HopperLowLevel.getInstructionAtAddress(self.__internal_segment_addr__,addr) if infos == None: return None return Instruction(infos[0], infos[1], infos[2], infos[3], infos[4], infos[5], infos[6]) def getReferencesOfAddress(self,addr): """Get the list of addresses that reference a given address.""" return HopperLowLevel.getReferencesOfAddress(self.__internal_segment_addr__,addr) def getReferencesFromAddress(self,addr): """Get the list of addresses referenced by a given address.""" return HopperLowLevel.getReferencesFromAddress(self.__internal_segment_addr__,addr) def addReference(self,addr,referenced): """Add a cross reference to the 'referenced' address from 'addr' address.""" return HopperLowLevel.addReference(self.__internal_segment_addr__,addr,referenced) def removeReference(self,addr,referenced): """Remove the cross reference to the 'referenced' address from 'addr' address.""" return HopperLowLevel.removeReference(self.__internal_segment_addr__,addr,referenced) def getLabelCount(self): """Get the number of named addresses.""" return HopperLowLevel.getLabelCount(self.__internal_segment_addr__) def getLabelName(self,index): """Get a label name by index.""" return HopperLowLevel.getLabelName(self.__internal_segment_addr__,index) def labelIterator(self): """Iterate over all the labels of a segment.""" for label in self.getLabelsList(): yield label def getLabelsList(self): """Returns a list with all the label of a segment.""" return HopperLowLevel.getLabelsList(self.__internal_segment_addr__) def getNamedAddresses(self): """Returns a list of all addresses with a label name. The list has the same order as the list returned by getLabelsList.""" return HopperLowLevel.getNamedAddresses(self.__internal_segment_addr__) def getProcedureCount(self): """Returns the number of procedures that has been defined in this segment.""" return HopperLowLevel.getProcedureCount(self.__internal_segment_addr__) def getProcedureAtIndex(self,index): """Returns the Nth Procedure object of the segment.""" return Procedure(self.__internal_segment_addr__,index) def getProcedureIndexAtAddress(self,address): """Returns the index of the procedure at a given address of the segment, or -1 if there is no procedure defined there.""" return HopperLowLevel.getProcedureIndexAtAddress(self.__internal_segment_addr__,address) def getProcedureAtAddress(self,address): """Returns the Procedure object at a given address of the segment, or None if there is no procedure defined at there.""" index=self.getProcedureIndexAtAddress(address) if index == -1: return None else: return self.getProcedureAtIndex(index) def getInstructionStart(self,address): """Returns the lowest address value of the instruction found at a particular address. If the given address""" """is in the middle of an instruction, Hopper will look back to find the first byte of this instruction.""" return HopperLowLevel.nearestBlock(self.__internal_segment_addr__,address) def getObjectLength(self,address): """Returns the length in bytes of the object at this address. The object can be an instruction, a data, etc.""" return HopperLowLevel.objectLength(self.__internal_segment_addr__,address) def isPartOfAnArray(self,address): """Returns True if the address is part of an array.""" return HopperLowLevel.partOfAnArray(self.__internal_segment_addr__,address) def getArrayStartAddress(self,address): """Returns the array start address, or BAD_ADDRESS if not inside an array.""" return HopperLowLevel.arrayStartAddress(self.__internal_segment_addr__,address) def getArrayElementCount(self,address): """Returns the number of element in the array, or 0 if not inside an array.""" return HopperLowLevel.arrayElementCount(self.__internal_segment_addr__,address) def getArrayElementAddress(self,address,index): """Returns the address of the element at the given index, or BAD_ADDRESS if not inside an array.""" return HopperLowLevel.arrayElementAddress(self.__internal_segment_addr__,address,index) def getArrayElementSize(self,address): """Returns the size in bytes of a single element of the array, or 0 if not inside an array.""" return HopperLowLevel.arrayElementSize(self.__internal_segment_addr__,address) def getStringCount(self): """Returns the number of strings in the segment.""" return HopperLowLevel.getStringCount(self.__internal_segment_addr__) def getStringAtIndex(self,index): """Return the nth string of the segment.""" return HopperLowLevel.getStringAtIndex(self.__internal_segment_addr__,index) def getStringAddressAtIndex(self,index): """Return the address of the nth string of the segment.""" return HopperLowLevel.getStringAddressAtIndex(self.__internal_segment_addr__,index) def getStringsList(self): """Returns a list containing all the strings and there address as a tuple.""" return [(self.getStringAddressAtIndex(x), self.getStringAtIndex(x)) for x in range(self.getStringCount())] class Document: """This class represents the disassembled document. A document is a set of segments.""" FORMAT_DEFAULT = 0 FORMAT_HEXADECIMAL = 1 FORMAT_DECIMAL = 2 FORMAT_OCTAL = 3 FORMAT_CHARACTER = 4 FORMAT_STACKVARIABLE = 5 FORMAT_OFFSET = 6 FORMAT_ADDRESS = 7 FORMAT_FLOAT = 8 FORMAT_BINARY = 9 FORMAT_STRUCTURED = 10 FORMAT_ENUM = 11 FORMAT_ADDRESS_DIFF=12 FORMAT_NEGATE = 0x20 FORMAT_LEADINGZEROES = 0x40 FORMAT_SIGNED = 0x80 def __init__(self,addr): self.__internal_document_addr__ = addr def __eq__(self,other): return other.__class__ == self.__class__ and self.__internal_document_addr__ == other.__internal_document_addr__ def __ne__(self,other): return other.__class__ != self.__class__ or self.__internal_document_addr__ != other.__internal_document_addr__ @staticmethod def newDocument(): """Creates and returns a new empty document.""" return Document(HopperLowLevel.newDocument()) @staticmethod def getCurrentDocument(): """Returns the current document.""" return Document(HopperLowLevel.currentDocument()) @staticmethod def getAllDocuments(): """Returns a list of all currently opened documents.""" return [Document(d) for d in HopperLowLevel.allDocuments()] @staticmethod def ask(msg): """Open a window containing a text field, and wait for the user to give a string value. Returns the string, or returns None if the Cancel button is hit.""" return HopperLowLevel.ask(msg) @staticmethod def askFile(msg,path,save): """Open a file dialog with a specified title, in order to select a file. The 'save' parameter allows you to choose between on 'open' or a 'save' dialog.""" return HopperLowLevel.askFile(msg, path if path != None else "", 1 if save else 0) @staticmethod def askDirectory(msg,path): """Open a file dialog with a specified title, in order to select a directory.""" return HopperLowLevel.askDirectory(msg, path if path != None else "") @staticmethod def message(msg,buttons): """Open a window containing a text field and a set of buttons. The 'Buttons' parameter is a list of strings. The function returns the index of the clicked button.""" if not isinstance(buttons,list): raise TypeError return HopperLowLevel.message(msg,list(map(lambda x: x.encode("utf-8"), buttons))) def closeDocument(self): """Close the document.""" HopperLowLevel.closeDocument(self.__internal_document_addr__) def loadDocumentAt(self,path): """Load a document at a given path.""" HopperLowLevel.loadDocumentAt(self.__internal_document_addr__,path) def saveDocument(self): """Save the document.""" HopperLowLevel.saveDocument(self.__internal_document_addr__) def saveDocumentAt(self,path): """Save the document at a given path.""" HopperLowLevel.saveDocumentAt(self.__internal_document_addr__,path) def getDocumentName(self): """Returns the document display name.""" return HopperLowLevel.documentName(self.__internal_document_addr__) def setDocumentName(self,name): """Set the document display name.""" HopperLowLevel.setDocumentName(self.__internal_document_addr__,name) def backgroundProcessActive(self): """Returns True if the background analysis is still running.""" return HopperLowLevel.backgroundProcessActive(self.__internal_document_addr__) def requestBackgroundProcessStop(self): """Request the background analysis to stop as soon as possible, and wait for its termination.""" HopperLowLevel.requestBackgroundProcessStop(self.__internal_document_addr__) def waitForBackgroundProcessToEnd(self): """Wait until the background analysis is ended.""" HopperLowLevel.waitForBackgroundProcessToEnd(self.__internal_document_addr__) def assemble(self,instr,address,syntax): """Assemble an instruction, and returns the bytes as an array. The instruction is NOT injected in the document: the address given to the function is used to encode the instruction. You wan use the writeByte method to inject an assembled instruction.""" """The first argument is the instruction to be assembled. The second is the address of the instruction. The last parameter is the syntax variant index. For the Intel processor, syntax = 0 for Intel syntax, and 1 for AT&amp;T syntax.""" return HopperLowLevel.assemble(self.__internal_document_addr__,instr,address,syntax) def getDatabaseFilePath(self): """Returns the path of the current Hopper database for this document (the HOP file).""" return HopperLowLevel.getDatabaseFilePath(self.__internal_document_addr__) def getExecutableFilePath(self): """Returns the path of the executable being analyzed.""" return HopperLowLevel.getExecutableFilePath(self.__internal_document_addr__) def setExecutableFilePath(self,path): """Set the path of the executable being analyzed.""" return HopperLowLevel.setExecutableFilePath(self.__internal_document_addr__,path) def log(self,msg): """Display a string message into the log window of the document.""" HopperLowLevel.log(self.__internal_document_addr__,msg) def rebase(self,new_base_address): """Change the file base address.""" HopperLowLevel.rebase(self.__internal_document_addr__,new_base_address) def newSegment(self,start_address,length): """Create a new segment of 'length' bytes starting at 'start_address'.""" HopperLowLevel.newSegment(self.__internal_document_addr__,start_address,length) return self.getSegmentAtAddress(start_address) def deleteSegment(self,seg_index): """Delete the segment at a given index. Return True if succeeded.""" return HopperLowLevel.deleteSegment(self.__internal_document_addr__,seg_index) def renameSegment(self,seg_index,name): """Rename the segment at a given index. Return True if succeeded.""" return HopperLowLevel.renameSegment(self.__internal_document_addr__,seg_index,name) def getSegmentCount(self): """Returns the number of segment the document contains.""" return HopperLowLevel.getSegmentCount(self.__internal_document_addr__); def getSegment(self,index): """Returns a segment by index. The returned object is an instance of the Segment class. If the index of not in the range [0;count[, the function returns None.""" addr = HopperLowLevel.getSegmentAddress(self.__internal_document_addr__,index) if addr == 0: return None return Segment(addr) def getSegmentByName(self,name): """Returns a segment by name. Return None if no segment with this name was found. If multiple segments have this name, the first one is returned.""" addr = HopperLowLevel.getSegmentAddressByName(self.__internal_document_addr__,name) if addr == 0: return None return Segment(addr) def getSectionByName(self,name): """Returns a section by name. Return None if no segment with this name was found. If multiple sections have this name, the first one is returned.""" addr = HopperLowLevel.getSectionAddressByName(self.__internal_document_addr__,name) if addr == 0: return None return Section(addr) def getSegmentsList(self): """Returns a list containing all the segments.""" return [self.getSegment(x) for x in range(self.getSegmentCount())] def getSegmentIndexAtAddress(self,addr): """Returns the segment index for a particular address.""" return HopperLowLevel.getSegmentIndexAtAddress(self.__internal_document_addr__,addr) def getSegmentAtAddress(self,addr): """Returns the segment for a particular address.""" idx=self.getSegmentIndexAtAddress(addr) if idx == -1: return None return self.getSegment(idx) def getSectionAtAddress(self,addr): """Returns the section for a particular address.""" seg=self.getSegmentAtAddress(addr) if seg == None: return None return seg.getSectionAtAddress(addr) def getCurrentSegmentIndex(self): """Returns the segment index where the cursor is. Returns -1 if the current segment cannot be located.""" return HopperLowLevel.getCurrentSegmentIndex(self.__internal_document_addr__) def getCurrentSegment(self): """Returns the segment where the cursor is. Returns None if the current segment cannot be located.""" index = HopperLowLevel.getCurrentSegmentIndex(self.__internal_document_addr__) if index == -1: return None else: return self.getSegment(index) def getCurrentSection(self): """Returns the section where the cursor is. Returns None if the current section cannot be located.""" return self.getSectionAtAddress(self.getCurrentAddress()) def getCurrentProcedure(self): """Returns the Procedure object where the cursor is. Returns None if there is no procedure there.""" seg = self.getCurrentSegment() if seg == None: return None return seg.getProcedureAtAddress(self.getCurrentAddress()) def getCurrentAddress(self): """Returns the address where the cursor currently is.""" return HopperLowLevel.getCurrentAddress(self.__internal_document_addr__) def setCurrentAddress(self,addr): """Set the address where the cursor currently is.""" return HopperLowLevel.setCurrentAddress(self.__internal_document_addr__,addr) def getSelectionAddressRange(self): """Returns a list, containing two addresses. Those address represents the range of bytes covered by the selection.""" return HopperLowLevel.getSelectionAddressRange(self.__internal_document_addr__) def moveCursorAtAddress(self,addr): """Move the cursor at a given address.""" HopperLowLevel.moveCursorAtAddress(self.__internal_document_addr__,addr) def selectAddressRange(self,addrRange): """Select a range of byte. The awaited argument is a list containing exactly two address.""" HopperLowLevel.selectAddressRange(self.__internal_document_addr__,addrRange[0],addrRange[1]) def getFileOffsetFromAddress(self,addr): """Returns the file offset corresponding to the given address.""" return HopperLowLevel.getFileOffsetFromAddress(self.__internal_document_addr__, addr) def getAddressFromFileOffset(self,offset): """Returns the address corresponding to the given file offset.""" return HopperLowLevel.getAddressFromFileOffset(self.__internal_document_addr__, offset) def is64Bits(self): """Returns True if the disassembled document is interpreted as a 64 bits binary.""" return HopperLowLevel.is64Bits(self.__internal_document_addr__); def getEntryPoint(self): """Returns the entry point of the document.""" return HopperLowLevel.getEntryPoint(self.__internal_document_addr__) def moveCursorAtEntryPoint(self): """Move the cursor at the entry point.""" self.moveCursorAtAddress(self.getEntryPoint()) def getHighlightedWord(self): """Returns the word that is currently highlighted in the assembly view.""" return HopperLowLevel.getHighlightedWord(self.__internal_document_addr__) def setNameAtAddress(self,addr,name): """Set the label name at a given address.""" seg=self.getSegmentAtAddress(addr) if seg != None: return seg.setNameAtAddress(addr,name) return False def getNameAtAddress(self,addr): """Get the label name at a given address.""" seg=self.getSegmentAtAddress(addr) if seg != None: return seg.getNameAtAddress(addr) return None def getAddressForName(self,name): """Get the address associated to a given name.""" return HopperLowLevel.getAddressForName(self.__internal_document_addr__,name) def refreshView(self): """Force the assembly view to be refresh.""" HopperLowLevel.refreshView(self.__internal_document_addr__) def moveCursorOneLineDown(self): """Move the current line down, and remove the multiselection if needed. Returns True if cursor moved.""" return HopperLowLevel.moveCursorOneLineDown(self.__internal_document_addr__) def moveCursorOneLineUp(self): """Move the current line up, and remove the multiselection if needed. Returns True if cursor moved.""" return HopperLowLevel.moveCursorOneLineUp(self.__internal_document_addr__) def getRawSelectedLines(self): """Returns a list of strings corresponding to the current selection.""" return HopperLowLevel.getRawSelectedLines(self.__internal_document_addr__) def addTagAtAddress(self,tag,addr): """Add a tag at a particular address.""" HopperLowLevel.addTagAtAddress(self.__internal_document_addr__,tag.__tag_internal__,addr) def removeTagAtAddress(self,tag,addr): """Remove the tag at a particular address.""" HopperLowLevel.removeTagAtAddress(self.__internal_document_addr__,tag.__tag_internal__,addr) def hasTagAtAddress(self,tag,addr): """Returns True if the tag is present at this address.""" return HopperLowLevel.hasTagAtAddress(self.__internal_document_addr__,tag.__tag_internal__,addr) def getTagCountAtAddress(self,addr): """Returns the number of tags at a given address.""" return HopperLowLevel.getTagCountAtAddress(self.__internal_document_addr__,addr) def getTagAtAddressByIndex(self,addr,index): """Returns the Nth tag present at a given address.""" tag_ptr = HopperLowLevel.getTagAtAddressByIndex(self.__internal_document_addr__,addr,index) return Tag(tag_ptr) if tag_ptr != 0 else None def tagIteratorAtAddress(self,addr): """Iterates over all tags present at a given address.""" for index in range(self.getTagCountAtAddress(addr)): yield self.getTagAtAddressByIndex(addr,index) def getTagListAtAddress(self,addr): """Returns the list of all tags present at a given address""" return [tag for tag in self.tagIteratorAtAddress(addr)] def getTagCount(self): """Returns the total number of tags available.""" return HopperLowLevel.getTagCount(self.__internal_document_addr__) def getTagAtIndex(self,index): """Returns a Tag object, or None if the index does not exists.""" tag_ptr = HopperLowLevel.getTagPtrAtIndex(self.__internal_document_addr__,index) return Tag(tag_ptr) if tag_ptr != 0 else None def tagIterator(self): """Iterate over all the tags.""" for index in range(self.getTagCount()): yield self.getTagAtIndex(index) def getTagList(self): """Returns a list of all tags.""" return [tag for tag in self.tagIterator()] def buildTag(self,name): """Build a tag with a given name. If a tag with the same name already exists, it return the existing tag.""" tag_ptr = HopperLowLevel.buildTagPtrWithName(self.__internal_document_addr__,name) return Tag(tag_ptr) def getTagWithName(self,name): """Returns a Tag object if a tag with this name already exists, or None.""" tag_ptr = HopperLowLevel.getTagPtrWithName(self.__internal_document_addr__,name) return Tag(tag_ptr) if tag_ptr != 0 else None def destroyTag(self,tag): """Remove the tag from every location, and delete it.""" HopperLowLevel.destroyTag(self.__internal_document_addr__,tag.__tag_internal__) def hasColorAtAddress(self,addr): """Returns True if a color has been defined at the given address.""" return HopperLowLevel.hasColorAtAddress(self.__internal_document_addr__,addr) def setColorAtAddress(self,color,addr): """Sets the color at a given address. The color is a 32bits integer representing the hexadecimal color in the form #AARRGGBB.""" return HopperLowLevel.setColorAtAddress(self.__internal_document_addr__,color,addr) def getColorAtAddress(self,addr): """Returns the color at a given address. The color is a 32bits integer representing the hexadecimal color in the form #AARRGGBB.""" return HopperLowLevel.colorAtAddress(self.__internal_document_addr__,addr) def removeColorAtAddress(self,addr): """Remove the color at a given address.""" HopperLowLevel.removeColorAtAddress(self.__internal_document_addr__,addr) def readBytes(self,addr,length): """Read bytes from a mapped segment, and return a string. Returns False if no segments was found for this range.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.readBytes(addr,length) def readByte(self,addr): """Read a byte from a mapped segment. Returns False if no segments was found for this address.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.readByte(addr) def readUInt16LE(self,addr): """Read a 16 bits little endian integer from a mapped segment. Returns False if no segments was found for this address.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.readUInt16LE(addr) def readUInt32LE(self,addr): """Read a 32 bits little endian integer from a mapped segment. Returns False if no segments was found for this address.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.readUInt32LE(addr) def readUInt64LE(self,addr): """Read a 64 bits little endian integer from a mapped segment. Returns False if no segments was found for this address.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.readUInt64LE(addr) def readUInt16BE(self,addr): """Read a 16 bits big endian integer from a mapped segment. Returns False if no segments was found for this address.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.readUInt16BE(addr) def readUInt32BE(self,addr): """Read a 32 bits big endian integer from a mapped segment. Returns False if no segments was found for this address.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.readUInt32BE(addr) def readUInt64BE(self,addr): """Read a 64 bits bif endian integer from a mapped segment. Returns False if no segments was found for this address.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.readUInt64BE(addr) def writeBytes(self,addr,byteStr): """Write bytes to a mapped segment. Bytes are given as a string. Returns False if no segments was found for this range.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.writeBytes(addr,byteStr) def writeByte(self,addr,value): """Write a byte to a mapped segment. Returns True if succeeded.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.writeByte(addr,value) def writeUInt16LE(self,addr,value): """Write a 16 bits little endian integer to a mapped segment. Returns True if succeeded.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.writeUInt16LE(addr,value) def writeUInt32LE(self,addr,value): """Write a 32 bits little endian integer to a mapped segment. Returns True if succeeded.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.writeUInt32LE(addr,value) def writeUInt64LE(self,addr,value): """Write a 64 bits little endian integer to a mapped segment. Returns True if succeeded.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.writeUInt64LE(addr,value) def writeUInt16BE(self,addr,value): """Write a 16 bits big endian integer to a mapped segment. Returns True if succeeded.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.writeUInt16BE(addr,value) def writeUInt32BE(self,addr,value): """Write a 32 bits big endian integer to a mapped segment. Returns True if succeeded.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.writeUInt32BE(addr,value) def writeUInt64BE(self,addr,value): """Write a 64 bits big endian integer to a mapped segment. Returns True if succeeded.""" segment = self.getSegmentAtAddress(addr) if segment == None: return False return segment.writeUInt64BE(addr,value) def getOperandFormat(self,addr,index): """Returns the format requested by the user for a given intruction operand.""" return HopperLowLevel.getOperandFormat(self.__internal_document_addr__,addr,index) def getOperandFormatRelativeTo(self,addr,index): """Returns the address to which the format is relative. Usually used for FORMAT_ADDRESS_DIFF format.""" return HopperLowLevel.getOperandFormatRelativeTo(self.__internal_document_addr__,addr,index) def setOperandFormat(self,addr,index,fmt): """Set the format of a given intruction operand.""" return HopperLowLevel.setOperandFormat(self.__internal_document_addr__,addr,index,fmt) def setOperandRelativeFormat(self,addr,relto,index,fmt): """Set the relative format of a given intruction operand. This version allows one to provide an address""" """used for the relative formats, like FORMAT_ADDRESS_DIFF.""" return HopperLowLevel.setOperandRelativeFormat(self.__internal_document_addr__,addr,relto,index,fmt) def getInstructionStart(self,address): """Returns the lowest address value of the instruction found at a particular address. If the given address""" """is in the middle of an instruction, Hopper will look back to find the first byte of this instruction.""" segment = self.getSegmentAtAddress(address) if segment == None: return BAD_ADDRESS return segment.getInstructionStart(address) def getObjectLength(self,address): """Returns the length in bytes of the object at this address. The object can be an instruction, a data, etc.""" segment = self.getSegmentAtAddress(address) if segment == None: return BAD_ADDRESS return segment.getObjectLength(address) def generateObjectiveCHeader(self): """Returns a bytearray object containing the generated Objective-C header extracted from the file's metadata.""" return HopperLowLevel.generateObjectiveCHeader(self.__internal_document_addr__) def produceNewExecutable(self,remove_sig=False): """Produces a new executable including all the modifications.""" """The optional argument is a boolean indicating if the signature is to be removed in the case of a Mach-O file.""" """Returns a string containing the produced executable.""" return HopperLowLevel.produceNewExecutable(self.__internal_document_addr__,1 if remove_sig else 0) def setBookmarkAtAddress(self,address,name=None): """Set a bookmark at a given address, with an optional name.""" return HopperLowLevel.setBookmark(self.__internal_document_addr__,address,name) def removeBookmarkAtAddress(self,address): """Removes a bookmark at a given address.""" return HopperLowLevel.removeBookmark(self.__internal_document_addr__,address) def hasBookmarkAtAddress(self,address): """Returns True if a bookmark is present at a given address.""" return HopperLowLevel.hasBookmark(self.__internal_document_addr__,address) def renameBookmarkAtAddress(self,address,name): """Changes the name of an existing bookmark.""" return HopperLowLevel.renameBookmark(self.__internal_document_addr__,address,name) def findBookmarkWithName(self,name): """Returns a list of all bookmarks of a given name.""" return HopperLowLevel.findBookmark(self.__internal_document_addr__,name) def getBookmarkName(self,address): """Returns the name of a bookmark at a given address.""" return HopperLowLevel.bookmarkName(self.__internal_document_addr__,address) def getBookmarks(self): """Returns a list of all the addresses with a bookmark.""" return HopperLowLevel.bookmarks(self.__internal_document_addr__) class GlobalInformation: """An object containing various information about the current version of Hopper.""" @staticmethod def getHopperMajorVersion(): """Returns the major version number. If Hopper is at version 4.1.2, it'll return "4".""" return HopperLowLevel.getMajorVersion() @staticmethod def getHopperMinorVersion(): """Returns the minor version number. If Hopper is at version 4.1.2, it'll return "1".""" return HopperLowLevel.getMinorVersion() @staticmethod def getHopperRevisionNumber(): """Returns the revision version number. If Hopper is at version 4.1.2, it'll return "2".""" return HopperLowLevel.getRevision() @staticmethod def getHopperVersion(): """Returns a string with the complete Hopper's version number (ie, something like "4.1.2")""" return "%d.%d.%d" % (GlobalInformation.getHopperMajorVersion(), GlobalInformation.getHopperMinorVersion(), GlobalInformation.getHopperRevisionNumber()) sys.stdin = open(os.devnull, "r") sys.stdout = HopperStdRedirection(0) sys.stderr = HopperStdRedirection(1)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dflatline/HopperPyMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server