如何在Rust中跳转到/调用任意内存
我正在尝试构建一个执行程序生成的 x86 指令的 JIT。我想我已经创建了一个有效的 x86 字节代码片段,应该打印“Hello World”,但我不确定如何调用它。
我正在将指向向量开头的指针转换为 void 函数并调用它:
fn main() {
let msg: &[u8] = b"Hello World ";
let mut byte_codes: Vec<u8> = Vec::with_capacity(1000);
// Move width into edx
byte_codes.extend_from_slice(&[0xba, msg.len() as u8, 0, 0, 0]);
// Msg to write
byte_codes.push(0xb9);
byte_codes.extend_from_slice(&(msg.as_ptr() as u64).to_be_bytes());
// File descriptor and sys call
byte_codes.extend_from_slice(&[0xbb, 0x01, 0, 0, 0]);
byte_codes.extend_from_slice(&[0xb8, 0x04, 0, 0, 0]);
// Sys call
byte_codes.extend_from_slice(&[0xcd, 0x80]);
// Return
byte_codes.push(0xc3);
let func_ptr = byte_codes.as_ptr();
unsafe {
let func: fn() -> () = func_ptr.cast::<fn() -> ()>().read();
func();
}
}
执行此返回:
error: process didn't exit successfully: `targetdebugrun-bytecode.exe` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)
删除除返回调用之外的所有字节码也会导致相同的错误。
我不确定那个错误是什么意思。字节码有问题还是我的函数转换不正确?我怎样才能让它打印“Hello World”?
回答
这是一个有效的版本:
use memmap::MmapMut;
fn main() {
let msg: &[u8] = b"Hello World ";
let mut byte_codes: Vec<u8> = Vec::with_capacity(1000);
// Move width into edx
byte_codes.extend_from_slice(&[0xba, msg.len() as u8, 0, 0, 0]);
// Msg to write
byte_codes.push(0xb9);
byte_codes.extend_from_slice(&(msg.as_ptr() as u32).to_le_bytes());
// File descriptor and sys call
byte_codes.extend_from_slice(&[0xbb, 0x01, 0, 0, 0]);
byte_codes.extend_from_slice(&[0xb8, 0x04, 0, 0, 0]);
// Sys call
byte_codes.extend_from_slice(&[0xcd, 0x80]);
// Return
byte_codes.push(0xc3);
let mut m = MmapMut::map_anon(byte_codes.len()).unwrap();
m.clone_from_slice(&byte_codes);
let m = m.make_exec().unwrap();
let func_ptr = m.as_ptr();
unsafe {
let func: extern "C" fn() = std::mem::transmute(func_ptr);
func();
}
}
有几件事需要解决:
- 它看起来像是
byte_codes32 位 x86 Linux 代码,因此需要使用类似的东西运行cargo run --target i686-unknown-linux-gnu - 因为它是 32 位代码,所以我们要强制转换
msg.as_ptr()为u32. - x86 是小端的,所以我们想使用
.to_le_bytes() func_ptr.cast::<fn() -> ()>().read()不转换为函数指针,而是将前 4/8 个字节byte_codes转换为函数指针。- 使用
extern "C" fn()确保 Rust 知道正确的 ABI - 我们使用 memmap crate 创建内存,我们可以将其标记为可执行文件
make_exec()。