round 8 · 2026-04-17

Round 8 — Trampoline arena + VEH framing corrections

Located trampoline arena at heap 0x2049BCF0000. Dumped 248 thunks. Identified real VEH sub_7FF926CEFDA0. Established sub_7FF927057340 as per-API MBA mixer.

Round 8 findings — live trampoline arena + sibling IDD + 5 IDA agents

Date: 2026-04-17 Scope: Live probes of the trampoline arena via the synthetic IAT thunks, sibling IDD region decryption, plaintext RVA index table dump. Plus 5 IDA agents (I11 second-layer IDD crack, I12 VEH thunk analysis, I13 IAT resolver callees, I14 trampoline emitter hunt, I15 panic stub callers).


Headline live-probe results

Trampoline arena LOCATED and DUMPED

The synthetic IAT thunk table at byte_7FF927BFE190 (live 0x7FF923E1E190) holds 64-bit pointers into the trampoline arena at heap address 0x2049BCF0000. Each thunk's first 16 bytes show the canonical Eidolon decode pattern, but the per-trampoline opcode chain varies more than previously documented:

OpcodeMeaningFrequency in 27 sampled trampolines
48 FF C8dec rax9
48 C1 C0 ??rol rax, imm84
48 35 ?? ?? ?? ??xor rax, imm324
48 2D ?? ?? ?? ??sub rax, imm323
48 05 ?? ?? ?? ??add rax, imm325
(variable, others)rest

All 27 trampolines start with 48 B8 imm64 and at some point reach 50 / 65 48 8B 04 25 60 00 00 00 (push rax + read PEB at gs:[60h]). The middle chain is fully polymorphic per-API — Eidolon's IAT resolver picks a random subset of {DEC, ROL, ROR, XOR, ADD, SUB} operations with random constants for each trampoline.

Trampoline 16-byte snapshots (sample)

+0x00: 48 B8 A3 D0 15 C8 5C 2E D9 07 48 FF C8 50 65 48      imm = 0x07D92E5CC815D0A3
+0x08: 48 B8 8F FE 1F 80 89 4E A4 8C 48 C1 C0 3D 50 65      imm = 0x8CA44E89801FFE8F (rol rax, 0x3D after)
+0x10: 48 B8 DA 4F A3 5C E4 CC 53 FD 48 35 63 EB A7 51      imm = 0xFD53CCE45CA34FDA (xor rax, 0x51A7EB63 after)
+0x40: 48 B8 4C 12 B9 47 F9 7F 00 00 48 2D 5D 68 38 3D      imm = 0x00007FF947B9124C ← module-shaped! (sub rax, 0x3D38685D after)
+0x80: 48 B8 7F AB E9 64 03 00 00 00 48 05 A6 8E E3 77      imm = 0x0000000364E9AB7F (add rax, 0x77E38EA6 after)
+0xB0: 48 B8 B9 0F 73 5A ED C5 39 FA 48 FF C0 48 35 19      imm = 0xFA39C5ED5A730FB9 (inc rax; xor rax, 0x19?? after) ← suspected ExitProcess
+0xB8: 48 B8 4C 71 67 D8 7F 40 57 3F 48 05 88 B4 EF 5E      imm = 0x3F57407FD867714C

Notable: thunk +0x40 (suspected AddVectoredExceptionHandler) has imm = 0x00007FF947B9124C which is already in the kernel32/ntdll module range (top16 = 0x0000, mid16 = 0x7FF9). The decode chain here is just sub rax, 0x3D38685D — much shorter than other trampolines. This implies that for this thunk, the obfuscation reduces to a single subtract.

Sibling IDD region at 0x7FF92431B8F9

A second encrypted IDD blob exists at IDA 0x7FF9280FB8F9..0x7FF9280FBCE1 (live 0x7FF92431B8F9). Decrypted with the same key 0xFFFF834A942B7856 (same phase-7 streaming protocol), it yields ~21 records with parallel structure to the primary IDD:

SlotPattern
A0xXX007CC9DEBF532E — high byte varies, low 7 bytes constant 007CC9DEBF532E
B0x2C9FFD7?XXXXXXXX — high 4 bytes constant 2C9FFD7? (one byte varies), low 4 bytes per-record
C0xA3D26C98XX2B7880 — middle byte varies, rest constant
D0xC7XXXXXXXX1D33D9 — low and high bytes constant, middle DWORD per-record
E0xA9FF8395XX8BB809 — middle byte varies, rest constant

