enkomio/shrun
GitHub: enkomio/shrun
Stars: 10 | Forks: 1
# Shellcode Runner
A minimal Windows PE builder written in Rust. Takes a raw shellcode file (or a hex string) and produces a standalone `.exe` that wraps the shellcode in a single RWX `.text` section — ready to load in your preferred debugger.
Intended for shellcode analysis and debugging. No external dependencies, no runtime, no imports beyond `KERNEL32.VirtualAlloc` — just a bare PE with your bytes starting at `ImageBase + 0x1000`.
## Usage
shrun.exe [32|64]
| Argument | Description |
|----------|-------------|
| `input` | Path to a raw shellcode binary, **or** a hex-encoded string |
| `32\|64` | Output PE bitness. Default: `64` |
## Examples
### From a binary file
# 64-bit PE (default)
.\shrun.exe .\shellcode.bin
# 32-bit PE
.\shrun.exe .\shellcode.bin 32
Output:
[*] input: C:\Users\user\Desktop\shellcode.bin
[*] mode: PE64 (64-bit)
[*] payload: 42 bytes
[*] output: C:\Users\user\Desktop\shellcode_sh.exe
[*] shellcode: 0x0000000180001000 (= BASE_ADDRESS → rcx)
[+] done — entry/stub: 0x000000018000102a
### From a hex string
If the first argument is not an existing file, it is decoded as a hex string and used directly as the shellcode payload.
# x64: xor rax,rax / inc rax / ret
.\shrun.exe 4831c048ffc0c3 64
# x86: xor eax,eax / inc eax / ret
.\shrun.exe 31c040c3 32
# 0x prefix accepted
.\shrun.exe 0x4831c048ffc0c3
Output:
[*] input:
[*] mode: PE64 (64-bit)
[*] payload: 7 bytes
[*] output: C:\Users\user\Desktop\shellcode_sh.exe
[*] shellcode: 0x0000000180001000 (= BASE_ADDRESS → rcx)
[+] done — entry/stub: 0x0000000180001007
## Build
# 64-bit host (default on Windows)
cargo build --release --target x86_64-pc-windows-msvc
# 32-bit target
cargo build --release --target i686-pc-windows-msvc
## Features
- Builds a minimal PE32 or PE32+ executable from scratch (hardcoded header, no linker)
- Accepts a raw binary file **or** a hex string as input
- Supports both **32-bit** (PE32 / x86) and **64-bit** (PE32+ / x86-64) output
- ASLR disabled — `DllCharacteristics = 0x0000` (no `DYNAMIC_BASE`)
- DEP disabled — no `NX_COMPAT`, `.text` section flagged as `RWX` (`0xE0000020`)
- Shellcode placed at `ImageBase + RVA 0x1000` (section start, always page-aligned)
- A small **position-independent stub** is appended after the shellcode and set as the entry point; it passes `ImageBase + 0x1000` (the shellcode address) as the first argument before jumping to the shellcode
- Three imports from `KERNEL32.DLL` (import table in `.rdata`): `VirtualAlloc`, `LoadLibraryA`, `GetProcAddress`
### PE layout
File offset 0x000 PE headers (padded to 0x200)
File offset 0x200 .text RVA 0x1000 — RWX
[shellcode bytes]
[stub 17–18 bytes] ← AddressOfEntryPoint
File offset 0x200+ .rdata — import table (VirtualAlloc, LoadLibraryA, GetProcAddress)
### Stub behaviour
| Step | x64 | x86 |
|------|-----|-----|
| Recover shellcode address | `CALL $+5` / `POP RCX` / `SUB RCX, imm32` | `CALL $+5` / `POP EAX` / `SUB EAX, imm32` |
| Pass as first argument | value in **RCX** | **PUSH EAX** |
| Transfer control | `JMP rel32` → shellcode | `JMP rel32` → shellcode |
The subtraction constant is computed at build time as `shellcode_len + 5`, so the result is always `ImageBase + RVA_TEXT` regardless of payload size.
标签:通知系统