Version target: Hopper 6.2.1 Scope: Public Python scripting API Audience: Reverse engineers who know binary analysis but are new to Hopper scripting
This guide explains how to script Hopper 6.2.1 with Python, using the public API surface reflected in this repository. It is intentionally practical first and reference-oriented second.
If you only need working examples, start with these repository scripts:
hopper_export_metadata.pyhopper_export_callgraph.pyhopper_export_rust_analysis.pyhopper_apply_annotations.py_hopper_utils.py
Hopper organizes analysis around a Document. A document contains Segment objects, and each segment contains Section objects and typed bytes. Procedures live inside segments and are made of BasicBlock objects. Instructions, strings, labels, tags, comments, colors, and bookmarks are all attached to addresses inside the document.
The most important consequence is this:
- start from
Document.getCurrentDocument() - use document methods when you want a global view
- use segment methods when you want address-local reads, writes, types, strings, or xrefs
- use procedure and basic-block methods when you want control-flow structure
Inside Hopper, scripts normally import directly from hopper:
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
print("Loaded:", document.getDocumentName())If you want the same script to fail cleanly outside Hopper, guard the import:
try:
from hopper import Document
except ModuleNotFoundError:
Document = None
def main() -> int:
if Document is None:
print("Hopper Python API is unavailable. Run this script inside Hopper.")
return 1
...The Document class is your entrypoint for almost everything:
- retrieve the current file and database paths
- inspect segments and procedures
- read and write bytes across mapped segments
- navigate the current cursor and selection
- create tags, colors, comments, and bookmarks
Useful starting calls:
Document.getCurrentDocument()document.getDocumentName()document.getExecutableFilePath()document.getDatabaseFilePath()document.getEntryPoint()document.is64Bits()
Before exporting or mining analysis, wait for background analysis to finish:
document = Document.getCurrentDocument()
if document and document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()That pattern matters. Exporting too early can silently miss procedures, labels, strings, or cross-references.
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
for segment in document.getSegmentsList():
print(
segment.getName(),
hex(segment.getStartingAddress()),
segment.getLength(),
)
for section in segment.getSectionsList():
print(
" ",
section.getName(),
hex(section.getStartingAddress()),
section.getLength(),
)Use this when you want:
- memory layout
- file offset mapping
- section-by-section export logic
Important segment helpers:
getFileOffset()getFileOffsetForAddress(addr)getSectionAtAddress(addr)getSectionIndexAtAddress(addr)
Prefer segment-based procedure enumeration. It stays on the documented public API:
def iter_procedures(document):
for segment in document.getSegmentsList():
count = segment.getProcedureCount()
for index in range(count):
procedure = segment.getProcedureAtIndex(index)
if procedure is not None:
yield procedure
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
for procedure in iter_procedures(document):
entry = procedure.getEntryPoint()
name = document.getNameAtAddress(entry) or hex(entry)
print(name, procedure.signatureString())This is better than relying on undocumented shortcuts from old scripts.
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
entry = document.getEntryPoint()
segment = document.getSegmentAtAddress(entry)
procedure = segment.getProcedureAtAddress(entry)
for index in range(procedure.getBasicBlockCount()):
block = procedure.getBasicBlock(index)
print("block", hex(block.getStartingAddress()), "->", hex(block.getEndingAddress()))
for succ_index in range(block.getSuccessorCount()):
succ = block.getSuccessorAddressAtIndex(succ_index)
print(" successor", hex(succ))Use this when you need:
- local control-flow graphs
- branch fan-out
- basic-block tagging
Procedure.getAllCallees() and Procedure.getAllCallers() return CallReference objects. Those objects preserve both the callsite and the referenced destination.
def iter_procedures(document):
for segment in document.getSegmentsList():
count = segment.getProcedureCount()
for index in range(count):
procedure = segment.getProcedureAtIndex(index)
if procedure is not None:
yield procedure
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
for procedure in iter_procedures(document):
caller_entry = procedure.getEntryPoint()
caller_name = document.getNameAtAddress(caller_entry) or hex(caller_entry)
for call in procedure.getAllCallees():
target = call.toAddress()
target_name = document.getNameAtAddress(target) or hex(target)
print(
caller_name,
"calls",
target_name,
"from",
hex(call.fromAddress()),
"type",
call.type(),
)Call reference types are documented as:
CALL_NONECALL_UNKNOWNCALL_DIRECTCALL_OBJC
The public API supports both annotation export and annotation write-back.
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
address = document.getEntryPoint()
segment = document.getSegmentAtAddress(address)
document.setNameAtAddress(address, "entry_main")
segment.setCommentAtAddress(address, "Recovered entry routine")
segment.setInlineCommentAtAddress(address, "Initial stack setup")
tag = document.buildTag("review")
document.addTagAtAddress(tag, address)
document.setColorAtAddress(0xFF112233, address)
document.setBookmarkAtAddress(address, "main entry")
document.refreshView()Use:
- prefix comments for longer address-level notes
- inline comments for a short remark on one line
- tags for machine-processable grouping
- colors for immediate visual triage
- bookmarks for analyst waypoints
If you already know the address is inside a specific segment, use the segment APIs. If you want Hopper to resolve the containing segment for you, use the document APIs.
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
segment = document.getCurrentSegment()
address = document.getCurrentAddress()
raw_byte = segment.readByte(address)
qword = document.readUInt64LE(address)
blob = document.readBytes(address, 16)
segment.writeByte(address, 0x90)
document.writeUInt32LE(address + 4, 0x12345678)Many read methods return False on failure. Check the result instead of assuming the access succeeded.
You can script binary cleanup and recovery by changing Hopper's byte types:
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
segment = document.getCurrentSegment()
address = document.getCurrentAddress()
segment.markAsUndefined(address)
segment.markAsCode(address)
segment.markAsProcedure(address)
segment.markAsDataByteArray(address, 32)
segment.makeAlignment(address, 16)Useful when dealing with:
- obfuscated binaries
- mixed code and data
- broken procedure boundaries
- ARM and Thumb mode issues
ARM helpers:
isThumbAtAddress(addr)setThumbModeAtAddress(addr)setARMModeAtAddress(addr)
Document.assemble(instr, address, syntax) lets you assemble bytes for an instruction before writing them back.
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
address = document.getCurrentAddress()
bytes_out = document.assemble("nop", address, 0)
if bytes_out:
document.writeBytes(address, bytes_out)
document.refreshView()For Intel syntax:
syntax = 0means Intelsyntax = 1means AT&T
After changing bytes, comments, labels, or analysis state, you can save the Hopper database and optionally produce a patched executable:
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
document.saveDocument()
output = document.produceNewExecutable(remove_sig=False)
print(output)For Mach-O targets, remove_sig=True can be useful when the original signature is no longer valid.
Write one procedure name per line to a file next to the binary.
from pathlib import Path
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
output_path = Path(document.getExecutableFilePath() + ".procedures.txt")
lines = []
for segment in document.getSegmentsList():
for index in range(segment.getProcedureCount()):
procedure = segment.getProcedureAtIndex(index)
if procedure is not None:
entry = procedure.getEntryPoint()
name = document.getNameAtAddress(entry) or hex(entry)
lines.append(f"{hex(entry)} {name}")
output_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
print(f"Exported {len(lines)} procedures to {output_path}")Collect all strings across segments and write them as CSV.
import csv
import io
from pathlib import Path
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
output_path = Path(document.getExecutableFilePath() + ".strings.csv")
buf = io.StringIO()
writer = csv.writer(buf)
writer.writerow(["segment", "address", "value"])
count = 0
for segment in document.getSegmentsList():
seg_name = segment.getName()
for text, address in segment.getStringsList():
writer.writerow([seg_name, hex(address), text])
count += 1
output_path.write_text(buf.getvalue(), encoding="utf-8")
print(f"Exported {count} strings to {output_path}")Build a list of procedure records and write them as indented JSON.
import json
from pathlib import Path
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
output_path = Path(document.getExecutableFilePath() + ".proc_meta.json")
records = []
for segment in document.getSegmentsList():
for index in range(segment.getProcedureCount()):
procedure = segment.getProcedureAtIndex(index)
if procedure is None:
continue
entry = procedure.getEntryPoint()
records.append({
"address": hex(entry),
"name": document.getNameAtAddress(entry) or "",
"signature": str(procedure.signatureString() or ""),
"basic_block_count": int(procedure.getBasicBlockCount()),
"segment": segment.getName(),
})
output_path.write_text(json.dumps(records, indent=2) + "\n", encoding="utf-8")
print(f"Exported {len(records)} procedures to {output_path}")Dump all callers and callees of the procedure at the current cursor position.
import json
from pathlib import Path
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
procedure = document.getCurrentProcedure()
if procedure is None:
print("No procedure at the current cursor position.")
raise SystemExit(1)
entry = procedure.getEntryPoint()
name = document.getNameAtAddress(entry) or hex(entry)
callers = []
for ref in procedure.getAllCallers():
callers.append({
"from": hex(ref.fromAddress()),
"to": hex(ref.toAddress()),
"type": int(ref.type()),
})
callees = []
for ref in procedure.getAllCallees():
callees.append({
"from": hex(ref.fromAddress()),
"to": hex(ref.toAddress()),
"type": int(ref.type()),
})
result = {
"procedure": name,
"address": hex(entry),
"callers": callers,
"callees": callees,
}
output_path = Path(document.getExecutableFilePath() + f".xrefs_{hex(entry)}.json")
output_path.write_text(json.dumps(result, indent=2) + "\n", encoding="utf-8")
print(f"Exported {len(callers)} callers and {len(callees)} callees to {output_path}")Capture the full segment and section layout of the binary.
import json
from pathlib import Path
document = Document.getCurrentDocument()
if document is None:
print("No active Hopper document.")
raise SystemExit(1)
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()
output_path = Path(document.getExecutableFilePath() + ".segments.json")
segments = []
for segment in document.getSegmentsList():
sections = []
for section_index in range(segment.getSectionCount()):
section = segment.getSection(section_index)
sections.append({
"name": section.getName(),
"start": hex(section.getStartingAddress()),
"length": int(section.getLength()),
"flags": int(section.getFlags()),
})
segments.append({
"name": segment.getName(),
"start": hex(segment.getStartingAddress()),
"length": int(segment.getLength()),
"file_offset": int(segment.getFileOffset()),
"procedure_count": int(segment.getProcedureCount()),
"string_count": int(segment.getStringCount()),
"sections": sections,
})
output_path.write_text(json.dumps(segments, indent=2) + "\n", encoding="utf-8")
print(f"Exported {len(segments)} segments to {output_path}")This section groups the public methods into the operations you typically care about.
Use this when you need to branch on Hopper version:
| Method | Purpose |
|---|---|
getHopperMajorVersion() |
Return the major version string |
getHopperMinorVersion() |
Return the minor version string |
getHopperRevisionNumber() |
Return the revision string |
getHopperVersion() |
Return the full version string |
| Method | Purpose |
|---|---|
newDocument() |
Create a new empty document |
getCurrentDocument() |
Get the active Hopper document |
getAllDocuments() |
List all open documents |
closeDocument() |
Close the current document |
loadDocumentAt(path) |
Load a document from disk |
saveDocument() |
Save the current Hopper database |
saveDocumentAt(path) |
Save the current Hopper database to a specific path |
| Method | Purpose |
|---|---|
getDocumentName() |
Display name of the document |
setDocumentName(name) |
Change the display name |
getDatabaseFilePath() |
Path to the Hopper database (.hop) |
getExecutableFilePath() |
Path to the executable under analysis |
setExecutableFilePath(path) |
Change the executable path |
is64Bits() |
Whether the document is treated as 64-bit |
getEntryPoint() |
Get the document entry point |
moveCursorAtEntryPoint() |
Move the cursor to the entry point |
| Method | Purpose |
|---|---|
backgroundProcessActive() |
Whether Hopper analysis is still running |
requestBackgroundProcessStop() |
Ask the background analysis to stop and wait |
waitForBackgroundProcessToEnd() |
Block until analysis finishes |
log(msg) |
Write a line to Hopper's log window |
| Method | Purpose |
|---|---|
ask(msg) |
Prompt for a string |
askFile(msg, path, save) |
Open a file chooser |
askDirectory(msg, path) |
Open a directory chooser |
message(msg, buttons) |
Show a dialog and return the clicked button index |
| Method | Purpose |
|---|---|
getCurrentSegmentIndex() |
Segment index at the cursor |
getCurrentSegment() |
Segment at the cursor |
getCurrentSection() |
Section at the cursor |
getCurrentProcedure() |
Procedure at the cursor |
getCurrentAddress() |
Current cursor address |
setCurrentAddress(addr) |
Set the current address |
moveCursorAtAddress(addr) |
Move the UI cursor |
moveCursorOneLineDown() |
Move one assembly line down |
moveCursorOneLineUp() |
Move one assembly line up |
getSelectionAddressRange() |
Get the selected address range |
selectAddressRange(addrRange) |
Select a byte range |
getHighlightedWord() |
Get the currently highlighted word |
getRawSelectedLines() |
Get the selected text lines |
refreshView() |
Force the assembly view to refresh |
| Method | Purpose |
|---|---|
getSegmentCount() |
Number of segments |
getSegment(index) |
Get a segment by index |
getSegmentByName(name) |
Get the first matching segment by name |
getSegmentsList() |
List all segments |
getSegmentIndexAtAddress(addr) |
Segment index for an address |
getSegmentAtAddress(addr) |
Segment for an address |
getSectionByName(name) |
Get the first matching section by name |
getSectionAtAddress(addr) |
Section for an address |
newSegment(start_address, length) |
Create a new segment |
deleteSegment(seg_index) |
Delete a segment |
renameSegment(seg_index, name) |
Rename a segment |
rebase(new_base_address) |
Rebase the loaded image |
getFileOffsetFromAddress(addr) |
Convert address to file offset |
getAddressFromFileOffset(offset) |
Convert file offset to address |
| Method | Purpose |
|---|---|
setNameAtAddress(addr, name) |
Set a label name |
getNameAtAddress(addr) |
Get a label name |
getAddressForName(name) |
Resolve a name to an address |
getOperandFormat(addr, index) |
Get user-selected operand format |
getOperandFormatRelativeTo(addr, index) |
Get the relative base for operand formatting |
setOperandFormat(addr, index, fmt) |
Set operand format |
setOperandRelativeFormat(addr, relto, index, fmt) |
Set relative operand format |
| Method | Purpose |
|---|---|
getTagCount() |
Number of tags defined in the document |
getTagAtIndex(index) |
Get a tag by index |
tagIterator() |
Iterate tags |
getTagList() |
List tags |
buildTag(name) |
Create or reuse a named tag |
getTagWithName(name) |
Look up a tag by name |
destroyTag(tag) |
Remove a tag everywhere and delete it |
addTagAtAddress(tag, addr) |
Add a tag to an address |
removeTagAtAddress(tag, addr) |
Remove a tag from an address |
hasTagAtAddress(tag, addr) |
Test whether a tag exists at an address |
getTagCountAtAddress(addr) |
Tag count at an address |
getTagAtAddressByIndex(addr, index) |
Get a tag from an address by index |
tagIteratorAtAddress(addr) |
Iterate tags at an address |
getTagListAtAddress(addr) |
List tags at an address |
hasColorAtAddress(addr) |
Whether an address has a color |
setColorAtAddress(color, addr) |
Set a color |
getColorAtAddress(addr) |
Read a color |
removeColorAtAddress(addr) |
Remove a color |
setBookmarkAtAddress(address, name=None) |
Add a bookmark |
removeBookmarkAtAddress(address) |
Remove a bookmark |
hasBookmarkAtAddress(address) |
Test for bookmark presence |
renameBookmarkAtAddress(address, name) |
Rename a bookmark |
findBookmarkWithName(name) |
Find bookmarks by name |
getBookmarkName(address) |
Get a bookmark name |
getBookmarks() |
List bookmarked addresses |
| Method | Purpose |
|---|---|
readBytes(addr, length) |
Read bytes across mapped segments |
readByte(addr) |
Read one byte |
readUInt16LE(addr) |
Read 16-bit little-endian |
readUInt32LE(addr) |
Read 32-bit little-endian |
readUInt64LE(addr) |
Read 64-bit little-endian |
readUInt16BE(addr) |
Read 16-bit big-endian |
readUInt32BE(addr) |
Read 32-bit big-endian |
readUInt64BE(addr) |
Read 64-bit big-endian |
writeBytes(addr, byteStr) |
Write bytes across mapped segments |
writeByte(addr, value) |
Write one byte |
writeUInt16LE(addr, value) |
Write 16-bit little-endian |
writeUInt32LE(addr, value) |
Write 32-bit little-endian |
writeUInt64LE(addr, value) |
Write 64-bit little-endian |
writeUInt16BE(addr, value) |
Write 16-bit big-endian |
writeUInt32BE(addr, value) |
Write 32-bit big-endian |
writeUInt64BE(addr, value) |
Write 64-bit big-endian |
| Method | Purpose |
|---|---|
assemble(instr, address, syntax) |
Assemble an instruction to bytes |
getInstructionStart(address) |
Find the beginning of the instruction at an address |
getObjectLength(address) |
Get the byte length of the object at an address |
generateObjectiveCHeader() |
Extract generated Objective-C headers from metadata |
produceNewExecutable(remove_sig=False) |
Build a modified executable |
Segment is the most important low-level analysis surface in Hopper.
The public API documents these byte types:
TYPE_UNDEFINEDTYPE_OUTSIDETYPE_NEXTTYPE_INT8TYPE_INT16TYPE_INT32TYPE_INT64TYPE_ASCIITYPE_ALIGNTYPE_UNICODETYPE_CODETYPE_PROCEDUREBAD_ADDRESS
Use Segment.stringForType(t) to convert a type constant into a string.
| Method | Purpose |
|---|---|
getName() |
Segment name |
getStartingAddress() |
Segment start address |
getLength() |
Segment length in bytes |
getFileOffset() |
File offset of the segment start |
getFileOffsetForAddress(addr) |
File offset for a specific address |
getSectionCount() |
Number of sections |
getSection(index) |
Get a section by index |
getSectionsList() |
List all sections |
getSectionIndexAtAddress(addr) |
Section index for an address |
getSectionAtAddress(addr) |
Section for an address |
| Method | Purpose |
|---|---|
readBytes(addr, length) |
Read a byte range |
readByte(addr) |
Read one byte |
readUInt16LE(addr) |
Read 16-bit little-endian |
readUInt32LE(addr) |
Read 32-bit little-endian |
readUInt64LE(addr) |
Read 64-bit little-endian |
readUInt16BE(addr) |
Read 16-bit big-endian |
readUInt32BE(addr) |
Read 32-bit big-endian |
readUInt64BE(addr) |
Read 64-bit big-endian |
writeBytes(addr, bytesStr) |
Write a byte range |
writeByte(addr, value) |
Write one byte |
writeUInt16LE(addr, value) |
Write 16-bit little-endian |
writeUInt32LE(addr, value) |
Write 32-bit little-endian |
writeUInt64LE(addr, value) |
Write 64-bit little-endian |
writeUInt16BE(addr, value) |
Write 16-bit big-endian |
writeUInt32BE(addr, value) |
Write 32-bit big-endian |
writeUInt64BE(addr, value) |
Write 64-bit big-endian |
| Method | Purpose |
|---|---|
markAsUndefined(addr) |
Mark one address undefined |
markRangeAsUndefined(addr, length) |
Mark a range undefined |
markAsCode(addr) |
Mark an address as code |
markAsProcedure(addr) |
Mark an address as a procedure |
markAsDataByteArray(addr, count) |
Mark a byte array |
markAsDataShortArray(addr, count) |
Mark a short array |
markAsDataIntArray(addr, count) |
Mark an int array |
getTypeAtAddress(addr) |
Get the Hopper byte type |
setTypeAtAddress(addr, length, typeValue) |
Set a byte type over a range |
makeAlignment(addr, size) |
Create an alignment object |
getNextAddressWithType(addr, typeValue) |
Scan forward for a type |
disassembleWholeSegment() |
Ask Hopper to disassemble the entire segment |
| Method | Purpose |
|---|---|
isThumbAtAddress(addr) |
Whether an address is in Thumb mode |
setThumbModeAtAddress(addr) |
Force Thumb mode |
setARMModeAtAddress(addr) |
Force ARM mode |
| Method | Purpose |
|---|---|
setNameAtAddress(addr, name) |
Set a label |
getNameAtAddress(addr) |
Read a label |
getDemangledNameAtAddress(addr) |
Read a demangled label |
getCommentAtAddress(addr) |
Read a prefix comment |
setCommentAtAddress(addr, comment) |
Set a prefix comment |
getInlineCommentAtAddress(addr) |
Read an inline comment |
setInlineCommentAtAddress(addr, comment) |
Set an inline comment |
getInstructionAtAddress(addr) |
Fetch an instruction object |
getReferencesOfAddress(addr) |
Addresses that reference this address |
getReferencesFromAddress(addr) |
Addresses referenced from this address |
addReference(addr, referenced) |
Add a cross-reference |
removeReference(addr, referenced) |
Remove a cross-reference |
| Method | Purpose |
|---|---|
getLabelCount() |
Number of labels in the segment |
labelIterator() |
Iterate labels |
getLabelsList() |
List label names |
getNamedAddresses() |
List addresses that have labels |
getProcedureCount() |
Number of procedures in the segment |
getProcedureAtIndex(index) |
Get a procedure by index |
getProcedureIndexAtAddress(address) |
Get the procedure index at an address |
getProcedureAtAddress(address) |
Get the procedure at an address |
getInstructionStart(address) |
Find instruction start for an address |
getObjectLength(address) |
Byte length of the object at an address |
isPartOfAnArray(address) |
Whether an address is inside an array |
getArrayStartAddress(address) |
Array start address |
getArrayElementCount(address) |
Element count |
getArrayElementAddress(address, index) |
Element address |
getArrayElementSize(address) |
Element size |
getStringCount() |
Number of strings |
getStringAtIndex(index) |
Read one string by index |
getStringAddressAtIndex(index) |
Read one string address by index |
getStringsList() |
List strings and their addresses |
Section is intentionally small:
| Method | Purpose |
|---|---|
getName() |
Section name |
getStartingAddress() |
Section start address |
getLength() |
Section length in bytes |
getFlags() |
Section flags |
Use Instruction when you need architecture or operand-level details.
| Method | Purpose |
|---|---|
stringForArchitecture(t) |
Convert an architecture constant to a string |
getArchitecture() |
Get the instruction architecture |
getInstructionString() |
Mnemonic or instruction text |
getArgumentCount() |
Number of operands |
getRawArgument(index) |
Raw assembly operand |
getFormattedArgument(index) |
Hopper-formatted operand |
isAnInconditionalJump() |
Whether the instruction is an unconditional jump |
isAConditionalJump() |
Whether the instruction is a conditional jump |
getInstructionLength() |
Byte length of the instruction |
Documented architecture constants include:
ARCHITECTURE_UNKNOWNARCHITECTURE_i386ARCHITECTURE_X86_64ARCHITECTURE_ARMARCHITECTURE_ARM_THUMBARCHITECTURE_AARCH64
The public Procedure API is centered around entry points, basic blocks, locals, tags, callers, callees, local labels, decompilation, and register renaming.
| Method | Purpose |
|---|---|
getSegment() |
Segment containing the procedure |
getSection() |
Section containing the procedure |
getEntryPoint() |
Procedure entry address |
getBasicBlockCount() |
Number of basic blocks |
getBasicBlock(index) |
Get a block by index |
getBasicBlockAtAddress(addr) |
Find the block containing an instruction |
basicBlockIterator() |
Iterate all basic blocks |
getHeapSize() |
Procedure heap size in bytes |
getLocalVariableList() |
List of local variables |
| Method | Purpose |
|---|---|
addTag(tag) |
Add a tag to the procedure |
removeTag(tag) |
Remove a tag |
hasTag(tag) |
Check whether the tag is present |
getTagCount() |
Number of tags |
getTagAtIndex(index) |
Read one tag |
tagIterator() |
Iterate tags |
getTagList() |
List tags |
| Method | Purpose |
|---|---|
getAllCallers() |
Call references into the procedure |
getAllCallees() |
Call references out of the procedure |
getAllCallerProcedures() |
Caller procedures |
getAllCalleeProcedures() |
Procedures called by this procedure |
| Method | Purpose |
|---|---|
hasLocalLabelAtAddress(addr) |
Whether a local label exists |
localLabelAtAddress(addr) |
Read the local label |
setLocalLabelAtAddress(label, addr) |
Set a local label |
declareLocalLabelAt(addr) |
Create a local label and return its name |
removeLocalLabelAtAddress(addr) |
Remove a local label |
addressOfLocalLabel(label) |
Resolve a local label to an address |
| Method | Purpose |
|---|---|
decompile() |
Return pseudo-code or None |
signatureString() |
Return the C-like signature string |
| Method | Purpose |
|---|---|
renameRegister(reg_cls, reg_idx, name) |
Rename a register inside the procedure |
registerNameOverride(reg_cls, reg_idx) |
Read the override name |
clearRegisterNameOverride(reg_cls, reg_idx) |
Remove the override |
The API also publishes register class and register index constants for x86 and ARM, such as:
REGCLS_GENERAL_PURPOSE_REGISTERREGCLS_X86_SSEREGCLS_ARM_VFP_DOUBLEREGIDX_X86_RAXREGIDX_X86_RIP
Important warning: the Hopper docs note that modifying the document structure can invalidate a Python Procedure object. Do not keep long-lived cached procedure handles across structural edits like creating or deleting procedures.
| Method | Purpose |
|---|---|
getProcedure() |
Parent procedure |
getStartingAddress() |
Block start address |
getEndingAddress() |
Address after the last instruction |
getSuccessorCount() |
Number of successors |
getSuccessorIndexAtIndex(index) |
Successor block index |
getSuccessorAddressAtIndex(index) |
Successor block address |
addTag(tag) |
Add a tag |
removeTag(tag) |
Remove a tag |
hasTag(tag) |
Check a tag |
getTagCount() |
Number of tags |
getTagAtIndex(index) |
Read a tag |
tagIterator() |
Iterate tags |
getTagList() |
List tags |
CallReference tells you where a call originated and where it points.
| Method | Purpose |
|---|---|
type() |
Call type |
fromAddress() |
Source callsite |
toAddress() |
Referenced destination |
Documented call-type values:
CALL_NONECALL_UNKNOWNCALL_DIRECTCALL_OBJC
| Method | Purpose |
|---|---|
name() |
Local variable name |
displacement() |
Stack displacement |
| Method | Purpose |
|---|---|
getName() |
Tag name |
Always use:
if document.backgroundProcessActive():
document.waitForBackgroundProcessToEnd()before exporting procedures, strings, or xrefs.
If you find old examples using undocumented helpers such as a direct document-wide procedure list, avoid copying them. In this repository, the stable pattern is segment-based enumeration.
The public API consistently revolves around addresses. When you need a stable procedure identity, use procedure.getEntryPoint() plus document.getNameAtAddress(entry) instead of relying on undocumented convenience methods.
Different Hopper APIs use different failure sentinels:
Falsefor failed reads and writesNonefor missing objectsSegment.BAD_ADDRESSfor invalid address queries
Check them explicitly.
After setting names, comments, tags, colors, or bookmarks, call:
document.refreshView()so the assembly view reflects your changes immediately.
After rebasing, segment edits, large-scale retyping, or procedure recovery, do not assume previously held objects are still valid. Re-fetch the document, segment, or procedure handles you need.
These scripts are the best local examples of Hopper 6.2.1 scripting style in this repository:
| File | What it demonstrates |
|---|---|
scripts/hopper/_hopper_utils.py |
Analysis wait pattern, procedure iteration, address and tag helpers |
scripts/hopper/hopper_export_metadata.py |
Segment, section, string, procedure, local-variable, and basic-block export |
scripts/hopper/hopper_export_callgraph.py |
Call graph extraction through Procedure and CallReference |
scripts/hopper/hopper_export_rust_analysis.py |
Symbol-oriented export and demangling workflow |
scripts/hopper/hopper_apply_annotations.py |
Name, comment, tag, color, and bookmark write-back |
If you are new to Hopper scripting, start with this sequence:
- Get the current document.
- Wait for background analysis to finish.
- Enumerate segments.
- Enumerate procedures through segments.
- Use entry-point addresses as stable identifiers.
- Export or annotate.
- Refresh the UI after write-back.
That sequence matches the approach used by the maintained Hopper scripts in this repository.