#dll #remote-procedure #rpc #injector #dll-injection

nightly dll-syringe

A windows dll injection library written in rust

30 releases

0.15.2 Jun 5, 2023
0.15.0 Jan 18, 2023
0.14.0 Nov 19, 2022
0.13.1 Jun 22, 2022
0.1.0 Jul 30, 2021

#2 in #injector

Download history 77/week @ 2024-07-01 428/week @ 2024-07-08 163/week @ 2024-07-15 92/week @ 2024-07-22 234/week @ 2024-07-29 142/week @ 2024-08-05 179/week @ 2024-08-12 338/week @ 2024-08-19 105/week @ 2024-08-26 163/week @ 2024-09-02 123/week @ 2024-09-09 133/week @ 2024-09-16 199/week @ 2024-09-23 161/week @ 2024-09-30 43/week @ 2024-10-07 133/week @ 2024-10-14

552 downloads per month

MIT license

205KB
4K SLoC

dll-syringe

CI crates.io Documentation dependency status MIT

A windows dll injection library written in Rust.

Supported scenarios

Injector Process Target Process Supported?
32-bit 32-bit Yes
32-bit 64-bit No
64-bit 32-bit Yes (requires feature into-x86-from-x64)
64-bit 64-bit Yes

Usage

Inject & Eject

This crate allows you to inject and eject a DLL into a target process. The example below will inject and then eject injection_payload.dll into the process called "ExampleProcess".

use dll_syringe::{Syringe, process::OwnedProcess};

// find target process by name
let target_process = OwnedProcess::find_first_by_name("ExampleProcess").unwrap();

// create a new syringe for the target process
let syringe = Syringe::for_process(target_process);

// inject the payload into the target process
let injected_payload = syringe.inject("injection_payload.dll").unwrap();

// do something else

// eject the payload from the target (optional)
syringe.eject(injected_payload).unwrap();

Remote Procedure Calls (RPC)

This crate supports two mechanisms for rpc. Both only work one-way for calling exported functions in the target process and are only intended for one-time initialization usage. For extended communication a dedicated rpc library should be used.

RemotePayloadProcedure RemoteRawProcedure
Feature rpc-payload rpc-raw
Argument and Return Requirements Serialize + DeserializeOwned Copy, Argument size has to be smaller than usize in target process
Function Definition Using macro payload_procedure! Any extern "system" or extern "C" with #[no_mangle]

RemotePayloadProcedure

A rpc mechanism based on bincode. The target procedure must be defined using the payload_procedure! macro (requires the payload-utils feature).

The definition of an exported add function could look like this:

dll_syringe::payload_procedure! {
    fn add(a: f64, b: f64) -> f64 {
        a + b
    }
}

The code of the injector/caller could looks like this:

use dll_syringe::{Syringe, process::OwnedProcess};

// find target process by name
let target_process = OwnedProcess::find_first_by_name("ExampleProcess").unwrap();

// create a new syringe for the target process
let syringe = Syringe::for_process(target_process);

// inject the payload into the target process
let injected_payload = syringe.inject("injection_payload.dll").unwrap();

let remote_add = unsafe { syringe.get_payload_procedure::<fn(f64, f64) -> f64>(injected_payload, "add") }.unwrap().unwrap();
let result = remote_add.call(&2.0, &4.0).unwrap();
println!("{}", result); // prints 6

// eject the payload from the target (optional)
syringe.eject(injected_payload).unwrap();

RemoteRawProcedure

This mechanism is based on dynamically generated assembly code. The target procedure can be any exported function as long as it uses either the system or C calling convention. This means that even Win32 functions can be called directly.

The definition of an exported add function could look like this:

#[no_mangle]
extern "system" fn add(a: f64, b: f64) -> f64 {
    a + b
}

The code of the injector/caller could looks like this:

use dll_syringe::{Syringe, process::OwnedProcess};

// find target process by name
let target_process = OwnedProcess::find_first_by_name("ExampleProcess").unwrap();

// create a new syringe for the target process
let syringe = Syringe::for_process(target_process);

// inject the payload into the target process
let injected_payload = syringe.inject("injection_payload.dll").unwrap();

let remote_add = unsafe { syringe.get_raw_procedure::<extern "system" fn(f64, f64) -> f64>(injected_payload, "add") }.unwrap().unwrap();
let result = remote_add.call(2.0, 4.0).unwrap();
println!("{}", result); // prints 6

// eject the payload from the target (optional)
syringe.eject(injected_payload).unwrap();

License

Licensed under MIT license (LICENSE or http://opensource.org/licenses/MIT)

Attribution

Inspired by Reloaded.Injector from Sewer.

Dependencies

~3–15MB
~191K SLoC