Skip to content

Commit da2647d

Browse files
committed
Update SKILL.md
1 parent 88bd8fe commit da2647d

1 file changed

Lines changed: 49 additions & 5 deletions

File tree

  • .trae/skills/检查钩子-check-hooks

.trae/skills/检查钩子-check-hooks/SKILL.md

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: 检查钩子 Check Hooks
3-
description: "Checks newly added Syringe hooks (DEFINE_HOOK / DEFINE_HOOK_AGAIN) on the current branch for common errors: insufficient size (< 5 bytes), conflicts with hooks from other engine extensions, instruction boundary misalignment, and register/stack variable extraction issues. Uses HookAnalysis.log for conflict detection and IDA MCP for deep instruction analysis."
3+
description: "Checks newly added Syringe hooks (DEFINE_HOOK / DEFINE_HOOK_AGAIN) on the current branch for common errors: insufficient size (< 5 bytes), conflicts with hooks from other engine extensions, instruction boundary misalignment, relative instruction coverage (jumps/calls with relative offsets and RIP-relative addressing), and register/stack variable extraction issues (GET_STACK vs GET_BASE, stack alignment). Uses HookAnalysis.log for conflict detection and IDA MCP for deep instruction analysis."
44
---
55

66
#### Helper Scripts
@@ -16,7 +16,7 @@ All scripts in this directory. The AI MUST use them — do not reimplement parsi
1616

1717
#### Workflow
1818

19-
This skill checks all newly added `DEFINE_HOOK` and `DEFINE_HOOK_AGAIN` macro invocations on the current branch for four classes of problems.
19+
This skill checks all newly added `DEFINE_HOOK` and `DEFINE_HOOK_AGAIN` macro invocations on the current branch for the following problem classes.
2020

2121
**Step 0: Discover new hooks**
2222

@@ -128,6 +128,32 @@ If any check fails:
128128
> **Problem 2: Instruction boundary issue**
129129
> Hook `HookName` at `0x<addr>` (size `0x<size>`) — <specific issue, e.g. "address is in the middle of an instruction" or "size does not end at an instruction boundary" or "return address 0x<ret> is not at an instruction start">. Disassemble the area at this address to find the correct boundaries.
130130
131+
**Problem 2b — Relative instruction coverage check:**
132+
133+
For each new hook, disassemble the full address range `[addr, addr + size)` and check for any relative-offset instructions. These instructions encode their target as `current_address + instruction_length + relative_offset`. When Syringe copies these bytes to a trampoline at a different address, the relative offset points to the wrong location, causing control flow to jump/call to an unintended address.
134+
135+
Hooks that cover any of the following instructions **MUST return a fixed value (not 0)** — returning 0 would execute the trampolined original code and trigger the bug:
136+
137+
Relative jump/call instructions:
138+
- `jmp short`, `jmp near`, `jz`, `jnz`, `je`, `jne`, `jg`, `jge`, `jl`, `jle`
139+
- `ja`, `jae`, `jb`, `jbe`, `jo`, `jno`, `js`, `jns`, `jp`, `jnp`, `jpe`, `jpo`
140+
- `jcxz`, `jecxz`, `jrcxz`
141+
- `call` (relative call, i.e. `call rel32`)
142+
- `loop`, `loope`, `loopne`, `loopz`, `loopnz`
143+
- `xbegin`
144+
145+
RIP-relative addressing instructions (x64 style, but also relevant for x86 with `[eip+disp32]`):
146+
- `mov`, `lea`, `cmp`, `add`, `sub`, `and`, `or`, `xor`, `test`
147+
- `push`, `pop`, `movsxd`, `movzx`, `movsx`
148+
- When any of the above uses RIP-relative addressing mode
149+
150+
Use IDA MCP to disassemble each instruction in the hook range and check for these patterns. Check the `returns` field from Step 0. If the hook returns `"0"` but covers any of these instructions, report:
151+
152+
> **Problem 2b: Hook covers relative-offset instruction but returns 0**
153+
> Hook `HookName` at `0x<addr>` covers instruction `<mnemonic>` at `0x<instruction_addr>` which uses relative addressing (encoded relative offset `<encoded_value>` → target `<computed_target>`). When this instruction is copied to a trampoline, the relative offset will point to the wrong address. This hook MUST return a fixed value (e.g. `return 0x<fixed_addr>` or `return R->Origin() + <offset>`), not `return 0`.
154+
155+
If no relative-offset instructions are found, or the hook already returns a fixed value: "✓ No relative instruction issues found."
156+
131157
**Problem 3 — Variable extraction validation:**
132158

