2 Commits
Author SHA1 Message Date
Momoko-Ayase d6224e639c chore: bump version to 1.3.1 2026-09-10 20:58:44 +08:00
Momoko-Ayase 9b8024b132 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.
2026-09-10 20:42:39 +08:00
5 changed files with 68 additions and 16 deletions
Generated
+7 -7
View File
@@ -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",
]
+1 -1
View File
@@ -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"
@@ -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)...");
+7 -7
View File
@@ -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",
+1 -1
View File
@@ -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/)"