pe_emu
Tool
tools/emu/pe_emu.py(library, Emulators). Call functions inside a Windows x64 game's EXE and DLLs under Unicorn, without running the game.
A library: other tools and your own scripts import it. Source on GitHub.
Its own help
This is the help text in the tool itself, as ./gameframe tool pe_emu prints it.
text
Call functions inside a Windows x64 game's EXE and DLLs under Unicorn, without running the game.
This is how you get *ground truth*: the exact outputs of an original function for inputs you
choose, to test a reimplementation against bit for bit.
from pe_emu import Emu, F32
emu = Emu('game.exe', ['engine.dll']) # load, relocate if needed, link imports
emu.hook_export('engine.dll', 'mem_alloc', lambda a: emu.malloc(a[0]))
out = emu.malloc(4 * 600)
emu.call(emu.export('game.exe', 'simulate'), 1234, 600, out)
print(emu.f32s(out, 600)[-1])
What it does:
- maps every module at its preferred base (or relocates it when two overlap) and writes the
image as the Windows loader would;
- links each import to an export of another loaded module by name or ordinal; any other import
becomes a stub that runs a Python handler (common C runtime and kernel32 functions have
working handlers in `CRT_STUBS`; an import with no handler stops the run with its name, so you
know exactly what to stub next);
- gives the emulated code a stack, a bump-allocator heap and a TEB (GS segment), so thread-local
reads and stack checks work;
- `call(addr, *args)` uses the Windows x64 convention: the first four integer or pointer
arguments in rcx, rdx, r8, r9, floats wrapped in `F32` in xmm0-xmm3, the rest on the stack
above 32 bytes of shadow space. It returns rax; `call_f32` returns xmm0 as a float.
What it does not do: run DllMain or TLS callbacks, create threads, open files or windows, or
talk to Direct3D. Hook or stub anything like that (`hook_function`). Needs `unicorn` and
`pefile` (scripts/setup.sh puts them in .venv).Named by
Skills: re-observe, re-emulate.