npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
When to Use
- Static and dynamic analysis have identified suspicious functionality that requires deeper code-level understanding
- You need to reverse engineer C2 communication protocols, encryption algorithms, or custom obfuscation
- Understanding the exact exploit mechanism or vulnerability targeted by a malware sample
- Extracting hardcoded configuration data (C2 addresses, encryption keys, campaign IDs) embedded in compiled code
- Developing precise YARA rules or detection signatures based on unique code patterns
Do not use for initial triage of unknown samples; perform static analysis with PEStudio and behavioral analysis with Cuckoo first.
Prerequisites
- Ghidra 11.x installed (download from https://ghidra-sre.org/) with JDK 17+
- Analysis VM isolated from production network (Windows or Linux host)
- Familiarity with x86/x64 assembly language and Windows API conventions
- PDB symbol files for Windows system DLLs to improve decompilation accuracy
- Ghidra scripts repository (ghidra_scripts) for automated analysis tasks
- Secondary reference: IDA Free or Binary Ninja for cross-validation of analysis results
Workflow
Step 1: Create Project and Import Binary
Set up a Ghidra project and import the malware sample:
1. Launch Ghidra: ghidraRun (Linux) or ghidraRun.bat (Windows)
2. File -> New Project -> Non-Shared Project -> Select directory
3. File -> Import File -> Select malware binary
4. Ghidra auto-detects format (PE, ELF, Mach-O) and architecture
5. Accept default import options (or specify base address if known)
6. Double-click imported file to open in CodeBrowser
7. When prompted, run Auto Analysis with default analyzers enabledHeadless analysis for automation:
# Run Ghidra headless analysis with decompiler
/opt/ghidra/support/analyzeHeadless /tmp/ghidra_project MalwareProject \
-import suspect.exe \
-postScript ExportDecompilation.py \
-scriptPath /opt/ghidra/scripts/ \
-deleteProjectStep 2: Identify Key Functions and Entry Points
Navigate the binary to locate critical code sections:
Navigation Strategy:
━━━━━━━━━━━━━━━━━━━
1. Start at entry point (OEP) - follow execution from _start/WinMain
2. Check Symbol Tree for imported functions (Window -> Symbol Tree)
3. Search for cross-references to suspicious APIs:
- VirtualAlloc/VirtualAllocEx (memory allocation for injection)
- CreateRemoteThread (remote thread injection)
- CryptEncrypt/CryptDecrypt (encryption operations)
- InternetOpen/HttpSendRequest (C2 communication)
- RegSetValueEx (persistence via registry)
4. Use Search -> For Strings to find embedded URLs, IPs, and paths
5. Check the Functions window sorted by size (large functions often contain core logic)Ghidra keyboard shortcuts for efficient navigation:
G - Go to address
Ctrl+E - Search for strings
X - Show cross-references to current location
Ctrl+Shift+F - Search memory for byte patterns
L - Rename label/function
; - Add comment
T - Retype variable
Ctrl+L - Retype return valueStep 3: Analyze Decompiled Code
Use Ghidra's decompiler to understand function logic:
// Example: Ghidra decompiler output for a decryption routine
// Analyst renames variables and adds types for clarity
void decrypt_config(BYTE *encrypted_data, int data_len, BYTE *key, int key_len) {
// XOR decryption with rolling key
for (int i = 0; i < data_len; i++) {
encrypted_data[i] = encrypted_data[i] ^ key[i % key_len];
}
return;
}
// Analyst actions in Ghidra:
// 1. Right-click parameters -> Retype to correct types (BYTE*, int)
// 2. Right-click variables -> Rename to meaningful names
// 3. Add comments explaining the algorithm
// 4. Set function signature to propagate types to callersStep 4: Trace C2 Communication Logic
Follow the network communication code path:
Analysis Steps for C2 Protocol Reverse Engineering:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Find InternetOpenA/WinHttpOpen call -> trace to wrapper function
2. Follow data flow from encrypted config -> URL construction
3. Identify HTTP method (GET/POST), headers, and body format
4. Locate response parsing logic (JSON parsing, custom binary protocol)
5. Map the C2 command dispatcher (switch/case or jump table)
6. Document the command set (download, execute, exfiltrate, update, uninstall)Ghidra Script for extracting C2 configuration:
# Ghidra Python script: extract_c2_config.py
# Run via Script Manager in Ghidra
from ghidra.program.model.data import StringDataType
from ghidra.program.model.symbol import SourceType
# Search for XOR decryption patterns
listing = currentProgram.getListing()
memory = currentProgram.getMemory()
# Find references to InternetOpenA
symbol_table = currentProgram.getSymbolTable()
for symbol in symbol_table.getExternalSymbols():
if "InternetOpen" in symbol.getName():
refs = getReferencesTo(symbol.getAddress())
for ref in refs:
print("C2 init at: {}".format(ref.getFromAddress()))Step 5: Analyze Encryption and Obfuscation
Identify and document cryptographic routines:
Common Malware Encryption Patterns:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
XOR Cipher: Loop with XOR operation, often single-byte or rolling key
RC4: Two loops (KSA + PRGA), 256-byte S-box initialization
AES: Look for S-box constants (0x63, 0x7C, 0x77...) or calls to CryptEncrypt
Base64: Lookup table with A-Za-z0-9+/= characters
Custom: Combination of arithmetic operations (ADD, SUB, ROL, ROR with XOR)
Identification Tips:
- Search for constants: AES S-box, CRC32 table, MD5 init values
- Look for loop structures operating on byte arrays
- Check for Windows Crypto API usage (CryptAcquireContext -> CryptCreateHash -> CryptEncrypt)
- FindCrypt Ghidra plugin automatically identifies crypto constantsStep 6: Document Findings and Create Detection Signatures
Produce actionable intelligence from reverse engineering:
# Generate YARA rule from unique code patterns found in Ghidra
cat << 'EOF' > malware_family_x.yar
rule MalwareFamilyX_Decryptor {
meta:
description = "Detects MalwareX decryption routine"
author = "analyst"
date = "2025-09-15"
strings:
// XOR decryption loop with hardcoded key
$decrypt = { 8A 04 0E 32 04 0F 88 04 0E 41 3B CA 7C F3 }
// C2 URL pattern after decryption
$c2_pattern = "/gate.php?id=" ascii
condition:
uint16(0) == 0x5A4D and $decrypt and $c2_pattern
}
EOFKey Concepts
| Term | Definition |
|---|---|
| Disassembly | Converting machine code bytes into human-readable assembly language instructions; Ghidra's Listing view shows disassembled code |
| Decompilation | Lifting assembly code to pseudo-C representation for easier analysis; Ghidra's Decompile window provides this view |
| Cross-Reference (XREF) | Reference showing where a function or data address is called from or used; essential for tracing code execution flow |
| Control Flow Graph (CFG) | Visual representation of all possible execution paths through a function; reveals branching logic and loops |
| Original Entry Point (OEP) | The actual start address of the malware code after unpacking; packers redirect execution through an unpacking stub first |
| Function Signature | The return type, name, and parameter types of a function; applying correct signatures improves decompiler output quality |
| Ghidra Script | Python or Java automation script executed within Ghidra to perform batch analysis, pattern searching, or data extraction |
Tools & Systems
- Ghidra: NSA's open-source software reverse engineering suite with disassembler, decompiler, and scripting support for multiple architectures
- IDA Pro/Free: Industry-standard interactive disassembler; IDA Free provides x86/x64 cloud-based decompilation
- Binary Ninja: Commercial reverse engineering platform with modern UI and extensive API for plugin development
- x64dbg: Open-source x64/x32 debugger for Windows used alongside Ghidra for dynamic debugging of malware
- FindCrypt (Ghidra Plugin): Plugin that identifies cryptographic constants and algorithms in binary code
Common Scenarios
Scenario: Reversing Custom C2 Protocol
Context: Behavioral analysis shows encrypted traffic to an external IP on a non-standard port. Network signatures cannot detect variants because the protocol is proprietary. Deep reverse engineering is needed to understand the protocol structure.
Approach:
- Import the unpacked sample into Ghidra and run full auto-analysis
- Locate socket/WinHTTP API calls and trace backwards to the calling function
- Identify the encryption routine called before data is sent (follow data flow from send/HttpSendRequest)
- Reverse the encryption (XOR key extraction, RC4 key derivation, AES key location)
- Map the command structure by analyzing the response parsing function (switch/case on command IDs)
- Document the protocol format (header structure, command bytes, encryption method)
- Create a protocol decoder script for network monitoring tools
Pitfalls:
- Not running the full auto-analysis before starting manual analysis (missing function boundaries and type propagation)
- Ignoring indirect calls through function pointers or vtables (use cross-references to data holding function addresses)
- Spending time on library code that Ghidra's Function ID (FID) or FLIRT signatures should have identified
- Not saving Ghidra project progress frequently (analysis state can be lost on crashes)
Output Format
REVERSE ENGINEERING ANALYSIS REPORT
=====================================
Sample: unpacked_payload.exe
SHA-256: abc123def456...
Architecture: x86 (32-bit PE)
Ghidra Project: MalwareX_Analysis
FUNCTION MAP
0x00401000 main() - Entry point, initializes config
0x00401200 decrypt_config() - XOR decryption with 16-byte key
0x00401400 init_c2() - WinHTTP initialization, URL construction
0x00401800 c2_beacon() - HTTP POST beacon with system info
0x00401C00 cmd_dispatcher() - Switch on 12 command codes
0x00402000 inject_process() - Process hollowing into svchost.exe
0x00402400 persist_registry() - HKCU Run key persistence
0x00402800 exfil_data() - File collection and encrypted upload
C2 PROTOCOL
Method: HTTPS POST to /gate.php
Encryption: RC4 with derived key (MD5 of bot_id + campaign_key)
Bot ID Format: MD5(hostname + username + volume_serial)
Beacon Interval: 60 seconds with 10% jitter
Command Set:
0x01 - Download and execute file
0x02 - Execute shell command
0x03 - Upload file to C2
0x04 - Update configuration
0x05 - Uninstall and remove traces
ENCRYPTION DETAILS
Algorithm: RC4
Key Derivation: MD5(bot_id + "campaign_2025_q3")
Hardcoded Seed: "campaign_2025_q3" at offset 0x00405A00
EXTRACTED IOCs
C2 URLs: hxxps://update.malicious[.]com/gate.php
hxxps://backup.evil[.]net/gate.php (failover)
Campaign ID: campaign_2025_q3
RC4 Key Material: [see encryption details above]References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 1
api-reference.md2.6 KB
API Reference: Malware Reverse Engineering with Ghidra Agent
Overview
Combines Ghidra headless analysis with r2pipe (radare2) for automated malware binary analysis: function enumeration, import classification, section entropy, cryptographic constant detection, and network indicator extraction.
Dependencies
| Package | Version | Purpose |
|---|---|---|
| r2pipe | >= 1.8 | Radare2 scripting interface for binary analysis |
| hashlib | stdlib | File hash computation |
External Tools
| Tool | Purpose |
|---|---|
| Ghidra (analyzeHeadless) | Automated disassembly and decompilation |
| radare2 | Binary analysis, function detection, string extraction |
Core Functions
run_ghidra_headless(ghidra_path, project_dir, project_name, binary_path, script)
Executes Ghidra in headless mode with optional post-analysis script.
- Timeout: 600 seconds
- Returns:
dictwith command, returncode, stdout/stderr
export_functions_ghidra(...)
Generates and runs a Ghidra script to export function list as JSON.
- Exports: name, address, size, calling convention, is_thunk
analyze_with_radare2(filepath)
Full r2pipe analysis: binary info, functions, imports, strings, sections, entry points.
- Classifies imports: injection, network, evasion, crypto, persistence
- Extracts: network indicators (URLs, IPs) from strings
- Returns:
dictwith info, function_count, suspicious_imports, sections, etc.
extract_crypto_constants(filepath)
Searches binary for known cryptographic constants: AES S-box, RC4 init table, SHA-256 init vector, RSA magic bytes.
- Returns:
list[dict]with constant name and file offset
analyze_malware(filepath, ghidra_path, output_dir)
Full pipeline: hashes -> crypto constants -> radare2 analysis -> Ghidra headless.
Suspicious Import Categories
| Category | Example Functions |
|---|---|
| injection | VirtualAllocEx, WriteProcessMemory, CreateRemoteThread |
| network | InternetOpenA, WSAStartup, URLDownloadToFileA |
| evasion | IsDebuggerPresent, NtQueryInformationProcess |
| crypto | CryptEncrypt, CryptDecrypt |
| persistence | RegSetValueExA, CreateServiceA |
Radare2 Commands Used
| Command | Purpose |
|---|---|
aaa |
Full auto-analysis |
ij |
Binary info as JSON |
aflj |
Function list as JSON |
iij |
Import list as JSON |
izj |
String list as JSON |
iSj |
Section list as JSON |
iej |
Entry points as JSON |
Usage
# With radare2 only
python agent.py malware.exe
# With Ghidra headless analysis
python agent.py malware.exe /opt/ghidraScripts 1
agent.py8.2 KB
#!/usr/bin/env python3
"""Malware reverse engineering agent using Ghidra headless analyzer and r2pipe."""
import subprocess
import os
import sys
import json
import re
import hashlib
try:
import r2pipe
except ImportError:
r2pipe = None
def compute_hashes(filepath):
"""Compute file hashes for identification."""
with open(filepath, "rb") as f:
data = f.read()
return {
"md5": hashlib.md5(data).hexdigest(),
"sha1": hashlib.sha1(data).hexdigest(),
"sha256": hashlib.sha256(data).hexdigest(),
"size": len(data),
}
def run_ghidra_headless(ghidra_path, project_dir, project_name, binary_path,
script=None, script_args=None):
"""Run Ghidra in headless mode for automated analysis."""
os.makedirs(project_dir, exist_ok=True)
cmd = [
os.path.join(ghidra_path, "support", "analyzeHeadless"),
project_dir, project_name,
"-import", binary_path,
"-overwrite",
]
if script:
cmd.extend(["-postScript", script])
if script_args:
cmd.extend(script_args)
result = subprocess.run(
cmd, capture_output=True, text=True, timeout=600
)
return {
"command": " ".join(cmd),
"returncode": result.returncode,
"stdout": result.stdout[-2000:] if result.stdout else "",
"stderr": result.stderr[-1000:] if result.stderr else "",
}
def export_functions_ghidra(ghidra_path, project_dir, project_name, binary_path,
output_file):
"""Export function list using Ghidra headless with a script."""
script_content = """
import ghidra.program.model.listing.FunctionIterator
import json
output = []
fm = currentProgram.getFunctionManager()
funcs = fm.getFunctions(True)
for func in funcs:
entry = {
"name": func.getName(),
"address": str(func.getEntryPoint()),
"size": func.getBody().getNumAddresses(),
"calling_convention": func.getCallingConventionName(),
"is_thunk": func.isThunk(),
}
output.append(entry)
with open("{output}", "w") as f:
json.dump(output, f, indent=2)
""".replace("{output}", output_file.replace("\\", "\\\\"))
script_path = os.path.join(project_dir, "export_functions.py")
with open(script_path, "w") as f:
f.write(script_content)
return run_ghidra_headless(
ghidra_path, project_dir, project_name, binary_path,
script="export_functions.py"
)
def analyze_with_radare2(filepath):
"""Analyze binary with radare2 via r2pipe for quick triage."""
if r2pipe is None:
return {"error": "r2pipe not installed (pip install r2pipe)"}
r2 = r2pipe.open(filepath, flags=["-2"])
r2.cmd("aaa")
info = r2.cmdj("ij")
functions = r2.cmdj("aflj") or []
imports = r2.cmdj("iij") or []
strings = r2.cmdj("izj") or []
sections = r2.cmdj("iSj") or []
entry_points = r2.cmdj("iej") or []
suspicious_imports = {
"injection": ["VirtualAllocEx", "WriteProcessMemory", "CreateRemoteThread",
"NtCreateThreadEx"],
"network": ["InternetOpenA", "HttpSendRequestA", "WSAStartup",
"URLDownloadToFileA"],
"evasion": ["IsDebuggerPresent", "CheckRemoteDebuggerPresent",
"NtQueryInformationProcess"],
"crypto": ["CryptEncrypt", "CryptDecrypt", "CryptAcquireContextA"],
"persistence": ["RegSetValueExA", "CreateServiceA"],
}
import_findings = []
for imp in imports:
name = imp.get("name", "")
for category, funcs in suspicious_imports.items():
if name in funcs:
import_findings.append({
"category": category,
"function": name,
"library": imp.get("lib", ""),
})
network_strings = []
for s in strings:
val = s.get("string", "")
if re.search(r"https?://", val) or re.search(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", val):
network_strings.append(val[:200])
section_analysis = []
for sec in sections:
entropy = sec.get("entropy", 0)
flags = []
if entropy and entropy > 7.0:
flags.append("HIGH_ENTROPY")
section_analysis.append({
"name": sec.get("name", ""),
"size": sec.get("size", 0),
"vsize": sec.get("vsize", 0),
"entropy": entropy,
"flags": flags,
})
r2.quit()
return {
"info": {
"arch": info.get("bin", {}).get("arch", ""),
"bits": info.get("bin", {}).get("bits", 0),
"os": info.get("bin", {}).get("os", ""),
"type": info.get("bin", {}).get("bintype", ""),
"compiler": info.get("bin", {}).get("compiler", ""),
},
"function_count": len(functions),
"import_count": len(imports),
"string_count": len(strings),
"suspicious_imports": import_findings,
"network_indicators": network_strings[:20],
"sections": section_analysis,
"entry_points": [{"vaddr": e.get("vaddr"), "type": e.get("type")} for e in entry_points],
}
def extract_crypto_constants(filepath):
"""Search binary for known cryptographic constants."""
with open(filepath, "rb") as f:
data = f.read()
constants = {
"AES_SBOX": bytes([0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5]),
"RC4_INIT": bytes(range(256)),
"SHA256_INIT": bytes.fromhex("6a09e667bb67ae853c6ef372a54ff53a"),
"RSA_MAGIC": b"RSA1",
}
found = []
for name, pattern in constants.items():
offset = data.find(pattern)
if offset >= 0:
found.append({"constant": name, "offset": hex(offset)})
return found
def analyze_malware(filepath, ghidra_path=None, output_dir="/tmp/ghidra_analysis"):
"""Full malware analysis pipeline."""
os.makedirs(output_dir, exist_ok=True)
report = {"file": os.path.basename(filepath)}
report["hashes"] = compute_hashes(filepath)
report["crypto_constants"] = extract_crypto_constants(filepath)
if r2pipe:
report["radare2"] = analyze_with_radare2(filepath)
if ghidra_path and os.path.exists(ghidra_path):
ghidra_result = run_ghidra_headless(
ghidra_path, output_dir, "malware_project", filepath
)
report["ghidra"] = {
"analysis_complete": ghidra_result["returncode"] == 0,
"output": ghidra_result["stdout"][-500:],
}
return report
def print_report(report):
print("Malware Reverse Engineering Report")
print("=" * 50)
print(f"File: {report['file']}")
print(f"SHA-256: {report['hashes']['sha256']}")
print(f"Size: {report['hashes']['size']} bytes")
if report.get("crypto_constants"):
print(f"\nCrypto Constants Found:")
for c in report["crypto_constants"]:
print(f" {c['constant']} at {c['offset']}")
r2 = report.get("radare2", {})
if r2 and "error" not in r2:
info = r2.get("info", {})
print(f"\nBinary Info: {info.get('arch', '?')}/{info.get('bits', '?')}bit "
f"({info.get('os', '?')}) [{info.get('type', '?')}]")
print(f"Functions: {r2.get('function_count', 0)}")
print(f"Imports: {r2.get('import_count', 0)}")
if r2.get("suspicious_imports"):
print(f"\nSuspicious Imports:")
for imp in r2["suspicious_imports"]:
print(f" [{imp['category']}] {imp['library']} -> {imp['function']}")
if r2.get("network_indicators"):
print(f"\nNetwork Indicators:")
for ni in r2["network_indicators"][:10]:
print(f" {ni}")
print(f"\nSections:")
for sec in r2.get("sections", []):
flags = f" [{', '.join(sec['flags'])}]" if sec.get("flags") else ""
print(f" {sec['name']:10s} size={sec['size']:>8} entropy={sec.get('entropy', 0):.2f}{flags}")
if report.get("ghidra", {}).get("analysis_complete"):
print(f"\nGhidra: Analysis complete")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python agent.py <binary> [ghidra_install_path]")
sys.exit(1)
binary = sys.argv[1]
ghidra = sys.argv[2] if len(sys.argv) > 2 else None
result = analyze_malware(binary, ghidra_path=ghidra)
print_report(result)