Under GDB, a call to an unsupported API logs a diagnostic and ends the run, so the user cannot add a hook or redirect the call. The timeout does not apply while a debugger is attached. Under GDB, a preflight now stops before the unsupported call is recorded, with the original frame suspended. The user can inspect it, add a hook, change PC or patch the entry, and then resume. The per-run timeout applies under the debugger and excludes the time that execution is paused.
6.7 KiB
GDB Debugging
Speakeasy supports interactive debugging of emulated binaries via the GDB Remote Serial Protocol. When enabled, the emulator pauses before the first instruction and waits for a GDB client to connect. You can then set breakpoints, inspect registers and memory, single-step, and continue execution — all through a standard GDB interface.
Synthesized Windows API pointers returned by GetProcAddress, bound imports, and export-table walks refer to mapped executable entries. Set a breakpoint directly on that pointer; stepping executes its public bytes before handler dispatch. See Unified Windows API addresses for entry layouts, dynamic-only symbols and unsupported-call stops.
Quick Start
1. Start speakeasy with GDB enabled
speakeasy -t sample.exe --gdb
This starts the GDB server on the default port (1234). Speakeasy will print a message and block until a GDB client connects:
Native GDB server listening on 127.0.0.1:1234, waiting for connection...
To use a different port:
speakeasy -t sample.exe --gdb --gdb-port 9999
The server binds to loopback by default. To allow a debugger on another machine to connect, explicitly choose a bind address:
speakeasy -t sample.exe --gdb --gdb-host 0.0.0.0 --gdb-port 9999
Warning: GDB RSP has no authentication and permits target memory and register modification. Only use a non-loopback
--gdb-hoston a trusted network or behind an appropriate firewall/tunnel.
--gdbautomatically enables--no-mpbecause the server must remain attached to the same Unicorn instance.
2. Connect with GDB
In another terminal, connect using gdb-multiarch (or your platform's GDB):
For a 32-bit x86 binary:
$ gdb-multiarch
(gdb) set architecture i386
(gdb) target remote localhost:1234
For a 64-bit x86-64 binary:
$ gdb-multiarch
(gdb) set architecture i386:x86-64
(gdb) target remote localhost:1234
Once connected, GDB will show the current stop position. From here you can debug normally.
3. Debug
Standard GDB commands work:
(gdb) info registers
(gdb) x/10i $pc
(gdb) break *0x10001000
(gdb) continue
(gdb) stepi
(gdb) x/32xb $esp
Examples
Debugging a DLL
speakeasy -t malware.dll --gdb --gdb-port 1234
$ gdb-multiarch
(gdb) set architecture i386
(gdb) target remote localhost:1234
(gdb) break *0x10001000
(gdb) continue
Breakpoint 1, 0x10001000 in ?? ()
(gdb) info registers
(gdb) x/10i $pc
Debugging shellcode
speakeasy --target shellcode.bin --raw --arch x86 --gdb
$ gdb-multiarch
(gdb) set architecture i386
(gdb) target remote localhost:1234
(gdb) stepi
(gdb) info registers
Debugging a kernel driver
speakeasy -t rootkit.sys --gdb --gdb-port 4444
$ gdb-multiarch
(gdb) set architecture i386:x86-64
(gdb) target remote localhost:4444
(gdb) continue
Using with IDA Pro
IDA Pro's built-in GDB debugger can connect to the speakeasy GDB stub:
- Start speakeasy with
--gdb - In IDA, go to Debugger > Select debugger > Remote GDB debugger
- Set Hostname to
localhostand Port to1234(or your chosen port) - Click Debugger > Attach to process or Start process
Programmatic Usage
The GDB server can also be enabled when using speakeasy as a library:
import speakeasy
se = speakeasy.Speakeasy(gdb_port=1234, gdb_host="127.0.0.1")
module = se.load_module("sample.dll")
# This will block waiting for GDB to connect before emulating
se.run_module(module)
How It Works
Speakeasy implements the all-stop GDB Remote Serial Protocol directly. The server provides register and memory access, software and hardware breakpoints, watchpoints, single-step, asynchronous Ctrl-C pause, and the Windows process metadata used by IDA:
qXfer:libraries:readreturns all mapped Speakeasy PE modules; mapping one while the target runs (for example throughLoadLibrary) halts it with thelibrarystop reason so the client re-reads the listqXfer:exec-file:readreturns the emulated executable pathqXfer:threads:read,qfThreadInfo, andqGetTIBAddrdescribe the active emulated threadqXfer:features:readreports a register layout matching the emulated architectureqXfer:memory-map:readreports Unicorn's mapped memory regionsvCont, legacy resume packets, register/memory writes, detach, and process exit are supported- when emulation ends without a fault, the server halts the target with a
T05stop reply before it sendsW00. The client can then read the final memory, for example to dump an unpacked image afterExitProcess.
A socket reader handles RSP framing and can stop Unicorn asynchronously when it receives Ctrl-C. Breakpoint and watchpoint hooks only record a stop reason and stop Unicorn; protocol processing happens after emu_start() returns, rather than from inside a Unicorn callback.
Supported protocol subset
The server implements the all-stop RSP subset exercised by GDB and IDA:
| Area | Packets |
|---|---|
| Transport | checksums, ACK/NACK retransmission, QStartNoAckMode, binary escaping, raw Ctrl-C |
| Target discovery | qSupported, qXfer:features:read, qXfer:memory-map:read |
| Windows metadata | qXfer:libraries:read, qXfer:exec-file:read, qXfer:threads:read, qGetTIBAddr |
| Thread selection | qC, qfThreadInfo, qsThreadInfo, H, T for the synthetic active thread |
| Registers | g, G, p, P |
| Memory | m, M, X |
| Breakpoints | Z0/z0, Z1/z1, and read/write/access watchpoints |
| Execution | c, s, vCont, asynchronous Ctrl-C, ?, swbreak/hwbreak/watchpoint/library stop replies |
| Lifecycle | D, k, a final T05 stop before exit, and Wxx exit replies |
Unsupported optional packets receive an empty response as required by RSP. Non-stop mode, multiprocess mode, reverse execution, and remote file I/O are not advertised.
Limitations
- one debugger client can connect to a Speakeasy instance
- Speakeasy models Windows thread objects but executes one Unicorn CPU context at a time; the server therefore presents the currently active execution context as one synthetic GDB thread
- breakpoints persist across runs: Speakeasy executes multiple runs (for example TLS callbacks, the module entry point, and exports)
- modules stay mapped for the life of the emulation, so the server reports library loads but never unloads
- reverse execution, non-stop mode, and multiprocess RSP are not implemented