#dll-injection #dll #windows #rpc #injector

nightly dll-syringe

A windows dll injection library written in rust

22 releases (10 breaking)

Uses new Rust 2021

0.11.2 May 7, 2022
0.11.0 Mar 19, 2022
0.1.2 Oct 14, 2021
0.1.0 Jul 30, 2021

#16 in Windows APIs

Download history 61/week @ 2022-01-27 24/week @ 2022-02-03 86/week @ 2022-02-10 89/week @ 2022-02-17 27/week @ 2022-02-24 89/week @ 2022-03-03 99/week @ 2022-03-10 139/week @ 2022-03-17 14/week @ 2022-03-24 15/week @ 2022-03-31 35/week @ 2022-04-07 7/week @ 2022-04-14 9/week @ 2022-04-21 143/week @ 2022-04-28 166/week @ 2022-05-05 363/week @ 2022-05-12

682 downloads per month

MIT license

200KB
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 = 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 = 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

~2.2–6.5MB
~135K SLoC