paavohtl Claude Opus 4.6 (1M context) commited on
Commit
2559813
Β·
1 Parent(s): 05dc1e9

feat: add debug-cli skill for live memory inspection

Browse files

Documents all commands, address syntax, pointer chain semantics,
key address chains (DDGame via g_GameSession->0xA0->0x488), and
common patterns for struct inspection and data dumping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. .claude/skills/debug-cli/SKILL.md +188 -0
.claude/skills/debug-cli/SKILL.md ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: debug-cli
3
+ description: Read memory from a live WA.exe process via the debug CLI. Inspect structs, vtables, pointer chains, and raw data at Ghidra or absolute addresses. Requires the game to be running in debug mode (start-debug.ps1).
4
+ ---
5
+
6
+ # Debug CLI
7
+
8
+ Read memory from a live WA.exe process. Useful for validating struct layouts, investigating unknown fields, following pointer chains, and comparing runtime state against Ghidra analysis.
9
+
10
+ ## Prerequisites
11
+
12
+ The game must be running in debug mode. If the CLI fails to connect, ask the user to:
13
+
14
+ 1. Run `powershell -ExecutionPolicy Bypass -File start-debug.ps1` (builds everything and launches WA.exe with debug server + debug UI enabled)
15
+ 2. Set up a game scenario as needed (e.g. "start a match with 2 teams of 4 worms each, CPU vs CPU")
16
+
17
+ The debug server listens on `127.0.0.1:19840` (TCP). It runs inside the injected DLL as a background thread.
18
+
19
+ **Important:** The server runs inside the DLL. If you change protocol types (Request/Response enums in `openwa-debug-proto`), the game must be restarted to load the new DLL. CLI-only changes (parsing, display) don't require a restart.
20
+
21
+ ## Commands
22
+
23
+ ### ping
24
+
25
+ Check if the debug server is running.
26
+
27
+ ```bash
28
+ target/i686-pc-windows-msvc/release/openwa-debug ping
29
+ ```
30
+
31
+ Returns `pong` if connected. Use this to verify the server is up before issuing read commands.
32
+
33
+ ### help
34
+
35
+ List available server-side commands with usage info.
36
+
37
+ ```bash
38
+ target/i686-pc-windows-msvc/release/openwa-debug help
39
+ ```
40
+
41
+ ### read
42
+
43
+ Read memory at an address. ASLR rebasing is handled automatically for Ghidra addresses.
44
+
45
+ ```bash
46
+ target/i686-pc-windows-msvc/release/openwa-debug read <addr> [len] [--format hex|raw]
47
+ ```
48
+
49
+ **Arguments:**
50
+ - `<addr>` (required): Address expression (see syntax below). Accepts hex (`0x56E220`) or decimal.
51
+ - `[len]` (optional): Bytes to read. Default: 256. Max: 1 MB.
52
+ - `--format hex` (default): Hex dump with ASCII sidebar + pointer annotations.
53
+ - `--format raw`: Binary output, suitable for piping to a file.
54
+ - `--port N`: Override default port 19840.
55
+
56
+ ## Address Syntax
57
+
58
+ All address forms can be used with the `read` command. **Always quote the address argument** to prevent shell interpretation.
59
+
60
+ | Syntax | Meaning |
61
+ |--------|---------|
62
+ | `0x669F8C` | Ghidra VA (ASLR-rebased automatically) |
63
+ | `abs:0x7FFF0000` | Absolute runtime address (no rebase) |
64
+ | `0x669F8C+0x10` | Address + hex offset |
65
+ | `0x669F8C+16` | Address + decimal offset |
66
+ | `0x669F8C[0x10]` | Bracket notation (same as +0x10) |
67
+ | `"0x7A0884->0xA0->0x2C"` | Pointer chain (quote required!) |
68
+ | `"0x7A0884->0xA0+0x10->0x0"` | Chain with compound offset in segment |
69
+
70
+ ### Pointer Chains
71
+
72
+ **You MUST quote chain addresses** β€” `>` is a shell redirect character:
73
+ ```bash
74
+ openwa-debug read "0x7A0884->0xA0->0x488" 64 # correct
75
+ openwa-debug read 0x7A0884->0xA0->0x488 64 # WRONG β€” shell eats >
76
+ ```
77
+ The CLI detects truncated addresses (ending with `-`) and warns about missing quotes.
78
+
79
+ Chain syntax `addr->offset1->offset2->...` walks a pointer chain server-side:
80
+
81
+ 1. Read DWORD at `addr` to get pointer P1
82
+ 2. Compute P1 + offset1, read DWORD to get pointer P2
83
+ 3. Compute P2 + offset2 β€” this is the final address
84
+ 4. Display memory at the final address
85
+
86
+ Each `->N` means "dereference current address, then add N." The last step produces the final address (no deref).
87
+
88
+ **Compound offsets work in every segment** β€” both the start address and chain segments support `+offset` and `[offset]`:
89
+ ```bash
90
+ openwa-debug read "0x7A0884->0xA0->0x488+0x10" # deref, add 0x498
91
+ openwa-debug read "0x7A0884->0xA0->0x488->0x10" # deref, add 0x488, deref again, add 0x10
92
+ ```
93
+ Note: `->0x488+0x10` is ONE step (deref then add 0x498), while `->0x488->0x10` is TWO steps (deref+0x488, deref+0x10). Choose based on whether you need an extra dereference.
94
+
95
+ The output shows each step of the chain walk before the hex dump, so you can verify intermediate pointers.
96
+
97
+ ## Key Address Chains
98
+
99
+ These chains are useful starting points for inspecting game state:
100
+
101
+ ```bash
102
+ # DDGame (the main game object):
103
+ # g_GameSession(0x7A0884) -> DDGameWrapper at +0xA0 -> DDGame at +0x488
104
+ openwa-debug read "0x7A0884->0xA0->0x488" 0x100
105
+
106
+ # DDGame sub-field (e.g. weapon_table pointer at DDGame+0x510):
107
+ openwa-debug read "0x7A0884->0xA0->0x488->0x510" 4
108
+
109
+ # Weapon table contents (deref the pointer, read raw entries):
110
+ # Get pointer first, then use abs: with the result
111
+ openwa-debug read "0x7A0884->0xA0->0x488->0x510" 4
112
+ openwa-debug read "abs:<ptr_from_above>" 0x80B0 --format raw > /tmp/weapon_table.bin
113
+ ```
114
+
115
+ **Common mistake:** `0x7A0884->0xA0->0x0` does NOT reach DDGame. It reads DDGameWrapper+0x0 (the vtable). DDGame is at DDGameWrapper+0x488.
116
+
117
+ ## Output
118
+
119
+ **Hex format (default):**
120
+ 1. Header line showing Ghidra address and runtime (ASLR-rebased) address
121
+ 2. Hex dump with 16 bytes per line and ASCII sidebar
122
+ 3. Pointer annotations: for each DWORD that looks like a pointer, shows offset, raw value, Ghidra value, classification, and detail
123
+
124
+ **Chain output** additionally shows a trace of each deref step before the hex dump.
125
+
126
+ **Pointer classifications:**
127
+ - `VTABLE` β€” points to .rdata section (likely a vtable). Detail shows `vt[0]` (first virtual method address)
128
+ - `CODE` β€” points to .text section (function pointer or return address)
129
+ - `DATA` β€” points to .data/.bss section (global variable)
130
+ - `OBJECT` β€” heap pointer whose first DWORD is a vtable. Detail shows vtable + vt[0] addresses
131
+ - `HEAP` β€” readable heap pointer without a vtable. Detail shows dereferenced value
132
+
133
+ ## Common Patterns
134
+
135
+ ### Inspect a struct via pointer chain
136
+
137
+ ```bash
138
+ # Follow g_GameSession -> DDGameWrapper -> DDGame, read 0x100 bytes
139
+ openwa-debug read "0x7A0884->0xA0->0x488" 0x100
140
+
141
+ # Read team arena state (DDGame+0x4628)
142
+ openwa-debug read "0x7A0884->0xA0->0x488->0x4628" 0x200
143
+ ```
144
+
145
+ ### Dump a large region for offline analysis
146
+
147
+ ```bash
148
+ # Dump weapon table (71 x 0x1D0 = 0x80B0 bytes) to file
149
+ openwa-debug read "abs:<weapon_table_ptr>" 0x80B0 --format raw > /tmp/weapon_table.bin
150
+
151
+ # Analyze with Python script
152
+ python tools/analyze_weapon_table.py /tmp/weapon_table.bin
153
+ ```
154
+
155
+ ### Read at an absolute heap address
156
+
157
+ ```bash
158
+ # If you know a runtime address from the debug UI or a log
159
+ openwa-debug read "abs:0x09AB1234" 64
160
+ ```
161
+
162
+ ### Inspect a vtable
163
+
164
+ ```bash
165
+ # CTask vtable (8 method pointers = 32 bytes)
166
+ openwa-debug read 0x669F8C 32
167
+ ```
168
+
169
+ ### Validate a struct field
170
+
171
+ Read the struct base, check that the DWORD at the expected offset matches what you expect (vtable address, known constant, pointer kind).
172
+
173
+ ## Limitations
174
+
175
+ - Cannot write memory, only read.
176
+ - Single-client: one connection at a time.
177
+ - 10-second read timeout per connection.
178
+ - Chain walks stop on NULL pointers with an error message.
179
+ - Some game data (e.g. WeaponTable) is only initialized after a match starts. If you get NULL pointers, ensure you're in-game, not at the main menu.
180
+
181
+ ## Notes
182
+
183
+ - The debug server is enabled by `OPENWA_DEBUG_SERVER=1` env var (set automatically by `start-debug.ps1`)
184
+ - The debug UI (egui overlay with entity census, struct inspector, cheats) is enabled by `OPENWA_DEBUG_UI=1` + the `debug-ui` cargo feature
185
+ - Both are independent β€” you can use the CLI without the UI and vice versa
186
+ - Protocol: MessagePack over TCP with 4-byte LE length-prefix framing
187
+ - The `openwa-debug` binary is built to `target/i686-pc-windows-msvc/release/openwa-debug.exe`
188
+ - Source: `crates/openwa-debug-cli/` (CLI), `crates/openwa-debug-proto/` (protocol), `crates/openwa-wormkit/src/debug_server.rs` (server)