Skip to content

Commit e7efb40

Browse files
authored
docs: explain libsui's behavior with executable packers (UPX) (#62)
1 parent 92f691a commit e7efb40

2 files changed

Lines changed: 50 additions & 0 deletions

File tree

‎README.md‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,40 @@ new SUI note to the note segment data.
118118
At run-time, data is extracted from note segments in memory using
119119
`dl_iterate_phdr`.
120120

121+
## Packers (UPX, etc.)
122+
123+
Runtime executable packers like [UPX](https://upx.github.io/) compress the
124+
original program and prepend a small stub that decompresses it back into
125+
memory at startup. The packed file on disk no longer contains the original
126+
section layout — sections, segments, notes, and (in many cases) resources
127+
are replaced by the packer's own envelope, so libsui's runtime lookup will
128+
fail on a packed binary that was injected *before* packing.
129+
130+
There is no fully packer-proof embedding scheme that works the way
131+
NativeAOT does on .NET: NativeAOT owns both the producer and the runtime
132+
extractor, so it can decompress its own payload after the stub has
133+
restored memory. libsui is a generic injection tool and has no hook into
134+
the unpacker, so it cannot recover data that the packer has hidden inside
135+
its compressed envelope.
136+
137+
The recommended workaround is **pack first, then inject**. libsui's
138+
writers operate on the final on-disk layout, so injecting after packing
139+
adds a fresh section/resource/note that the unpacker's stub never
140+
touches:
141+
142+
| Format | Behavior of `libsui` injection on top of a packed binary |
143+
|---------------|------------------------------------------------------------------------|
144+
| PE (UPX) | A new `RT_RCDATA` resource is added to `.rsrc`; UPX preserves the resource directory, so `find_section` continues to work. |
145+
| ELF (UPX) | A new `.note.sui` is placed in a fresh `PT_LOAD` past the packed payload, with a matching `PT_NOTE` program header. `dl_iterate_phdr` enumerates the program headers after the unpacker hands control back, so the note remains visible. |
146+
| Mach-O arm64 | A new `__SUI` segment is appended after `__LINKEDIT`; `getsectdata` reads it directly from the mapped image. (Most packers on Apple Silicon are blocked by code signing anyway.) |
147+
| Mach-O x86_64 | Data is appended past the end of the file with a sentinel marker; this survives any packer that does not truncate or rewrite the file tail. |
148+
149+
Injecting *before* packing is not supported: the packer is free to
150+
discard or relocate the embedded data, and on ELF/Mach-O the runtime
151+
lookup will not find it. If you must pack a binary that already contains
152+
libsui data, treat it as a build step ordering issue and move the
153+
injection to after the pack step.
154+
121155
## Testing
122156

123157
This crate is fuzzed with LLVM's libFuzzer. See [fuzz/](fuzz/).

‎lib.rs‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,22 @@
4343
//! # Ok(())
4444
//! # }
4545
//! ```
46+
//!
47+
//! # Packers (UPX, etc.)
48+
//!
49+
//! Runtime packers compress the original program and decompress it back into
50+
//! memory at startup, replacing the on-disk section layout with the packer's
51+
//! own envelope. Embedded sections, notes, and (in many cases) resources are
52+
//! hidden inside the packed payload, so [`find_section`] will not see data
53+
//! that was injected *before* packing.
54+
//!
55+
//! There is no fully packer-proof embedding scheme that works the way
56+
//! NativeAOT does on .NET (NativeAOT owns both the producer and the runtime
57+
//! extractor, so it can decompress its own payload after the stub restores
58+
//! memory). The recommended workaround with libsui is to **pack first, then
59+
//! inject** — libsui's writers operate on the final on-disk layout, so a
60+
//! resource/note/segment added after packing is not touched by the
61+
//! unpacker stub at startup. See the project README for per-format details.
4662
4763
use core::mem::size_of;
4864
use editpe::{

0 commit comments

Comments
 (0)