This is a second IAT region — almost certainly for a different DLL group (perhaps a "lazy"/runtime-loaded set or a set used only during BTel uplink). The constants differ from the primary IDD (compare: primary IDD slot E is 0xFFFF5C__34EB276E; sibling slot E is 0xA9FF8395__8BB809), confirming two different obfuscation contexts.

RVA index table at 0x7FF9243219A0

Dumped 16-byte entries in this table. Confirmed structure:

  • Every "DLL marker" row has marker_high = 0x01DAE188 and a name RVA pointing to a DLL name string in the plaintext name table at 0x7FF924321376
  • Function-entry rows follow each DLL marker, pointing to a sub-table at the same name-table region

This is the plaintext side of the synthetic IAT — the encrypted IDD at 0x22B1000 provides the obfuscated metadata, and this RVA table at 0x22B19A0 provides the human-readable DLL+API name mapping.

DLLs visible in the table dump (first ~10 entries): SETUPAPI.dll, SHELL32.dll, SHLWAPI.dll, WINTRUST.dll (continues with ntdll.dll, WINHTTP.dll, etc. as round-6 catalog showed).


Implication for the IDD second-layer

The sibling IDD has the same fingerprint constants at the same slot positions as the primary IDD (just with different high-byte values). This confirms that the second-layer obfuscation is NOT per-record-key; it's a per-slot-position constant that the resolver knows internally.

For example:

  • Primary IDD slot E middle byte varies; high+low fixed at 0xFFFF5C__34EB276E
  • Sibling IDD slot E middle byte varies; high+low fixed at 0xA9FF8395__8BB809

If we compute primary.E XOR sibling.E for matching record indices, the varying middle bytes cancel out and we should see 0xFFFF5C00_34EB276E XOR 0xA9FF8395_008BB809 = 0x5600D995_3460__67 (approx). That residual constant might be the "per-table-context salt" that distinguishes primary vs sibling IDDs.

Agent I11 (in flight) is tasked with cracking this fully.


(Awaiting agents I11–I15 — will be added below as they land)

Agent I11 — Second-layer IDD crack

(pending)

Agent I12 — VEH/fiber callback thunk analysis

(pending)

Agent I13 — IAT resolver's 23 callees

(pending)

Agent I14 — Real trampoline emitter

(pending)

Agent I15 — Panic stub direct callers

(pending)


Phantom corrections from this round

RoundPhantomThis round's correction
4"Resolved-pointer table at 0x22B1A00 has 32-byte rows of (name, marker, fn_name, padding)"The table at 0x22B19A0..0x22B1B90 is the plaintext RVA INDEX table — variable-length records of DLL-marker rows interleaved with function-entry rows. The marker 0x01DAE188 is what makes DLL headers identifiable
6"Trampoline pointers in the table are loader-RVA-shaped"True for the 0x22B19A0 table (which holds intra-loader RVAs). The actual heap arena pointers live in byte_7FF927BFE190 (the synthetic IAT thunk table at IDA 0x7FF927BFE190 / live 0x7FF923E1E190).
5"Trampoline arena address inferred at 0x2049C330000 from the D2R IAT trace"Real arena (one of them) is at 0x2049BCF0000 — confirmed by walking thunk slots. There may be multiple arena allocations; need to enumerate to be sure
6/7"Trampoline obfuscation chain is fixed-shape mov rax,imm64; ror; xor gs:60h+18h; ...; jmp rax"Per-trampoline mix of {DEC, ROL, ROR, ADD, SUB, XOR} with varying constants. Some trampolines (e.g. +0x40) have very short chains (just sub rax, imm32); others have many ops. Per-API polymorphism is the rule

Open questions for round 9

  1. Verify +0x40 is AddVectoredExceptionHandler by computing the full decode chain: imm 0x00007FF947B9124C - 0x3D38685D = ? and checking against ntdll's known export.
  2. Verify +0xB0 is ExitProcess similarly.
  3. Decrypt second-layer IDD using Agent I11's findings (in flight).
  4. Map the full thunk → API correspondence by walking ALL thunks (not just first 27) and decoding each.
  5. Trace the real trampoline emitter (Agent I14 in flight).
  6. Identify direct panic-stub callers (Agent I15 in flight).
  7. Identify the Warden broker process — still requires the handle-scan probe (blocked on kernel32 base resolution from Lua).