From 9b8024b132278e41f5c5b38be27c81165e1dfde9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B6=BE=E7=80=AC=E6=A1=83=E6=A1=83?= Date: Thu, 10 Sep 2026 20:42:39 +0800 Subject: [PATCH 1/2] fix(windows): re-arm neutralized TLS field relocations for PE32 DLLs The packer demotes the four base-relocation entries covering the TLS directory's VA fields to IMAGE_REL_BASED_ABSOLUTE padding, because its own loader fixes TLS up by hand. Restored verbatim, a DLL mapped off its preferred base keeps stale VAs in its TLS directory and the OS loader faults in LdrpAllocateTlsEntry while writing the TLS slot index through the unrelocated AddressOfIndex (observed as a 0xc0000005 startup failure). Walk the restored BaseReloc blocks and promote those entries back to IMAGE_REL_BASED_HIGHLOW, gated on is_dll so EXE output stays byte-identical. --- .../src/windows/exe/pipeline/pe32.rs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/senbei-engine/src/windows/exe/pipeline/pe32.rs b/senbei-engine/src/windows/exe/pipeline/pe32.rs index 4ddb6bb..66827e1 100644 --- a/senbei-engine/src/windows/exe/pipeline/pe32.rs +++ b/senbei-engine/src/windows/exe/pipeline/pe32.rs @@ -976,6 +976,58 @@ impl<'a> Unpacker<'a> { } } + // ---- Re-arm TLS field base relocations (PE32 DLL) ---- + // The packer neutralizes the four relocations covering the TLS + // directory's VA fields (Start/EndAddressOfRawData, AddressOfIndex, + // AddressOfCallBacks) by demoting them to IMAGE_REL_BASED_ABSOLUTE + // padding, because its own loader fixes TLS up by hand. Restored + // verbatim, a DLL mapped off its preferred base keeps stale VAs in its + // TLS directory, and the OS loader faults in LdrpAllocateTlsEntry when + // it writes the TLS slot index through the unrelocated AddressOfIndex. + // Promote those entries back to IMAGE_REL_BASED_HIGHLOW. EXE output + // is untouched: its BaseReloc directory is zeroed above, and its + // goldens must stay byte-identical. + if is_dll && tls_dir_rva > 0 { + let reloc_rva = get_u32(&self.decompressed, exe_pe.wrapping_add(0xA0)); + let reloc_size = get_u32(&self.decompressed, exe_pe.wrapping_add(0xA4)); + let reloc_end = reloc_rva.wrapping_add(reloc_size); + let dlen = self.decompressed.len() as u32; + if reloc_rva > 0 && reloc_size >= 8 && reloc_end <= dlen { + let mut rearmed = 0u32; + let mut block = reloc_rva; + while block.wrapping_add(8) <= reloc_end { + let page = get_u32(&self.decompressed, block); + let block_size = get_u32(&self.decompressed, block.wrapping_add(4)); + if block_size < 8 + || !block_size.is_multiple_of(2) + || block.wrapping_add(block_size) > reloc_end + { + break; + } + let entries = (block_size - 8) / 2; + for i in 0..entries { + let entry_off = block.wrapping_add(8).wrapping_add(i.wrapping_mul(2)); + let entry = get_u16(&self.decompressed, entry_off); + let entry_rva = page.wrapping_add((entry & 0x0FFF) as u32); + // IMAGE_REL_BASED_ABSOLUTE with a real offset is + // neutralized, not padding (padding keeps offset 0). + if entry >> 12 == 0 + && entry & 0x0FFF != 0 + && entry_rva >= tls_dir_rva + && entry_rva < tls_dir_rva.wrapping_add(16) + { + write_u16(&mut self.decompressed, entry_off, (entry | 0x3000) as u32); + rearmed = rearmed.wrapping_add(1); + } + } + block = block.wrapping_add(block_size); + } + if verbose && rearmed > 0 { + println!(" re-armed {} TLS field relocations", rearmed); + } + } + } + // ---- Import table (PE32, 4-byte thunks) ---- if verbose { println!("[8/9] Decrypting import strings (PE32)..."); From d6224e639cee25d88d80e9f961912c91ef48ba54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B6=BE=E7=80=AC=E6=A1=83=E6=A1=83?= Date: Thu, 10 Sep 2026 20:58:44 +0800 Subject: [PATCH 2/2] chore: bump version to 1.3.1 --- Cargo.lock | 14 +++++++------- Cargo.toml | 2 +- senbei-wasm/Cargo.lock | 14 +++++++------- senbei-wasm/Cargo.toml | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4047856..98039fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -402,7 +402,7 @@ dependencies = [ [[package]] name = "senbei-cli" -version = "1.3.0" +version = "1.3.1" dependencies = [ "senbei-engine", "senbei-io", @@ -413,7 +413,7 @@ dependencies = [ [[package]] name = "senbei-crypto" -version = "1.3.0" +version = "1.3.1" dependencies = [ "aes", "thiserror", @@ -421,7 +421,7 @@ dependencies = [ [[package]] name = "senbei-elf" -version = "1.3.0" +version = "1.3.1" dependencies = [ "goblin", "thiserror", @@ -429,7 +429,7 @@ dependencies = [ [[package]] name = "senbei-engine" -version = "1.3.0" +version = "1.3.1" dependencies = [ "memmap2", "senbei-crypto", @@ -444,7 +444,7 @@ dependencies = [ [[package]] name = "senbei-io" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "indicatif", @@ -465,7 +465,7 @@ dependencies = [ [[package]] name = "senbei-metadata" -version = "1.3.0" +version = "1.3.1" dependencies = [ "serde", "thiserror", @@ -473,7 +473,7 @@ dependencies = [ [[package]] name = "senbei-pe" -version = "1.3.0" +version = "1.3.1" dependencies = [ "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index e7b583f..42a146a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ exclude = ["senbei-wasm"] resolver = "2" [workspace.package] -version = "1.3.0" +version = "1.3.1" edition = "2024" rust-version = "1.98.1" license = "AGPL-3.0-only" diff --git a/senbei-wasm/Cargo.lock b/senbei-wasm/Cargo.lock index f9912af..22a80df 100644 --- a/senbei-wasm/Cargo.lock +++ b/senbei-wasm/Cargo.lock @@ -412,7 +412,7 @@ dependencies = [ [[package]] name = "senbei-crypto" -version = "1.3.0" +version = "1.3.1" dependencies = [ "aes", "thiserror", @@ -420,7 +420,7 @@ dependencies = [ [[package]] name = "senbei-elf" -version = "1.3.0" +version = "1.3.1" dependencies = [ "goblin", "thiserror", @@ -428,7 +428,7 @@ dependencies = [ [[package]] name = "senbei-engine" -version = "1.3.0" +version = "1.3.1" dependencies = [ "memmap2", "senbei-crypto", @@ -443,7 +443,7 @@ dependencies = [ [[package]] name = "senbei-io" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "indicatif", @@ -464,7 +464,7 @@ dependencies = [ [[package]] name = "senbei-metadata" -version = "1.3.0" +version = "1.3.1" dependencies = [ "serde", "thiserror", @@ -472,14 +472,14 @@ dependencies = [ [[package]] name = "senbei-pe" -version = "1.3.0" +version = "1.3.1" dependencies = [ "thiserror", ] [[package]] name = "senbei-wasm" -version = "1.3.0" +version = "1.3.1" dependencies = [ "console_error_panic_hook", "senbei-engine", diff --git a/senbei-wasm/Cargo.toml b/senbei-wasm/Cargo.toml index 966a1e5..b071f5c 100644 --- a/senbei-wasm/Cargo.toml +++ b/senbei-wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "senbei-wasm" -version = "1.3.0" +version = "1.3.1" edition = "2024" rust-version = "1.98.1" description = "WebAssembly bindings for senbei (browser frontend assets live in web/)"