#可执行文件 #执行 #内存 #进程 #内存中 #磁盘 #执行

memfd-exec

使用友好的接口直接从内存中运行可执行文件

8 个版本

0.2.1 2023年6月14日
0.2.0 2023年6月9日
0.1.5 2023年3月25日
0.1.4 2022年11月4日
0.1.3 2022年10月2日

#92操作系统

Download history 144/week @ 2024-03-11 168/week @ 2024-03-18 256/week @ 2024-03-25 258/week @ 2024-04-01 103/week @ 2024-04-08 153/week @ 2024-04-15 164/week @ 2024-04-22 158/week @ 2024-04-29 193/week @ 2024-05-06 166/week @ 2024-05-13 120/week @ 2024-05-20 151/week @ 2024-05-27 148/week @ 2024-06-03 134/week @ 2024-06-10 128/week @ 2024-06-17 164/week @ 2024-06-24

576 每月下载量
用于 68 个crate(66个直接)

MIT 许可证

62KB
1K SLoC

memfd_exec crates.io

这是一个非常简单的crate,允许只执行内存中的程序。简单来说,如果你有Linux可执行文件的内容在一个 Vec<u8> 中,你可以使用 memfd_exec 来执行程序,而无需它触及你的硬盘。这个用例可能包括

  • 将静态可执行文件与另一个程序捆绑在一起(例如,我创建这个包的动机是我想将静态构建的QEMU与 cantrace 一起分发)
  • 通过网络发送可执行文件并运行它们,以减少占用空间并提高吞吐量
  • 一些真正巧妙的想法,我还没有想到的,如果你有酷炫的用例,请随意向README提交PR或在 examples 中添加示例

使用

只需在您的 Cargo.toml 文件中包含 memfd-exec = "0.1.4" 即可。

功能

  • process::Command 的功能相匹配的API,唯一的区别是我们不执行任何来自磁盘的内容。
  • 只有两个依赖项

示例

运行通过网络下载的可执行文件

对于红队人员,此示例将下载并运行一个可执行文件,而无需将其写入磁盘。这可能无法绕过高级威胁保护,但至少不会留下巨大的磁盘占用空间!

use memfd_exec::{MemFdExecutable, Stdio};
use reqwest::blocking::get;

const URL: &str = "https://novafacing.github.io/assets/qemu-x86_64";
let resp = get(URL).unwrap();

// The `MemFdExecutable` struct is at near feature-parity with `std::process::Command`,
// so you can use it in the same way. The only difference is that you must provide the
// executable contents as a `Vec<u8>` as well as telling it the argv[0] to use.
let qemu = MemFdExecutable::new("qemu-x86_64", resp.bytes().unwrap().to_vec())
    // We'll just get the version here, but you can do anything you want with the
    // args.
    .arg("-version")
    // We'll capture the stdout of the process, so we need to set up a pipe.
    .stdout(Stdio::piped())
    // Spawn the process as a forked child
    .spawn()
    .unwrap();

// Get the output and status code of the process (this will block until the process
// exits)
let output = qemu.wait_with_output().unwrap();
assert!(output.status.into_raw() == 0);
// Print out the version we got!
println!("{}", String::from_utf8_lossy(&output.stdout));

捆绑并运行本地静态可执行文件

此项目的动机示例是将可执行文件与rust程序捆绑在一起,并能够直接从内存中运行可执行文件,而不是通过将可执行文件写入磁盘并作为命令调用它的繁琐且缓慢的过程。

本示例创建了一个可执行文件,其中包含一个捆绑的程序,该程序打开一个套接字,读取一些输入,然后打印出输入。当然,这个想法的逻辑扩展可能是使用静态的netcat构建或其他类似的东西。


use memfd_exec::{MemFdExecutable, Stdio};

const EXECUTABLE_FILE: &[u8] = include_bytes!("tets/test_static");

fn main() {
    const PORT = 1234;
    // We create an in-memory executable with an argv[0] "test" and an executable file
    // that we embedded in our rust binary
    let exe = MemFdExecutable::new("test", EXECUTABLE_FILE.to_vec())
        // We pass one arg, the port number to listen on
        .arg(format!("{}", PORT))
        // We tell it to use a pipe for stdout (stdin and stderr will default to Stdio::inherit())
        .stdout(Stdio::piped())
        // We spawn the child process as a forked child process
        .spawn()
        .expect("Failed to create process!");

    // Wait until the process finishes and print its output
    let output = exe.wait_with_output().unwrap();
    println!("Got output: {:?}", output.stdout);
}

测试

为了测试目的,您需要安装

  • clang
  • glibc-static
  • glibc

您还应该在您的系统上安装/bin/cat/bin/ls。这在绝大多数Linux系统中是默认的,但如果这个测试失败而它们缺失,请不要慌张。

依赖项

~1.5MB
~35K SLoC