1.8 KiB
ACE patch (Windows, clients ≤ 8.9)
Builds a patched BH3.exe from a capture of the clean client's
initialized state. Only BH3.exe is written, without a backup. 9.0+
clients use HoYoKProtect instead.
Discovery (ace/discovery.rs)
Read-only; all checks must pass:
| File | Required layout |
|---|---|
BH3.exe |
7 sections all named .ace*, 4 KiB section alignment, no TLS directory, entry calls the BH3Base.dll ordinal-1 import |
BH3_Data/sharedassets0.assets |
ChannelConfig resolves to a known region |
UnityPlayer.dll |
exports UnityMain; three writable decoder globals found via a .text code pattern |
BH3_Data/Native/UserAssembly.dll |
exports il2cpp_get_api_table; executable .tvm0 section with a virtual tail larger than its raw size |
Capture (ace/capture.rs, ace/process.rs)
After confirmation, launches the client (Windows directly; Linux via
umu-run + Proton) and reads its memory read-only — no injection or
protection tampering. Once the state is stable for 2 s (120 s timeout),
it captures the .tvm0 section, the three decoder states and the
committed private executable allocations.
Normalize and rebuild (ace/runtime.rs, ace/generate.rs)
runtime.rs checks the captured .tvm0 against the original after PE
relocations, resolves FF 25 jump slots against the captured
allocations, and emits a position-independent blob and two thunk tables.
generate.rs appends .bh3code (the bootstrap.bin stub, patched with
the discovered RVAs/sizes per bootstrap.json) and .bh3imp (imports,
decoder states and the runtime blob), then repoints the entry point.
A HI3AUTO1 receipt with SHA-256 hashes of the patched image,
UnityPlayer.dll and UserAssembly.dll is appended; a repeat run that
validates it is a no-op. Written via util::replace::verified.