手把手教你用Rust进行Dll注入

我是一个懒惰的男孩,我甚至懒的不想按键盘上的按键和挪动鼠标.可是我还是想玩游戏,该怎么做呢?通过 google 了解到我可以通过将我自己编写的dll文件注入到目标程序内,来实现这个事情.

前言

我是一个懒惰的男孩,我甚至懒的不想按键盘上的按键和挪动鼠标.可是我还是想玩游戏,该怎么做呢?

通过google了解到我可以通过将我自己编写的 dll 文件注入到目标程序内,来实现这个事情.

将大象放在冰箱里需要几步?

答案是三步。

snes9x 模拟器 Dll 注入实战

一、现在我们需要进行第一步,生成 Dll 文件

准确说是我们需要生成符合 C 标准的 dll 文件,如果你使用 go 语言,直接使用 CgoC 进行互动,即可生成符合 C 标准的 dll .

但是很明显,我要用 Rust 来做这件事。

由于 Rust 拥有出色的所有权机制,和其他语言的交互会导致 Rust 失去这个特性,所以这一块是属于 Unsafe 区域的。

Rust 默认生成的 Dll 是提供给 Rust 语言来调用的,而非C系语言的 dll.

我们现在来生成 C 系语言的 Dll 吧。

1.新建项目 lib 目录 lib 目录主要作为库文件以方便其他开发者调用

1
2
3
# 新建库项目
Cargo new --lib <project name>
Cargo new --lib joy

2.修改 Cargo.toml 文件 增加 bin 区域

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[package]
name = "joy"
version = "0.1.0"
edition = "2021"

[lib]
name = "joy"
path = "src/lib.rs"
crate-type = ["cdylib"]

[[bin]]
name = "joyrun"
path = "src/main.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1
2
# 为项目导入依赖ctor来生成符合c标准的dll
cargo add ctor 

3.修改 lib.rs 使用 ctor

1
2
3
4
5
// lib.rs
#[ctor::ctor]
fn ctor() {
    println!("我是一个dll")
}

4.编译项目生成 joy.dll 以及 joyrun.exe

1
cargo build 

现在我们有了我们自己的 dll 文件,该如何将他注入到目标的进程呢?

二、使用 dll-syringe 进行dll注入

1
cargo add dll-syringe

1.修改main.rs 将刚刚编写的dll注入到目标应用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// main.rs
use dll_syringe::{Syringe, process::OwnedProcess};

fn main() {
    // 通过进程名找到目标进程
    let target_process = OwnedProcess::find_first_by_name("snes9x").unwrap();
 
    // 新建一个注入器
    let syringe = Syringe::for_process(target_process);

    // 将我们刚刚编写的dll加载进去
    let injected_payload = syringe.inject("joy.dll").unwrap();

    // do something else

    // 将我们刚刚注入的dll从目标程序内移除
    syringe.eject(injected_payload).unwrap();
}

2.运行项目

1
2
# 运行项目
cargo run 

此时你可能会遇到一个新问题,我的dll已经加载进目标程序了,为什么没有打印 “我是一个dll”

3.解决控制台无输出问题

这是由于目标程序没有控制台,所以我们没有看到 dll 的输出,接下来让我们来获取 dll 的输出。

此时我们可以使用 TCP 交互的方式或采用 OutputDebugStringA function (debugapi.h) 来进行打印

OutputDebugStringA ,需要额外开启features Win32_System_Diagnostics_Debug

1
2
3
4
5
6
// Rust Unsafe fn
// windows::Win32::System::Diagnostics::Debug::OutputDebugStringA
pub unsafe fn OutputDebugStringA<'a, P0>(lpoutputstring: P0) 
where
    P0: Into<PCSTR>, 
// Required features: "Win32_System_Diagnostics_Debug"

采用 Tcp 通信交互

1
2
// 在lib.rs 新建tcp客户端
let stream = TcpStream::connect("127.0.0.1:7331").unwrap();
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    // 在main.rs 新建tcp服务端
    let (mut stream, addr) = listener.accept()?;
    info!(%addr,"Accepted!");
    let mut buf = vec![0u8; 1024];
    let mut stdout = std::io::stdout();
    while let Ok(n) = stream.read(&mut buf[..]) {
        if n == 0 {
            break;
        }
        stdout.write_all(&buf[..n])?
 }
1
2
3
# 运行项目
cargo run 
# 运行之后,大功告成,成功在Tcp服务端看到了,客户端对我们发起了请求。
Licensed under CC BY-NC-SA 4.0