$npx -y skills add meltedinhex/analyst-ai-pack --skill debugging-malware-with-x64dbgUses x64dbg to dynamically debug Windows malware: setting strategic breakpoints
| 1 | # Debugging Malware with x64dbg |
| 2 | |
| 3 | ## When to Use |
| 4 | |
| 5 | - You need to observe a sample's runtime behavior at the instruction level on Windows. |
| 6 | - You are manually unpacking, decrypting strings, or reaching code guarded by anti-analysis. |
| 7 | - You want to dump a payload from memory after it is decoded but before it is hidden. |
| 8 | |
| 9 | **Do not use** a debugger on a host you care about — debug only inside the isolated victim VM, |
| 10 | and revert the snapshot after. |
| 11 | |
| 12 | ## Prerequisites |
| 13 | |
| 14 | - x64dbg (and the matching x32/x64 build for the sample's bitness) inside the victim VM. |
| 15 | - A clean snapshot; the lab's simulated internet. |
| 16 | - Familiarity with Windows APIs commonly used by malware. |
| 17 | |
| 18 | ## Safety & Handling |
| 19 | |
| 20 | - The sample executes under the debugger; this is detonation. Stay in the isolated VM. |
| 21 | - Revert the snapshot after the session; treat memory dumps as live samples. |
| 22 | |
| 23 | ## Workflow |
| 24 | |
| 25 | ### Step 1: Set strategic API breakpoints |
| 26 | |
| 27 | Rather than stepping from the entry point, break on APIs that mark interesting moments: |
| 28 | |
| 29 | ```text |
| 30 | VirtualAlloc / VirtualProtect -> unpacking buffer about to be written/executed |
| 31 | CreateProcessInternalW -> process hollowing target |
| 32 | WriteProcessMemory -> injection payload in a register/buffer |
| 33 | CryptDecrypt / lstrcpy -> decoded data available |
| 34 | ResumeThread -> hollowed process about to run |
| 35 | ``` |
| 36 | |
| 37 | Use `bp VirtualAlloc` then inspect the return value (allocated base) on return. |
| 38 | |
| 39 | ### Step 2: Step through unpacking |
| 40 | |
| 41 | Run to the allocation, set a memory breakpoint on the new region, and continue until the |
| 42 | unpacker writes and jumps to it. The tail jump to OEP marks the unpacked entry. |
| 43 | |
| 44 | ### Step 3: Dump at the right moment |
| 45 | |
| 46 | When the payload is decoded in memory (e.g. after `VirtualProtect` makes it executable), dump |
| 47 | the region with Scylla/the dump plugin and fix the import table for the unpacked PE. |
| 48 | |
| 49 | ### Step 4: Defeat simple anti-debugging |
| 50 | |
| 51 | Patch or skip checks like `IsDebuggerPresent`, PEB `BeingDebugged`, and timing checks (see the |
| 52 | anti-debugging skill). Set the return value to evade detection rather than removing the call. |
| 53 | |
| 54 | ### Step 5: Record breakpoints and notes |
| 55 | |
| 56 | Save a breakpoint plan and observations. The bundled script generates an x64dbg command |
| 57 | script of API breakpoints to bootstrap a session. |
| 58 | |
| 59 | ```bash |
| 60 | python scripts/analyst.py breakpoints --preset unpacking > bp.txt |
| 61 | ``` |
| 62 | |
| 63 | ## Validation |
| 64 | |
| 65 | - The dumped region is a valid PE (MZ/PE headers) and disassembles to real code. |
| 66 | - API breakpoints fire in an order consistent with unpacking/injection. |
| 67 | - Patched anti-debug checks no longer alter the execution path. |
| 68 | |
| 69 | ## Pitfalls |
| 70 | |
| 71 | - Stepping blindly from the entry point instead of using API breakpoints — slow and easy to |
| 72 | get lost. |
| 73 | - Dumping too early (still encrypted) or too late (already executed/freed). |
| 74 | - Removing anti-debug calls entirely, which can break control flow; prefer faking the result. |
| 75 | |
| 76 | ## References |
| 77 | |
| 78 | - See [`references/api-reference.md`](references/api-reference.md) for the breakpoint-script |
| 79 | generator. |
| 80 | - x64dbg and Windows API documentation (linked in frontmatter). |