如何在 Rust 中跳转到/调用任意内存

How to jump to / call arbitrary memory in Rust

我正在尝试构建一个 JIT 来执行程序生成的 x86 指令。我想我已经创建了一个有效的 x86 字节代码片段,它应该打印“Hello World”,但我不确定如何调用它。

我将指向向量开头的指针转换为 void 函数并调用它:

fn main() {
    let msg: &[u8] = b"Hello World[=10=]";

    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();
    }
}

执行此 returns:

error: process didn't exit successfully: `target\debug\run-bytecode.exe` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)

删除除 return 调用之外的所有字节码也会导致相同的错误。

我不确定这个错误是什么意思。字节码有问题还是我的函数转换不正确?如何让它打印“Hello World”?

这是一个有效的版本:

use memmap::MmapMut;

fn main() {
    let msg: &[u8] = b"Hello World[=10=]";

    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();
    }
}

有几件事需要解决:

  1. 看起来 byte_codes 是 32 位 x86 Linux 代码,所以它需要 运行 和 cargo run --target i686-unknown-linux-gnu
  2. 因为它是 32 位代码,我们希望将 msg.as_ptr() 转换为 u32
  3. x86 是小端所以我们想使用 .to_le_bytes()
  4. func_ptr.cast::<fn() -> ()>().read() 不转换为函数指针,它将 byte_codes 的前 4/8 字节转换为函数指针。
  5. 使用 extern "C" fn() 确保 Rust 知道正确的 ABI
  6. 我们使用 memmap crate 创建内存,我们可以使用 make_exec() 将其标记为可执行文件。