<!-- Copyright (c) 2026 defconxt. All rights reserved. -->
<!-- Licensed under AGPL-3.0 — see LICENSE file for details. -->
---
name: building-custom-ida-ghidra-scripts
description: >-
  Build custom scripts for IDA Pro and Ghidra to automate reverse engineering
  tasks including function identification, string decryption, control flow
  analysis, decompiler output parsing, and automated IOC extraction from
  malware binaries.
domain: cybersecurity
subdomain: automation-scripting
tags:
  - ida-pro
  - ghidra
  - reverse-engineering
  - binary-analysis
  - decompilation
  - malware-analysis
version: "1.0"
author: defconxt
license: AGPL-3.0
metadata:
  mitre-attack: ["T1588.002"]
---

# Building Custom IDA/Ghidra Scripts

## Overview

Reverse engineering at scale requires scripted analysis. This skill covers
building automation scripts for IDA Pro (IDAPython) and Ghidra (Ghidra
Script/Pyhidra) to identify functions, decrypt strings, trace API calls,
extract IOCs, and generate analysis reports from malware binaries.

Mode: `[MODE: RED]` — Offensive analysis with detection notes.

## Prerequisites

| Requirement | Details |
|---|---|
| IDA Pro 7.x+ with IDAPython, or Ghidra 10.x+ with Pyhidra | Required |
| Python 3.10+ for standalone analysis scripts | Required |
| `capstone` for disassembly, `unicorn` for emulation (optional) | Required |

## Key Concepts

### IDAPython: Function Enumeration

```python
# IDA Pro script — enumerate all functions with metadata
import idautils
import idaapi
import idc
import json

def enumerate_functions() -> list[dict]:
    """List all functions with size, flags, and cross-references."""
    functions = []
    for func_ea in idautils.Functions():
        func = idaapi.get_func(func_ea)
        if not func:
            continue
        xrefs_to = list(idautils.XrefsTo(func_ea))
        functions.append({
            "address": hex(func_ea),
            "name": idc.get_func_name(func_ea),
            "size": func.size(),
            "xref_count": len(xrefs_to),
            "flags": hex(func.flags),
        })
    return functions
```

### IDAPython: Suspicious API Detection

```python
SUSPICIOUS_APIS = {
    "VirtualAlloc", "VirtualProtect", "CreateRemoteThread",
    "WriteProcessMemory", "NtUnmapViewOfSection", "LoadLibraryA",
    "GetProcAddress", "WinExec", "ShellExecuteA", "URLDownloadToFileA",
    "InternetOpenA", "HttpSendRequestA", "CryptEncrypt",
}

def find_suspicious_imports() -> list[dict]:
    """Identify imports commonly used by malware."""
    findings = []
    for ea in idautils.Functions():
        name = idc.get_func_name(ea)
        if name in SUSPICIOUS_APIS:
            callers = [
                {"address": hex(ref.frm), "function": idc.get_func_name(ref.frm)}
                for ref in idautils.XrefsTo(ea)
            ]
            findings.append({"api": name, "address": hex(ea), "callers": callers})
    return findings
```

### Ghidra Script: String Decryption

```python
# Ghidra headless script — extract and attempt XOR string decryption
# Run: analyzeHeadless /tmp/project proj -import binary -postScript this_script.py

from ghidra.program.model.data import DataUtilities
from ghidra.program.model.listing import Data

def xor_decrypt(data: bytes, key: int) -> str:
    """XOR decrypt with single-byte key."""
    return bytes(b ^ key for b in data).decode("ascii", errors="replace")

def find_encrypted_strings(program):
    """Scan for potential XOR-encrypted strings."""
    memory = program.getMemory()
    results = []
    listing = program.getListing()

    for block in memory.getBlocks():
        if not block.isInitialized():
            continue
        start = block.getStart()
        end = block.getEnd()
        buf = bytearray(block.getSize())
        memory.getBytes(start, buf)

        # Try common XOR keys
        for key in range(1, 256):
            decrypted = xor_decrypt(bytes(buf[:64]), key)
            if sum(c.isalpha() for c in decrypted) > len(decrypted) * 0.6:
                results.append({
                    "address": str(start),
                    "key": hex(key),
                    "preview": decrypted[:40],
                })
                break
    return results
```

### Standalone: Capstone Disassembly

```python
from capstone import Cs, CS_ARCH_X86, CS_MODE_64

def disassemble_bytes(code: bytes, base_addr: int = 0x400000) -> list[dict]:
    """Disassemble raw bytes using Capstone."""
    md = Cs(CS_ARCH_X86, CS_MODE_64)
    md.detail = True
    instructions = []
    for insn in md.disasm(code, base_addr):
        instructions.append({
            "address": hex(insn.address),
            "mnemonic": insn.mnemonic,
            "operands": insn.op_str,
            "size": insn.size,
        })
    return instructions
```

## Workflow

### Step 1: Analyze Binary Metadata

```bash
node scripts/agent.js --action analyze --binary /tmp/sample.exe
```

### Step 2: Extract Strings and IOCs

```bash
node scripts/agent.js --action extract-strings --binary /tmp/sample.exe
```

### Step 3: Disassemble Entry Point

```bash
node scripts/agent.js --action disassemble --binary /tmp/sample.exe --offset 0 --count 50
```

## Verification

- [ ] Function enumeration lists all identified functions with xref counts
- [ ] Suspicious API detection flags known malware indicators
- [ ] XOR string decryption attempts common single-byte keys
- [ ] Capstone disassembly produces correct mnemonics for x86/x64
- [ ] Exit code 0 on success, 1 on findings, 2 on error

## References

- [IDAPython Documentation](https://hex-rays.com/products/ida/support/idapython_docs/)
- [Ghidra API Documentation](https://ghidra.re/ghidra_docs/api/)
- [Capstone Engine](https://www.capstone-engine.org/)
- [MITRE ATT&CK T1140 — Deobfuscate/Decode Files](https://attack.mitre.org/techniques/T1140/)
