An ARM64 ELF Packer/Loader for AArch64 Linux Binaries
A comprehensive security research tool that encrypts ARM64 ELF executables using multi-layer encryption and provides runtime in-memory execution without writing the original binary to disk.
- ARM64 ELF Support: Specifically designed for AArch64 Linux binaries
- Multi-Layer Encryption: Triple encryption using AES-256, ChaCha20, and RC4
- Memory Execution: Runtime decryption and execution entirely in memory using
memfd_create - Code Obfuscation: Advanced obfuscation techniques for anti-analysis
- CRC32 Verification: Integrity checking to detect tampering
- Self-Contained: Packed binaries are completely standalone
- Core Dump Prevention: Prevents memory dumps using
setrlimit - Secure Memory Wiping: Multi-pass memory erasure for sensitive data
- Direct Syscalls: Bypasses userland hooks for enhanced stealth
- Polymorphic Loader: Every packed binary is bytewise unique β randomized magic, filler, padding, and symbol table scrubbing at stub-generation time
# Clone the repository
git clone https://github.com/litemars/hARMless.git
cd hARMless
# Build everything
make all
# Pack a binary
make pack INPUT=/bin/ls OUTPUT=packed_ls
# Run the packed binary
./packed_ls- ARM64/AArch64 Linux system or cross-compilation toolchain
- GCC for ARM64 (
aarch64-linux-gnu-gccor native) - Make
- OpenSSL (
libssl-dev) β required for AES-256 and ChaCha20 via the EVP API - Standard development tools (
git,build-essential)
# 1. Clone the repository
git clone https://github.com/litemars/hARMless.git
cd hARMless
# 2. Build all components
make all
# This creates:
# - build/packer : Binary packer
# - build/loader : Stub loader
# - build/stubgen : Stub generator# Install ARM64 cross-compiler and OpenSSL
sudo apt-get install gcc-aarch64-linux-gnu libssl-dev
# Build with cross-compiler
make CC=aarch64-linux-gnu-gcc all# Pack an ARM64 binary
make pack INPUT=your_arm64_binary OUTPUT=packed_binary
# Alternative: Use tools directly
./build/packer your_arm64_binary packed_data
./build/stubgen ./build/loader packed_data packed_binary# Simply execute the packed binary
./packed_binary
# The packed binary will:
# 1. Run anti-debug and anti-sandbox checks
# 2. Read its own embedded encrypted data
# 3. Decrypt the original ELF in memory
# 4. Verify integrity with CRC32
# 5. Create a masqueraded memfd and write the ELF into it
# 6. Delete itself from disk (unlink)
# 7. Execute directly from memory via /proc/self/fd/<memfd># Testing using /bin/ls
make test
# Output: packed_binary: packed_ls
The packer uses a triple-layer encryption approach:
- AES-256-ECB: First encryption pass (OpenSSL EVP)
- ChaCha20: Modern stream cipher for additional security (OpenSSL EVP)
- RC4 Stream Cipher: Final obfuscation layer
Original Binary β AES-256 β ChaCha20 β RC4 β Packed Data
Key Generation: Cryptographically secure random keys from /dev/urandom (256 bits per layer)
Three selectable methods for writing the decrypted ELF into the memfd (chosen at compile time):
| Method | Flag | Kernel Requirement |
|---|---|---|
io_uring (default) |
-DCOPY_WITH_IO_URING |
β₯ 5.1 |
mmap |
-DCOPY_WITH_MMAP |
Any |
write(2) |
(neither flag) | Any |
Every invocation of stubgen produces a bytewise-unique packed binary, even when packing the same input:
| Mutation | Mechanism |
|---|---|
| Random magic | 32-bit g_packed_magic patched to a fresh value from /dev/urandom; packed header is synchronized |
| Random filler | 256-byte g_pack_polymorph array in .data overwritten with random bytes |
| Random padding | 0β4095 bytes of random junk inserted between loader stub and payload |
| SC table re-keying | hARMless_sc[] re-encoded with a fresh random g_sc_xor_key so syscall numbers differ in every binary |
| String block re-keying | All 241 obfuscated string bytes in g_obf_str_block re-encoded with a fresh random g_str_xor_key |
| Header OTP blinding | Pack header body (140 bytes) XOR'd with the first 140 bytes of g_pack_polymorph as a one-time pad |
| Symbol scrub | .symtab/.strtab sections overwritten with random data; ELF section header fields zeroed |
The result: no two packed outputs share the same byte pattern, defeating static hash-based signatures.
Syscall numbers are stored in a volatile array (hARMless_sc[]) XOR-encoded with the key 0xDEADBEEF at compile time. At stub-generation time, stubgen re-encodes the entire table with a fresh random key written into g_sc_xor_key, so syscall numbers differ in every packed binary. They are decoded inline at each call site via hARMless_sc[i] ^ g_sc_xor_key, preventing static analysis tools from recovering syscall identifiers.
All anti-debug and process-masquerade strings (tool names, hypervisor signatures, env var names, process titles) are stored in a single contiguous g_obf_str_block[] array, XOR-encoded with g_str_xor_key. At stub-generation time, stubgen re-encodes the entire block with a fresh random byte key, making every binary's string patterns unique and independent of one another.
- Secure Wiping: 3-pass overwrite (zeros, ones, random) with volatile access to prevent compiler optimization
- No Disk Writes: Original binary never touches filesystem
- Self-Deletion: Loader calls
unlink()on itself before executing the payload - ASLR Compatible: Position-independent code; random address slot reserved via
mmap(NULL)before execution
setrlimit(RLIMIT_CORE, &(struct rlimit){0, 0});Ensures sensitive memory is never written to disk, even during crashes.
CRC32 checksums detect any tampering with:
- Encrypted payload
- Decryption keys
- Loader code
- No debug symbols: Stripped binaries; section header table zeroed at stub-generation time
- Obfuscated control flow: Reduces reverse engineering surface
- Obfuscated syscall numbers: Stored XOR'd with
0xDEADBEEF, decoded at each call site - Direct syscalls: Evades LD_PRELOAD and EDR hooks
- Noise delays: CPU-bound xorshift loops seeded from the ASLR stack address; cannot be skipped by sandbox time-acceleration and emit no recognizable timing syscall
- Process context probes:
getpid,getppid, andprctl(PR_GET_NAME)woven between critical operations; results used in a runtime condition to avoid trivial dead-code elimination by an analyst - In-memory execution: No
/tmpartifacts
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Original Binary β
ββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββ
β Packer (packer.c) β
β - Read ELF β
β - Generate keys β
β - Triple encrypt β
β - Compute CRC32 β
βββββββββββββ¬ββββββββββββ
β
βΌ
βββββββββββββββββββββββββ
β Packed Data File β
β [encrypted payload] β
βββββββββββββ¬ββββββββββββ
β
βΌ
βββββββββββββββββββββββββ
β Stub Generator β
β (stubgen.c) β
β - Patch magic/filler β
β - Scrub symbols β
β - Insert padding β
β - Append payload β
βββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Packed Binary (Output) β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Loader Stub (loader.c) β β
β β - Anti-debug / anti-sandbox checks β β
β β - Decrypt (RC4 β ChaCha20 β AES-256) β β
β β - Verify CRC32 β β
β β - Create masqueraded memfd β β
β β - Unlink self β β
β β - Execute via /proc/self/fd/<N> β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
β β [random padding] Encrypted Payload + Header β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββ
β Runtime Execution β
β (in-memory only) β
βββββββββββββββββββββββββ
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
- Authorized penetration testing
- Security research and education
- Red team operations
- Malware analysis
Unauthorized use is prohibited and may be illegal.
This project is licensed under the MIT License - see the LICENSE file for details.