Skip to content

Analysis Workflows

These workflows assume you have MCGhidra installed and configured as described in the Installation guide.

The fastest way to get oriented in an unknown binary. Start a container, wait for Ghidra to finish analysis, then survey the surface area.

docker_auto_start(binary_path="/path/to/target.exe")

This returns immediately with a port number. It does not block while Ghidra runs.

Poll until the HTTP API responds:

docker_health(port=8195)

For a small binary (under 1 MB), expect about 20 seconds. Larger binaries can take several minutes. Check docker_logs(port=8195) while waiting to see Ghidra’s progress.

instances_use(port=8195)

After this, every tool call defaults to this instance. No need to pass port again.

program_info()
functions_list(page_size=100)
data_list_strings(page_size=100)

program_info returns architecture, compiler, and image base address. The function and string listings give a first sense of scale and naming conventions.

Use server-side grep to find functions and strings related to security-sensitive behavior:

functions_list(grep="password|key|auth|crypt|login|verify", page_size=100)
data_list_strings(grep="password|secret|key|token|credential", page_size=100)

From here, decompile anything that looks relevant and follow cross-references to understand the surrounding logic.


Ghidra auto-analysis produces generic names like FUN_00401234. As you reverse engineer, renaming functions and adding comments makes the decompiled output progressively easier to read.

functions_decompile(address="00401234")

Read the pseudocode. Look at string references, called functions, and parameter usage to determine the function’s purpose.

functions_rename(address="00401234", new_name="validate_user_credentials")

If you can determine the parameter types and return type:

functions_set_signature(
address="00401234",
signature="int validate_user_credentials(char *username, char *password)"
)
functions_set_comment(
address="00401234",
comment="Checks username/password against the SQLite user table. Returns 1 on success."
)
functions_decompile(address="00401234")

The decompiled output now uses your names, types, and annotations. Functions called from validate_user_credentials also reflect the updated name wherever they reference it. Repeat this loop for each function you investigate.


Raw firmware (bootloaders, embedded system images, bare-metal code) requires extra setup because there is no ELF/PE header for Ghidra to parse.

Specify the processor language and base address:

docker_auto_start(
binary_path="/path/to/firmware.bin",
language="ARM:LE:32:v4t",
base_address="0x00000000"
)

When language is set, MCGhidra uses BinaryLoader to map the raw bytes at the given address. See the Installation guide for a table of common language IDs.

ARM firmware typically starts with an exception vector table at address 0x00000000. The first entry is the initial stack pointer, and the second is the reset vector (entry point):

memory_read(address="0x00000000", length=32, format="hex")
functions_decompile(address="0x00000004")

Embedded firmware talks to hardware through memory-mapped I/O. Look for reads and writes to addresses outside the firmware’s code and data regions:

data_list_strings(grep="UART|SPI|I2C|GPIO")
functions_list(grep="init_periph|hw_init|bsp_")

Constants like 0x40000000, 0x48000000, or 0xE000E000 (ARM Cortex-M NVIC) are strong indicators of peripheral access.

Interrupt vector tables are typically at fixed offsets. For Cortex-M, the vector table starts at the base address. Each 4-byte entry points to a handler:

memory_read(address="0x00000000", length=256, format="hex")

Create functions at each non-null vector address:

functions_create(address="0x00000040")
functions_decompile(address="0x00000040")

Firmware that communicates over a bus (UART, SPI, USB, CAN) will have recognizable patterns: ring buffers, state machines with packet parsing, and checksum calculations. Use call graph analysis to trace from peripheral init functions to protocol handlers:

analysis_get_callgraph(name="uart_init", max_depth=4)

MCGhidra includes 13 built-in prompts that guide Claude through structured analysis workflows. Each prompt defines a series of steps, tool calls, and checks for a specific reverse engineering task.

In Claude Code or Claude Desktop, use the /prompt command:

/prompt malware_triage

Claude will then execute a multi-step analysis: listing functions, scanning strings, checking imports, and producing a structured report. Prompts that involve scanning (like malware_triage or identify_crypto) report progress as they work through each step.

PromptWhat it does
malware_triageQuick capability assessment across 21 scanning steps: checks for network activity, file manipulation, process injection, anti-analysis tricks, and persistence mechanisms
identify_cryptoScans for known crypto constants (AES S-boxes, SHA magic numbers), function names matching crypto libraries, and common key schedule patterns
find_authenticationSearches for password checks, credential storage, license validation, certificate handling, and authentication bypass patterns
analyze_protocolFramework for reversing network or file format protocols: identifies packet structures, state machines, serialization routines
trace_data_flowFollows data forward or backward through a program to map how input reaches sensitive operations
find_main_logicNavigates past CRT startup, compiler-generated wrappers, and initialization to find the actual application entry point
analyze_importsCategorizes imported functions by capability (file I/O, networking, crypto, process management) and flags suspicious combinations
analyze_stringsGroups strings by category (URLs, file paths, error messages, format strings) and cross-references them to find their usage
analyze_switch_tableIdentifies jump tables and command dispatchers, maps case values to handler functions
find_config_parsingLocates configuration file readers, command-line parsers, registry access, and environment variable lookups
compare_functionsSide-by-side comparison of two functions to identify patches, variants, or shared library code
document_structTraces struct usage across the binary to document field types, offsets, sizes, and purpose
find_error_handlersMaps error handling paths, cleanup routines, exception handlers, and exit patterns

Triage an unknown binary for malicious capabilities:

/prompt malware_triage

Find all cryptographic implementations:

/prompt identify_crypto

Trace how user input flows to a specific sink:

/prompt trace_data_flow

Each prompt orchestrates a series of MCP tool calls. For example, malware_triage will:

  1. Call program_info() to determine the architecture and format
  2. Call functions_list(grep=...) repeatedly with patterns for each capability category (networking, file ops, process injection, etc.)
  3. Call data_list_strings(grep=...) to find suspicious string patterns
  4. Call symbols_imports(grep=...) to categorize imported APIs
  5. Produce a summary with findings organized by risk category

Prompts that scan many patterns report numeric progress (e.g., “Step 12/21: Checking for anti-debug techniques”) so you can see where they are in the analysis.