3.8 KiB
Reverse-engineering notes
Carried over from slinkyfullsrc/SRC.md. None of this is needed to build the
module sources.
.slinky0 — engine / module manager
slinky2.decrypted/slinky0.bin is 14 255 293 bytes (.slinky0 of the client
image). Region map (verified):
| range | content |
|---|---|
0x000000–0x77FFFF |
native code + data (MSVC CRT/RTTI strings at 0x2d000–0x3c000, engine code; clean x86-64, ~30 % printable) |
0x780000–0x8BFFFF |
high-entropy (7.99) blob — encrypted/compressed, not consumed by visible code |
0x8C0000–0x9FFFFF |
sorted (u32 rva, u16 size) table, 213 035 records, covers 0x36300E–0x867551, sizes multiple of 8, dense (≈50 % of the region), deltas 2–16 bytes, overlap → looks like a per-site patch/relocation or checksum map for the code region |
0xA00000–0xAA0000 |
table A, XOR 00 10 00 62 75 86, 109 225 × (u32 off, u16 size); offsets 0x223–0x27FFB4 fit inside the tail block; sizes ∈ {0,8,24,32,…,120} |
0xAA0000–0xAB0000 |
table B, XOR 83 A7 00 10 00 53, same 6-byte record shape |
0xAB0000–0xD80000 |
tail_high.bin — 2 949 120 bytes, entropy 7.98, no standard container/PE; table A indexes into it |
0xD80000–0xD984BD |
tail_code.bin (~100 KB), mixed code/data |
Module-manager data model (recovered from the client image, used by the reconstructed sources):
- Module descriptor in
.data(.rdatastring pointers + a factory/log function at+0x58).modules_real.jsonlists all 67 (61 with a GUI cb). - Per-module settings block in
.data, variable stride, begins with three function pointers{gui_cb, register_settings, aux}followed by the widget cfg objects (CfgBool/CfgSlider/CfgRange/CfgEnum/CfgColor/...). R_*registration calls are made from the client into the engine manager at DLL init (onereg_<addr>function per module), theW_*calls render the settings pane.
The module-side functions that read the settings and implement each feature
are in src/logic/ (see docs/logic.md); they were extracted 1:1 from .text
by tools/extract_logic.py.
The manager itself is in client .text, not .slinky0: constructor 0x28000,
dispatch 0x285d0, descriptor table 0x14f810. It groups modules by category
and by the event callback slots at descriptor+0x48 … +0x1F8, and dispatches
the enabled callbacks. See docs/engine.md for the full picture, including
the six engine-only modules. .slinky0 has no static RIP-relative
references into the client's .rdata/.data/.text (checked: 0 refs), so
it receives whatever it needs through runtime-installed pointers.
tail_high.bin
tail_high.bin is still packed. Findings:
- Not a PE, not ZIP/7z/RAR/gzip/zlib/LZMA (standard headers checked).
- Entropy 7.978; byte histogram ≈ uniform; no repeated 24-byte blocks.
- Table A (109 225 records) and the sorted table (213 035 records) both use the
same
(u32 offset, u16 size)record form, sizes are multiples of 8, and both point into their subject buffers with heavy overlap — this is a site map (patch/relocation/checksum), not a container directory. - The runtime copy of
.slinky0captured inslinky.dllis byte-identical to the static stream up to0xC4B658and zero beyond, i.e. that dump was taken before the engine performed the tail transformation; it gives no plaintext. - Therefore decryption requires either (a) implementing the protector's
per-site transform from the
0x8C0000table, or (b) a live Wine trace of the first execution that touchesslinky0+0xAB0000(same breakpoint technique that recovered the.slinky2LZMA streams).
Decrypting tail_high.bin is not required for anything in this tree: the
module callbacks/registrations are plain .text, and the registry data model
is plain .data. It is required only for the remainder of the engine internals.