133159
For each new hook, inspect the function body for `GET`, `GET_STACK`, `REF_STACK`, `LEA_STACK` macros and register writes (`R->EAX(value)`, `R->ECX(value)`, `R->STACK(offset, value)`, etc.). Use IDA MCP to decompile or disassemble the code around the hook address and verify the register/stack state matches.
@@ -137,12 +163,30 @@ For `GET(type, var, reg)`:
137163
- If the type declared in GET differs from what IDA suggests, warn the user
138164

139165
For `GET_STACK(type, var, offset)` / `REF_STACK(type, var, offset)`:
140-
- Check the stack layout at the hook point per IDA
141-
- If the offset suggests a different type or value, warn the user
166+
- **Do NOT rely on IDA's offset labels alone** — they may reference a virtual frame pointer that is not ESP. `R->Stack` always reads from `captured_ESP`, so you MUST compute the actual ESP at the hook point.
167+
- **Verify EBP is a real frame pointer:** Disassemble the first 5-10 instructions of the function. If EBP is overwritten (e.g. `mov ebp, [esp+...]`) instead of set to `mov ebp, esp`, then EBP is NOT a frame pointer — do NOT use EBP-relative offsets from IDA as ESP offsets.
168+
- **Trace ESP from function entry to the hook point.** Manually compute the cumulative offset:
169+
170+
1. Start from `ESP_entry` — the ESP value immediately after the `call` that entered the function (i.e. the stack pointer at function entry, with the return address already pushed by `call`)
171+
2. Account for every `push` (subtract 4 per push), `pop` (add 4 per pop), and `sub esp, X` / `add esp, X` between function entry and the hook address
172+
3. The resulting ESP at the hook point = `ESP_entry + cumulative_offset` (where cumulative_offset is negative for pushes/subs, positive for pops/adds)
173+
4. The offset passed to `R->Stack` (after resolving `STACK_OFFSET` macros) is then added to this value
174+
175+
- **Resolve `STACK_OFFSET` macros explicitly.** `STACK_OFFSET(a, b)` is simply `(a + b)` — it is pure addition. Do NOT assign special semantics to the parameter names `cur_offset` or `wanted_offset`. Compute the numeric result before using it.
176+
- **Map the final address back to the function's parameter list.** Once you have `captured_ESP + resolved_offset`, subtract `ESP_entry` to get the offset from the function entry point. This tells you which parameter slot or local variable the macro is accessing. Cross-reference with the function's signature (parameters start at `ESP_entry + 0x04` for the first param after the return address).
177+
178+
- **Check for stack alignment (`and esp, alignment_mask`) in the function prologue.** Disassemble the first ~20 instructions of the function. Look for `and esp, <mask>` where `<mask>` is an alignment boundary (e.g. `0FFFFFFF8h` for 8-byte, `0FFFFFFF0h` for 16-byte, `0FFFFFFFCh` for 4-byte, or their equivalents `-8`, `-16`, `-4`). After this instruction, ESP is aligned down to the mask boundary and is no longer at a known offset from `ESP_entry``GET_STACK` cannot reliably access function parameters. In this case, the hook MUST use `GET_BASE(type, name, offset)` instead of `GET_STACK(type, name, STACK_OFFSET(...))` for any parameter access. If a hook uses `GET_STACK` to access a parameter (offset maps to `ESP_entry + positive_offset`) and the function prologue contains stack alignment, report:
179+
180+
> **Problem 3: GET_STACK used after stack alignment — should use GET_BASE**
181+
> Hook `HookName` at `0x<addr>` uses `GET_STACK` to access what appears to be a function parameter (resolved to entry offset `+<entry_offset>`). The function prologue at `0x<prologue_addr>` contains `and esp, <alignment_mask>` which realigns ESP, making `GET_STACK` unreliable for parameter access. Replace with `GET_BASE(type, name, <entry_offset - 4>)` (the offset from EBP after a standard `push ebp; mov ebp, esp` frame, adjusting for the alignment loss).
142182
143183
If a mismatch is found:
144184
> ⚠️ **Problem 3: Variable extraction may be incorrect**
145-
> At `0x<addr>`: `GET(<type>, <var>, <reg>)``<reg>` appears to hold `<actual_type>` based on IDA analysis. Verify the register assignment at this address.
185+
> At `0x<addr>`: `GET_STACK(<type>, <var>, <offset>)` — resolved offset `<computed_offset>` maps to function entry `+<entry_offset>`, expected parameter `<param_name>` of type `<expected_type>` at that position. The declared type `<declared_type>` does not match.
186+
187+
For register writes like `R->EAX(value)`:
188+
- Disassemble after the hook point to verify the register will be read as expected by the original code
189+
- If the return address is a fixed address (not `R->Origin()`), verify the original code at that address uses the register being set
146190

147191
If all Problem 3 checks pass: "✓ Variable extraction checks passed."
148192

0 commit comments

Comments
 (0)