Documentation Index
Fetch the complete documentation index at: https://mintlify.com/NationalSecurityAgency/ghidra/llms.txt
Use this file to discover all available pages before exploring further.
Introduction
The Ghidra Headless Analyzer (analyzeHeadless) enables batch processing of binaries without launching the GUI. This is essential for:
- Automated analysis pipelines
- CI/CD integration
- Large-scale binary processing
- Server-side analysis
- Scripted workflows
Basic Usage
Command Syntax
analyzeHeadless <project_path> <project_name> [options]
Simple Analysis
Analyze a single binary:
analyzeHeadless /tmp/projects MyProject \
-import /path/to/binary.exe \
-postScript ExportFunctionInfo.java output.json
Batch Processing
Process multiple binaries:
analyzeHeadless /tmp/projects BatchAnalysis \
-import /samples \
-recursive
Command-Line Options
Project Options
Create or use existing project:
# Project is created if it doesn't exist
analyzeHeadless /projects MyProject -import binary.exe
Delete project after processing:
analyzeHeadless /projects TempProject \
-import binary.exe \
-deleteProject
Read-only mode:
analyzeHeadless /projects ExistingProject \
-process binary.exe \
-readOnly
Import Options
Import file or directory:
# Import single file
-import /path/to/binary.exe
# Import directory
-import /path/to/binaries/
# Import recursively
-import /path/to/binaries/ -recursive
Overwrite existing:
analyzeHeadless /projects MyProject \
-import binary.exe \
-overwrite
Specify processor/language:
analyzeHeadless /projects MyProject \
-import firmware.bin \
-processor x86:LE:64:default \
-cspec gcc
Specify loader:
analyzeHeadless /projects MyProject \
-import data.bin \
-loader BinaryLoader
Analysis Options
Disable analysis:
analyzeHeadless /projects MyProject \
-import binary.exe \
-noanalysis
Analysis timeout:
# Timeout per file (in seconds)
analyzeHeadless /projects MyProject \
-import binary.exe \
-analysisTimeoutPerFile 300
CPU core limit:
analyzeHeadless /projects MyProject \
-import /samples/ \
-recursive \
-max-cpu 4
Script Options
Pre-script (runs before analysis):
analyzeHeadless /projects MyProject \
-import binary.exe \
-preScript SetupEnvironment.java
Post-script (runs after analysis):
analyzeHeadless /projects MyProject \
-import binary.exe \
-postScript ExportData.java output.json
Script with arguments:
analyzeHeadless /projects MyProject \
-import binary.exe \
-postScript ProcessFunctions.java arg1 arg2 arg3
Custom script path:
analyzeHeadless /projects MyProject \
-import binary.exe \
-scriptPath "/custom/scripts:/more/scripts" \
-postScript MyCustomScript.java
Script log:
analyzeHeadless /projects MyProject \
-import binary.exe \
-postScript MyScript.java \
-scriptlog /tmp/script.log
Process Options
Process existing program:
# Process specific file in project
analyzeHeadless /projects MyProject \
-process binary.exe \
-postScript AnalyzeSymbols.java
# Process all programs in project
analyzeHeadless /projects MyProject \
-process \
-postScript ExportAll.java
Logging Options
Log file:
analyzeHeadless /projects MyProject \
-import binary.exe \
-log /tmp/analysis.log
Server Options
Connect to Ghidra Server:
analyzeHeadless ghidra://server/Repository ProjectName \
-connect username \
-p \
-process binary.exe
Commit changes:
analyzeHeadless ghidra://server/Repository ProjectName \
-connect username \
-commit "Analysis completed"
Real-World Examples
Example 1: Batch Malware Analysis
#!/bin/bash
# Analyze all executables in malware directory
analyzeHeadless /analysis/projects MalwareBatch \
-import /malware/samples/ \
-recursive \
-overwrite \
-analysisTimeoutPerFile 300 \
-max-cpu 8 \
-postScript ExtractIOCs.java /output/iocs.json \
-log /logs/malware_analysis.log \
-deleteProject
echo "Analysis complete. IOCs extracted to /output/iocs.json"
Example 2: Function Signature Export
#!/bin/bash
# Export function signatures from existing project
for binary in /projects/MyProject/*.exe; do
basename=$(basename "$binary" .exe)
analyzeHeadless /projects MyProject \
-process "$basename.exe" \
-readOnly \
-postScript ExportFunctionInfo.java "/output/${basename}_funcs.json" \
-log "/logs/${basename}.log"
done
Example 3: Differential Analysis
#!/bin/bash
# Compare two versions of a binary
analyzeHeadless /analysis/diff DiffProject \
-import /samples/app_v1.exe \
-import /samples/app_v2.exe \
-postScript CompareBinaries.java app_v1.exe app_v2.exe /output/diff.txt \
-log /logs/diff_analysis.log
Example 4: Custom Loader Analysis
#!/bin/bash
# Analyze raw firmware with specific loader
analyzeHeadless /projects Firmware \
-import firmware.bin \
-processor ARM:LE:32:v7 \
-cspec default \
-loader BinaryLoader \
-loader-baseAddr 0x8000000 \
-postScript AnnotatePeripherals.java \
-log firmware_analysis.log
Example 5: CI/CD Integration
#!/bin/bash
# CI pipeline script for automated analysis
set -e # Exit on error
BUILD_ARTIFACT="$1"
PROJECT_DIR="/tmp/ci_analysis"
PROJECT_NAME="CI_Build_$(date +%s)"
# Analyze the build artifact
analyzeHeadless "$PROJECT_DIR" "$PROJECT_NAME" \
-import "$BUILD_ARTIFACT" \
-analysisTimeoutPerFile 600 \
-postScript SecurityChecks.java /tmp/security_report.json \
-log /tmp/analysis.log \
-deleteProject
# Check for security issues
if grep -q '"vulnerabilities": \[' /tmp/security_report.json; then
echo "Security issues found!"
cat /tmp/security_report.json
exit 1
fi
echo "Analysis passed"
exit 0
Script Development for Headless
Headless-Compatible Scripts
Scripts must handle absence of GUI:
import ghidra.app.script.GhidraScript;
import ghidra.app.util.headless.HeadlessScript;
import java.io.*;
public class HeadlessExport extends GhidraScript {
@Override
public void run() throws Exception {
// Check if running headless
if (!isRunningHeadless()) {
println("This script is designed for headless mode");
}
// Get output path from script arguments
String[] args = getScriptArgs();
if (args.length < 1) {
printerr("Usage: script.java <output_file>");
return;
}
String outputPath = args[0];
File outputFile = new File(outputPath);
// Export data
try (PrintWriter writer = new PrintWriter(outputFile)) {
writer.println("# Function Export");
FunctionIterator iter = currentProgram.getListing().getFunctions(true);
while (iter.hasNext() && !monitor.isCancelled()) {
Function func = iter.next();
writer.printf("%s,%s%n",
func.getName(),
func.getEntryPoint());
}
}
println("Exported to: " + outputPath);
}
}
Using .properties Files
Provide default values for headless execution:
MyScript.properties:
outputDir=/tmp/output
verbose=true
maxFunctions=1000
MyScript.java:
public class MyScript extends GhidraScript {
@Override
public void run() throws Exception {
// Properties file values are auto-loaded
String outputDir = askString("outputDir", "Output directory:");
boolean verbose = askYesNo("verbose", "Verbose output?");
int maxFuncs = askInt("maxFunctions", "Max functions:");
// Script logic using these values
}
}
Python Scripts in Headless Mode
PyGhidra Headless
Use PyGhidra for pure Python headless analysis:
#!/usr/bin/env python3
import pyghidra
import sys
if len(sys.argv) < 2:
print("Usage: analyze.py <binary>")
sys.exit(1)
binary_path = sys.argv[1]
pyghidra.start()
with pyghidra.open_program(binary_path, analyze=True) as flat_api:
program = flat_api.getCurrentProgram()
listing = program.getListing()
print(f"Analyzing: {program.getName()}")
print(f"Functions: {listing.getFunctions(True).size()}")
# Export function list
with open('functions.txt', 'w') as f:
for func in listing.getFunctions(True):
f.write(f"{func.getName()} @ {func.getEntryPoint()}\n")
print("Export complete")
Jython Scripts
Jython scripts work in headless mode:
analyzeHeadless /projects MyProject \
-import binary.exe \
-postScript analyze.py
analyze.py:
# @category Analysis
listing = currentProgram.getListing()
funcs = listing.getFunctions(True)
count = 0
while funcs.hasNext():
func = funcs.next()
println("%s @ %s" % (func.getName(), func.getEntryPoint()))
count += 1
println("Total functions: %d" % count)
Parallel Processing
#!/bin/bash
# Process binaries in parallel
find /samples -name "*.exe" | parallel -j 4 \
analyzeHeadless /tmp/projects Project_{#} \
-import {} \
-postScript Export.java {.}.json \
-deleteProject
Resource Limits
# Limit memory and CPU
export MAXMEM=4G
analyzeHeadless /projects MyProject \
-import /samples/ \
-recursive \
-max-cpu 2 \
-analysisTimeoutPerFile 300
Analysis Control
Disable unnecessary analyzers for speed:
// Pre-script to disable slow analyzers
import ghidra.app.script.GhidraScript;
import ghidra.framework.options.Options;
public class DisableAnalyzers extends GhidraScript {
public void run() throws Exception {
Options options = currentProgram.getOptions("Analyzers");
options.setBoolean("Non-Returning Functions - Discovered", false);
options.setBoolean("Decompiler Switch Analysis", false);
}
}
Troubleshooting
Common Issues
Script not found:
# Add script path
analyzeHeadless /projects MyProject \
-scriptPath "/path/to/scripts" \
-postScript MyScript.java
Analysis timeout:
# Increase timeout or disable analysis
-analysisTimeoutPerFile 3600 # 1 hour
# OR
-noanalysis
Memory errors:
# Increase JVM heap
export MAXMEM=8G
analyzeHeadless ...
Import fails:
# Specify processor explicitly
-processor x86:LE:64:default
-loader BinaryLoader
Debug Mode
Enable verbose output:
analyzeHeadless /projects MyProject \
-import binary.exe \
-log /tmp/debug.log
# Check log for details
tail -f /tmp/debug.log
Environment Variables
# Ghidra installation
export GHIDRA_INSTALL_DIR=/opt/ghidra
# JVM heap size
export MAXMEM=4G
# Script paths
export GHIDRA_SCRIPT_PATHS="/custom/scripts:/more/scripts"
# Temporary directory
export TMPDIR=/fast/storage/tmp
Integration Examples
Docker Container
FROM ubuntu:22.04
# Install dependencies
RUN apt-get update && apt-get install -y \
openjdk-17-jdk \
wget \
unzip
# Install Ghidra
WORKDIR /opt
RUN wget https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_11.4_build/ghidra_11.4_PUBLIC_20250620.zip \
&& unzip ghidra_11.4_PUBLIC_20250620.zip \
&& rm ghidra_11.4_PUBLIC_20250620.zip
ENV GHIDRA_INSTALL_DIR=/opt/ghidra_11.4_PUBLIC
ENV PATH="$GHIDRA_INSTALL_DIR/support:$PATH"
# Copy analysis scripts
COPY scripts/ /scripts/
# Entry point
ENTRYPOINT ["analyzeHeadless"]
GitHub Actions
name: Binary Analysis
on: [push]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Ghidra
run: |
wget https://github.com/.../ghidra_11.4_PUBLIC.zip
unzip ghidra_11.4_PUBLIC.zip
echo "GHIDRA_INSTALL_DIR=$PWD/ghidra_11.4_PUBLIC" >> $GITHUB_ENV
- name: Analyze Binary
run: |
analyzeHeadless /tmp/projects Analysis \
-import ${{ github.workspace }}/binary.exe \
-postScript ExportReport.java report.json \
-deleteProject
- name: Upload Report
uses: actions/upload-artifact@v3
with:
name: analysis-report
path: report.json
Best Practices
- Use -deleteProject for temporary analysis to save disk space
- Set timeouts to prevent hung analysis jobs
- Log output for debugging and audit trails
- Validate inputs in scripts before processing
- Handle errors gracefully in scripts
- Use -max-cpu to control resource usage
- Test scripts in GUI before headless deployment
- Version control scripts and configurations