Support managed (CLR) EXEs in the old-layout EXE pipeline

Managed EXE builds differ from their native counterparts in the old
layout: the encrypted import-name table pointer is null (their imports
are just the CLR bootstrap stub), the config block's entry-point field
is 0, and the COR20 header / BSJB metadata stream / CLR resources are
stored verbatim in the protected file rather than arriving through the
section-block pass.

- Skip the import-string walk when the table pointer is null instead of
  chasing header garbage as a pointer chain (previously a caught
  out-of-bounds panic reported as corrupt input).
- Keep the protected header's entry point when the config block stores
  0, instead of overwriting it with 0.
- Restore the COR20 header, BSJB metadata stream, and CLR resources
  verbatim from the protected file after the .text dd8 pass, and
  suppress the native COR20-directory clearing when the restore ran.

Validated by decompiling the unpacked managed EXEs with ilspycmd: full
assemblies (types, methods, IL bodies) decompile cleanly. Golden corpus
unchanged (35/35 byte-identical).
This commit is contained in:
2026-08-16 05:33:44 +08:00
parent 21cd151e15
commit 7f143b3b27
2 changed files with 74 additions and 3 deletions
+6 -1
View File
@@ -53,7 +53,12 @@ managed DLL.
`unpack_auto` then dispatches:
- `Exe` → the EXE pipeline (handles both PE32+ and PE32).
- `Exe` → the EXE pipeline (handles both PE32+ and PE32). Managed EXEs take
the same path: their import-string table is null (imports are the CLR
bootstrap stub), the entry point comes from the protected header (the
config block stores 0 for managed images), and the COR20 header, BSJB
metadata stream, and CLR resources are restored verbatim from the protected
file, mirroring the managed-DLL restore.
- `NativeDll` / `ManagedDll` → the DLL pipeline first; on failure, the EXE
pipeline as a fallback. Two DLL layouts exist in the wild: an older layout
the DLL pipeline parses, and a newer one that protects DLLs with the