- Overview
- Execution Flow
- Runtime Behavior
- ELF 64-bit File Structure
- Injection Mechanism
- Stub Patch System
- Entry Point Redirection
- Encryption System
- Commands
- Automated Test Suite
- Debug Tools
- Compatibility Notes
This project implements a 64-bit ELF packer with runtime unpacking capabilities.
It transforms an ELF binary into a new executable (woody) that embeds:
- an encrypted copy of the original executable segment
- a custom unpacking stub injected into the binary
At runtime, the stub reconstructs the original program in memory and transfers execution back to it, preserving identical behavior while modifying only the binary representation.
Original ELF
→ Parse ELF headers & program headers
→ Locate executable PT_LOAD segment
→ Extract segment (includes .text region)
→ Encrypt payload (XOR + additive cipher)
→ Inject stub into executable segment region
(padding or shifting mode)
→ Patch stub with metadata (offsets, sizes, key)
→ Modify ELF entry point (e_entry → stub)
→ Generate "woody"
Runtime:
→ Kernel loads ELF
→ Execution starts in stub
→ Stub decrypts payload in memory
→ Restores original code
→ Prints: ....WOODY.....
→ Jumps to original entry point
→ Original program executes
At execution time, a packed /bin/whoami behaves as follows:
....WOODY.....
username
Except for the printed string, the final behavior remains strictly identical to the original binary.
+----------------------------------------------------+
| ELF Header |
| (64 bytes, ELF64_Ehdr structure) |
+----------------------------------------------------+
| Program Header Table |
| (Variable size, ELF64_Phdr entries) |
| describes memory-mapped segments |
+----------------------------------------------------+
| Text Section |
| (Executable code inside PT_LOAD segment) |
+----------------------------------------------------+
| Data Section |
| (Initialized data inside PT_LOAD segment) |
+----------------------------------------------------+
| Symbol Table Section |
| (ELF64_Sym entries) |
+----------------------------------------------------+
| String Table Section |
| ("name1\0name2\0...") |
+----------------------------------------------------+
| Section Header Table (optional) |
| (ELF64_Shdr entries) |
+----------------------------------------------------+
The stub is injected into the executable PT_LOAD segment, which typically contains:
.text- executable data pages
- padding between segments
It is not restricted to .text only, but placed inside the executable memory-mapped region.
Used when sufficient space exists inside the executable segment.
Condition:
sizeof_stub ≤ padding_size
Where:
padding_size = next_segment.p_offset - injection_offset
Behavior:
- stub is written directly into existing padding
- no ELF relocation required
- no structural modification of headers
[ PT_LOAD (code + .text) | padding | STUB ]
Used when padding is insufficient or explicitly required.
p_filesz += sizeof_stub;
p_memsz += sizeof_stub;All headers after injection are updated:
- program headers offsets shifted
- section headers offsets shifted
- ELF section header table offset updated
[ original ELF ]
[ STUB inserted ]
[ shifted ELF data ]
The injected stub is patched with runtime metadata:
ww_t_patch patch;This includes:
- offset from stub to original entry point
.textsection offset relative to stub.textsize- segment relocation offsets
Layout inside binary:
[ STUB ........ PATCH ........ KEY ]
The ELF entry point is modified:
e_entry = injection_addr;Result:
Before: entry → main()
After: entry → stub → main()
- symmetric encryption
- same function used for encryption and decryption
Applied before XOR:
byte → +offset → XOR → encrypted
encrypted → XOR → -offset → original
make # Build project
./woody_woodpacker <binary> [OPTIONS]
# Make and run the packer with the default options, then run the packed binary, all with valgrind
make run
# Example: run the packer with verbose mode, padding injection mode, then run the binary
./woody_woodpacker /bin/ls -i p -v && ./woody
Displays detailed information about:
- ELF parsing
- injection offsets
- padding analysis
- relocation operations
- stub patch values
-v
--verboseAttempts to inject stub into existing executable segment padding.
-i p
--injection-type padding- safest mode
- no ELF restructuring
- requires enough free space
Forces structural modification of ELF when padding is insufficient or explicitly selected.
-i s
--injection-type shift- rewrites ELF layout
- shifts headers and segments
- guarantees injection success
Here’s a clean README section you can drop in, matching your project tone and that test script:
The project includes a simple validation script to test packing behavior across multiple ELF binaries.
It builds the project, runs the packer on a set of sample binaries, and verifies execution output consistency.
For each binary in the resources/ directory, the script:
-
Builds the project using
make -
Executes the packer in both modes:
-i s(shift injection mode)-i p(padding injection mode)
-
Runs the resulting packed binary (
./woody) -
Checks execution output for the success signature:
....WOODY.... -
Reports results per binary and mode:
- 🟢 PASS → correct execution detected
- 🔴 FAIL → missing or invalid output
chmod +x test.sh
./test.shproject/
├── resources/
│ ├── bin1
│ ├── bin2
│ └── ...
├── woody_woodpacker
├── woody
└── test.sh
readelf -l [filename] # Check program headers of the file
readelf -S [filename] # Check section headers
hexdump -C [filename] # Check the file in hex format
vimdiff [filename 1] [filename 2] # Check the difference between two files
# Extract the .text section from code.o
objcopy --dump-section .text=code-raw code.o
# Print the loaded file content in hex format at address 0x401040
gdb ./woody
run (or r)
x/16xw 0x401040
# Add breakpoint at relative address 11ad if base address is 0x4011ad
b *0x4011ad
# Show ELF entrypoint
gdb ./woody
starti
info files
info proc map
# Produce a trace trap that stops the execution at the position (useful when debugging)
int3While the tool works on many standard Linux x86-64 ELF binaries, some binaries may fail to pack or may become unstable after packing.
Not all ELF binaries have the same layout characteristics. The shifting injection mode depends on being able to safely:
- Extend a
PT_LOADsegment (p_filesz/p_memsz) - Shift all subsequent program and section headers correctly
- Preserve valid page-aligned memory mappings between segments
- Maintain consistent execution flow after entry point redirection
Some binaries cannot satisfy these conditions safely due to:
-
Tight segment packing
- No sufficient contiguous space between
PT_LOADsegments - Stub overflow crosses into adjacent mapped regions
- No sufficient contiguous space between
-
Complex or hardened ELF layouts
- Multiple overlapping or tightly coupled
PT_LOADsegments - Special-purpose loader assumptions (e.g. glibc, ld-linux optimizations)
- Multiple overlapping or tightly coupled
-
RELRO / TLS / dynamic loader constraints
GNU_RELROandDYNAMICsegments may break if shifted incorrectly- Runtime loader expects strict offsets for relocation and linking
-
PIE + ASLR edge cases
- Some binaries rely on exact relative layout between segments
- Small changes in layout can invalidate runtime assumptions
- 32-bit support
- Custom key parameter
- PE / Mach-O support
- Compression