rustdllproxy 2.0.0

Crate to ease the development of proxy DLLs in Rust
# rustdllproxy


  

A crate utility to easily generate and develop proxy DLLs.

  

Install with `cargo install rustdllproxy`.

  

There is a video tutorial [here](https://youtu.be/f7WVPpsBXNA).

  

> This video is now outdated. The process of writing functions remains the same, however the crate creation process and naming requirements have changed.

  

# Compatability


  

This crate currently only supports the normal DLL PE format. As such, .NET DLLs are not supported.

  

# Utility


  

This crate serves two main purposes:

  

- Proxying a single DLL in order to modify or better understand its behavior.

  

- Collecting several DLLs into one to modify behavior, which can then be used along side a custom application or technique to use or understand several DLLs as one.

  

**Current Limitations**

  

- Currently, the crate only understands exports from the standard PE DLL format. As such, .NET DLLs are not compatible.

  

- When hooking functions with custom code, the function signature must be known. This can easily be found with a multitude of disassemblers and reverse engineering tools, like Ghidra.

  

# Creating a New Crate


Rustdllproxy generates a `cdylib` crate that can be compiled into a DLL. See `rustdllproxy --help` for more info on using the command.

> Note, the `-p` argument can be used several times to unify several different DLLs into one.

Before creating your crate, consider how you want the proxy DLL to interact with the original(s). A typical pattern is to append and underscore to the name of the original. ***THIS MUST BE DONE BEFORE GENERATING THE CRATE.*** The generated .def file will reference this name for forwarding behavior. This of course can be modified later, but current Cargo behaviour makes this a pain in the ass.

> Cargo will not rebuild if you change a .def file. Either force a rebuild or change something within lib.rs to force Cargo to take account of the updated .def

# Writing Hooks


The macro library current supports 3 main hooks: prehook, posthook, and fullhook.

In order to invoke a hook, you must do the following:

1. Replace the `#[no_mangle]` directive with the hook macro, IE `#[prehook("dllbeingproxied.dll", "function_name")]`
2. Fill out the function signature. Remember, you can delcare inputs as `mut` to modify them in the function.
3. Go to the generated .def file, and remove the forwarding behavior. `function_name = dllbeingproxied.function_name @2` now becomes `function_name @2`. This tells the compiler to export the new symbol instead of using the default function.
4. All set!

> Remember, if you build and forget to update the .def, force Cargo to do a full rebuild. Cargo will not notice the changed .def file and will serve you a cached build instead.

## prehook


Prehook is the simplest hook. Code you write in a prehook will execute before the normal function. In this time you are able to add functionality or modify input variables.

## posthook


Posthook allows for adding functionality after the orginal function is executed. This allows you to both view and edit the return value with the magic `ret` variable. If the function returns a value, you can directly write to this variable, IE `ret = 4` to change the return value.

> Note, the macro has already defined ret as mutable. Also, you are not required to reference it if you don't need too.

## fullhook


Full hook allows for full control of what occurs both before and after the function, but in turn adds a small amount of complexity.

When writing a full hook, you must manually manage the return value and the calling of the function via the magic `func()` function, which you must call with the correct arguments in order to signal the execution of the function being proxied.

Additionally, if `func()` has a return value, you must also ensure that the hook stores and returns this value at the end of the hook's execution.

Example:

```rust
#[fullhook("dllbeingproxied.dll, "do_multi_add")]

fn do_multi_add(mut a: i32, mut b: i32, mut c: i32) -> i32 {
	// Do some stuff to a, b, c, or add more code

	let mut return_value: i32 = func(a, b, c);

	// Do some stuff to the return value or add more code

	// Return the value
	return_value
}
```

# A Typical Workflow


Say I want to modify a DLL used in common office software. I plan on using search order hijacking directly in the directory of the DLL, lets call it `office.dll`.

I would elect to rename `office.dll` to `office_.dll`. Then, run `rustdllproxy` with the appropriate arguments.

In the generated crate, I would like the prehook the `open_window` function, which I know following some reverse engineering has no arguments or return type. I would write the following:

```rust
#[prehook("office_.dll, "open_window")]

fn open_window() {
	// Write arbitrary code here...
}
```

Following, edit the .def file from `open_window = office_.open_window @3` to `open_window @3`.

Build as release, and as described earlier, rename the new DLL as if it was the original `office.dll` and move it into the directory.