
Generate Proxy DLLs in Rust
cargo install rustdllproxy
This crate currently only supports the standard DLL PE format.
Rustdllproxy ships two subcommands:
| Command | Purpose |
|---|---|
rustdllproxy new | Generate a new proxy cdylib crate from one or more existing DLLs. |
rustdllproxy build | Sync the .def file with src/lib.rs and build the crate. |
rustdllproxy --help # top-level help
rustdllproxy new --help # generation flags
rustdllproxy build --help # build flags
Before generating your crate, decide how you would like your proxy to work. A typical pattern is search order hijacking, where you would first rename your target DLL to something like target_.dll, and then use the compiled proxy as target.dll. This creates a flow resembling binary -> target.dll -> target_.dll
There are multiple paths forward depending on your use case. If however you need to rename the underlying DLL being proxied, update the generated .def file accordingly.
rustdllproxy new -p path/to/target_.dll -n my_proxy
Tip: rustdllproxy is built as a CLI with clap. Run rustdllproxy --help to see all options and flags.
The macro library supports 3 main hook types: prehook, posthook, and fullhook.
Replace the #[no_mangle] directive with the hook macro (leave the //<dllname>.dll trailing comment in place)
#[prehook("dllbeingproxied.dll", "function_name")] //dllbeingproxied.dll
Fill out the function signature (declare inputs as mut to modify them)
Build with rustdllproxy build.
prehookExecutes code before the original function. Allows you to add functionality or modify input variables.
#[prehook("target.dll", "my_function")] //target.dll
fn my_function(mut param1: i32, mut param2: &str) {
// Your code here - executes before original function
param1 *= 2; // Modify parameters if needed
}
posthookExecutes code after the original function. View and edit the return value using the magic ret variable.
#[posthook("target.dll", "calculate")] //target.dll
fn calculate(input: i32) -> i32 {
// Original function executes first
// Then your code runs with access to 'ret'
ret = ret * 2; // Modify return value
}
Note: The
retvariable is automatically defined as mutable. You don't need to reference it if not needed.
fullhookProvides complete control over function execution. Manually manage the return value and function calling.
#[fullhook("target.dll", "do_multi_add")] //target.dll
fn do_multi_add(mut a: i32, mut b: i32, mut c: i32) -> i32 {
// Pre-processing
a += 10;
b += 20;
// Call original function with magic func()
let mut return_value: i32 = func(a, b, c);
// Post-processing
return_value *= 2;
// Must explicitly return the value
return_value
}
Run from the proxy crate directory (or pass it as the first argument):
rustdllproxy build [PATH] [--profile <name>] [--no-build] [-- <extra cargo args>]
| Flag | Default | Effect |
|---|---|---|
PATH | . | Path to the proxy crate root. |
--profile <name> | release | Cargo build profile (release, dev, custom). |
--no-build | off | Regenerate the .def file but skip cargo build. |
-- <args> | — | Forwarded verbatim to cargo build. |
.def file is fully regenerated on every build, manual changes will be overwritten. If you need to make manual changes against how rustdllproxy builds, cargo can be used to accomplish this.Let's say you want to modify office.dll used in office software via DLL search order hijacking:
# Rename the original DLL
mv office.dll office_.dll
rustdllproxy new -p office_.dll -n office_proxy
#[prehook("office_.dll", "open_window")] //office_.dll
fn open_window() {
// Your custom code here...
println!("Window is about to open!");
}
cd office_proxy
rustdllproxy build
Build files are located under
/target
It is possible to proxy several target DLLs with a single crate. This feature is rarely used and comes with some important caveats.
When bundling multiple DLLs:
Release notes live in CHANGELOG.md.
Contributions are welcome! Please feel free to submit issues and pull requests.