7个稳定版本
2.0.0 | 2023年5月12日 |
---|---|
1.4.1 | 2023年5月9日 |
#427 in Unix API
22KB
416 行代码
Memmod
这是一个用于简化连接到、从进程中读取和写入的库。将来,它可能会支持更高级的功能(如注入代码、扫描等)。
目前,它只支持Unix。我有一台Windows开发机器,一旦我对它的稳定性满意并且它准备好投入生产,我将添加Windows支持。
连接到进程
连接到进程有两种方式:使用带有PID的 Process::new
,和使用带有名称的 Process::find[_strict]
。这两种方法都需要root权限,因为它们会立即连接到进程。
如果您想通过名称查找进程,请使用 Process::find
。这将寻找名称中包含所提供名称的进程。这可能很危险:如果它遇到第一个匹配项,它将匹配 cat file.txt | grep <name>
。要执行严格的相等性检查,请使用 Process::find_strict
。 提示:如果您想找到进程的确切名称,请尝试获取其PID,然后运行 cat /proc/<pid>/status
。第一行以全名结束。
以下是连接到进程的示例
use memmod::Process;
fn main() {
let proc = match Process::find("vscodium") {
Ok(proc) => proc,
Err(e) => {
eprintln!("Failed to attach: {e}");
return;
}
};
// When the process gets dropped, it will detach. Detaching
// fom a process automatically resumes it.
//
// To handle errrors when detaching, use `Process::detach`:
if let Err(e) = proc.detach() {
eprintln!("Failed to detach: {e}");
}
}
读取/写入进程的内存
读取进程内存有两种方式:使用 Process::read_word[_offset]
,和使用 ProcessReader
。
第一种方法从进程读取一个单词(一个isize
)。_offset
变体首先将进程的基址加到地址上。然而,以这种方式读取数据可能很笨拙且令人讨厌,因此还提供了一个ProcessReader
类型,该类型实现了Read
并处理单个字节。您可以使用Process::reader[_offset]
来创建一个。
默认情况下,每次读取时,读取器将通过内存前进;这可以通过类似builder-pattern的ProcessReader::no_advance
方法(这也可以应用于已创建的读取器)来禁用。之后,读取器将“冻结”在其当前地址处,并始终从同一内存片段中读取。
以下是从进程读取的示例
use memmod::Process;
fn main() {
let mut proc = Process::new(1234)
.expect("Failed to attach to the process");
// Reads 4 bytes on a 32-bit machine,
// and 8 bytes on a 64-bit machine.
let word: isize = proc.read_word(0xdeadbeef)
.expect("Failed to read word from process");
// Readers contain a mutable reference to the
// process, so they must be dropped after using.
let data = {
let mut data = Vec::new();
let mut buf = [0u8; 8];
let mut reader = proc.reader(0xbadf00d, 8);
while buf[0] == 0 {
// `buf` is the same size as the reader,
// so we don't have to worry about how
// much data was read.
//
// If `buf` had been 7 bytes, `reader` would
// have read one word (two on 32-bit), and
// discarded the last byte.
reader.read_exact(&mut buf)
.expect("Failed to read bytes from process");
data.extend_from_slice(&buf);
}
// `reader` gets dropped and we can use `proc` again.
};
}
要向进程的内存写入,操作完全相同(当然要替换正确的函数)。有一个ProcessWriter
结构体,它实现了Write
,并且具有与读取器相同的语义。但是,当您销毁一个ProcessWriter
时,它尝试刷新您已写入的数据。如果失败,这将导致一个讨厌的panic!始终在销毁写入器之前调用flush
。
跟随指针链
如果您不知道指针链是什么,请谷歌搜索multi-level pointers cheat engine
。
还有一个通过Process::pointer_chain
来跟随指针链的实用程序。它遵循传统的语义(取消引用地址,添加偏移量,重复)。
许可和贡献
这是在非常自由的MIT许可下授权的。如果有人愿意接受这个项目并在此基础上扩展,甚至从中获利,我将非常高兴。
至于贡献,任何贡献都受欢迎(但并不一定被接受)。除非另有说明,否则贡献在MIT许可下授权。不符合MIT的贡献可能会被拒绝(抱歉,但一个项目使用多个许可太多)。
依赖关系
~1.5MB
~35K SLoC