|
| 1 | +# SC6ModdingDocs - Ghidra MCP instructions |
| 2 | + |
| 3 | +The reverse-engineering work runs through `bethington/ghidra-mcp` MCP tools. |
| 4 | +The MCP is the authoritative interface for Ghidra - never edit `.gpr` files or invoke Ghidra scripts directly. |
| 5 | +The bridge auto-discovers tools from `/mcp/schema`; if a tool name isn't in the schema it doesn't exist. |
| 6 | + |
| 7 | +## Ghidra MCP conventions |
| 8 | + |
| 9 | +The MCP enforces conventions in the tool layer (auto-fix / warn / reject tiers). Don't fight them - they exist because the codebase outgrew prompt-only discipline. |
| 10 | + |
| 11 | +**Tool inventory and dynamic groups**: |
| 12 | + |
| 13 | +`/mcp/schema` is authoritative. Don't guess tool names from training data. |
| 14 | +For Ghidra comment work, load the `comment` group and use native comment tools: |
| 15 | +`batch_set_comments`, `set_plate_comment`, `set_decompiler_comment`, `set_disassembly_comment`, and `get_plate_comment`. |
| 16 | +`set_bookmark` is only for bookmarks and audit breadcrumbs; do not treat it as a substitute for plate, PRE, EOL, or disassembly comments. |
| 17 | + |
| 18 | +If `check_tools` reports a tool callable but the Codex client cannot invoke that endpoint, state that as a client/tool-exposure problem and stop before claiming the Ghidra comments were updated. |
| 19 | + |
| 20 | +**Allowed Ghidra MCP tool groups by task**: |
| 21 | +- Default allowed non-malware groups: `analysis`, `comment`, `datatype`, `documentation`, `function`, `listing`, `program`, `symbol`, `xref` |
| 22 | +- Function cleanup: `function`, `comment`, `analysis` |
| 23 | +- Comment audits: `comment`, `analysis`, `listing`, `xref` |
| 24 | +- Struct/type recovery: `datatype`, `xref`, `function`, `symbol`, `analysis` |
| 25 | +- Global/data labeling: `symbol`, `datatype`, `listing`, `xref` |
| 26 | +- Caller/callee tracing: `xref`, `function`, `listing` |
| 27 | +- Documentation transfer and binary comparison: `documentation`, `function`, `listing` |
| 28 | +- Program navigation and persistence: `program`, `listing` |
| 29 | + |
| 30 | +Load these groups as needed with `load_tool_group`. The `malware` group remains excluded by default; use it only when the user explicitly requests malware, IOC, or anti-analysis work. |
| 31 | +When doing Ghidra reverse-engineering work, opportunistically improve clear function names, variable names, variable types, labels, and structs that are directly relevant to the current analysis. |
| 32 | + |
| 33 | +**Naming**: |
| 34 | +- Functions: PascalCase, verb-first. `GetPlayerHealth`, not `playerHealth` or `SKILLS_GetLevel`. Module prefixes (`UPPERCASE_`) are accepted and validated separately. |
| 35 | +- Globals: `g_` + Hungarian (`g_dwCount`, `g_pMain`, `g_szPath`). |
| 36 | +- Labels: snake_case. |
| 37 | +- Struct fields are auto-prefixed on `add_struct_field` / `modify_struct_field` / `create_struct` - pass the logical name (`count`, `next`, `health`); the tool stamps the prefix (`dwCount`, `pNext`, `wHealth`). |
| 38 | + |
| 39 | +**Hungarian quick-ref**: |
| 40 | +``` |
| 41 | +b:byte c:char f:bool n:int/short dw:uint/DWORD w:ushort l:long |
| 42 | +fl:float d:double ll:longlong qw:ulonglong ld:float10 h:HANDLE |
| 43 | +p:void*/ptr pb:byte* pw:ushort* pdw:uint* pn:int* pp:void** |
| 44 | +sz:char*(local) lpsz:char*(param) wsz:wchar_t* lpcsz:const char*(param) |
| 45 | +ab:byte[N] aw:ushort[N] ad:uint[N] an:int[N] |
| 46 | +g_:global prefix (g_dwCount, g_pMain) pfn:func_ptr (PascalCase, no g_) |
| 47 | +Struct pointers: p+StructName (pUnit, pInventory, ppItem for double ptr) |
| 48 | +``` |
| 49 | + |
| 50 | +**Type normalization**: `undefined1` -> byte, `undefined2` -> ushort, `undefined4` -> uint/int/float/ptr (by usage), `undefined8` -> double/longlong. Use Ghidra builtins (`dword`, `byte`, `ushort`) not Windows aliases (`DWORD`, `BYTE`) when calling `set_local_variable_type`. |
| 51 | + |
| 52 | +## Workflow ordering (load-bearing) |
| 53 | + |
| 54 | +`set_function_prototype` **wipes plate comments**. Do all structural work first: |
| 55 | + |
| 56 | +1. Rename function + set prototype (parallel) |
| 57 | +2. Type audit + variable rename (`get_function_variables` -> `set_local_variable_type` -> `rename_variables`) |
| 58 | +3. Plate + PRE + EOL comments in one `batch_set_comments` call |
| 59 | +4. `analyze_function_completeness` - if fixable deductions > 10 points, address and re-verify |
| 60 | + |
| 61 | +Never rename a variable with a Hungarian prefix (`dw`, `n`, `b`, `p`, `sz`, ...) while its type is still `undefined*`. |
| 62 | +Resolve the type first; if undeterminable, use a descriptive name without a type prefix (`questBits` not `dwQuestBits`). |
| 63 | + |
| 64 | +Phantoms (`extraout_*`, `in_*` with undefined types) are decompiler artifacts. Note them in plate-comment Special Cases - don't retry type-setting. |
| 65 | + |
| 66 | +## Gotchas |
| 67 | + |
| 68 | +- **Plate-comment newlines**: passing `\n` literally produces the text `\n` in the comment. Use actual multi-line strings. |
| 69 | +- **Register-only variables**: when `set_local_variable_type` fails for a register var, document via `set_decompiler_comment` PRE_COMMENT (`"nIterator: int - loop counter (register-only)"`). The completeness scorer excludes these. |
| 70 | +- **Struct access without a struct**: for raw `*(ptr+0x10)` access where no matching struct exists, add EOL comments at each access (`/* +0x10: flags */`) - satisfies the scorer without forcing struct creation. |
| 71 | +- `/run_script_inline`, `/run_ghidra_script`, and `run_script` may appear callable in the MCP `program` group. Project policy still forbids using them for Ghidra database edits unless the user explicitly overrides this rule. Use native MCP tools instead. |
| 72 | + |
| 73 | +## Skills available |
| 74 | + |
| 75 | +- `ghidra-doc-function` - full V5 doc workflow (classify -> rename -> type -> comment -> verify) for a single function |
| 76 | +- `ghidra-investigate-type` - discover/define a struct from generic-pointer parameters (`int*`/`void*`) and apply it across all callers |
0 commit comments