3 个版本

0.0.4 2023年5月28日
0.0.3 2023年5月27日
0.0.2 2023年5月27日
0.0.1 2023年5月27日

500Unix API 中排名

每月 下载 33

无许可证

47KB
531

greenhook

https://crates.io/crates/greenhook

Greenhook 是一个基于seccomp-unotify的系统调用钩子库。它是由 https://github.com/pdlan/binder 改编而来的。

如果你想要寻找除 LD_PRELOADptrace 之外的替代方案,可以尝试它。但是请注意,seccomp unotify 并不完全 替代这些技术,在开始之前请花一些时间阅读 seccomp_unotify(2)

要充分利用这个库,你需要有一个 >= 5.9.0 的内核版本。如果你想在Docker或其他容器中运行它(允许 process_vm_readv()pidfd_getfd() 在没有能力的情况下运行),你需要 一个特殊的seccomp策略文件,如下所示

# docker run --security-opt seccomp=assets/seccomp.json ...

还需要安装libseccomp头文件和库

$ sudo apt install libseccomp-dev

示例

你可以在测试代码中找到一些示例。这里有一个简单的示例,它通过钩子 geteuid(2) 来使程序像 whoami(1) 一样工作,即使你不是root用户

use std::process::Command;

use greenhook::{Supervisor, UNotifyEventRequest};
use libseccomp::ScmpSyscall;

fn geteuid_handler(req: &UNotifyEventRequest) -> libseccomp::ScmpNotifResp {
    req.return_syscall(0)
}

fn main() {
    env_logger::init();
    // Get argv[1]
    let program = std::env::args().nth(1).unwrap();
    let mut supervisor = Supervisor::new(2).unwrap();
    supervisor.insert_handler(ScmpSyscall::new("geteuid"), geteuid_handler);
    let mut cmd = Command::new(program);
    let (mut child, thread_handle, pool) = supervisor.exec(&mut cmd).unwrap();
    let _ = Supervisor::wait(&mut child, thread_handle, pool).unwrap();
}

运行此命令

> cargo run --example geteuid -- whoami
root
> whoami
user

一个更复杂的示例,通过钩子 openat(2)/etc/passwd 替换为 /etc/resolv.conf

use std::{process::Command, ffi::CStr, fs::File, os::fd::AsRawFd};

use greenhook::{Supervisor, UNotifyEventRequest, RemoteProcess};
use libseccomp::ScmpSyscall;
use log::info;
use nix::{unistd::Pid, libc};

fn openat_handler(req: &UNotifyEventRequest) -> libseccomp::ScmpNotifResp {
    let path = req.get_request().data.args[1];
    let remote = RemoteProcess::new(Pid::from_raw(req.get_request().pid as i32)).unwrap();
    let mut buf = [0u8; 256];
    remote.read_mem(&mut buf, path as usize).unwrap();
    // debug!("open (read from remote): {:?}", buf);
    let path = CStr::from_bytes_until_nul(&buf).unwrap();
    if !req.is_valid() {
        return req.fail_syscall(libc::EACCES);
    }
    info!("open (path CStr): {:?}", path);
    if path.to_str().unwrap() == "/etc/passwd" {
        // open /etc/resolv.conf instead
        let file = File::open("/etc/resolv.conf").unwrap();
        let fd = file.as_raw_fd();
        let remote_fd = req.add_fd(fd).unwrap();
        req.return_syscall(remote_fd as i64)
    } else {
        unsafe { req.continue_syscall() }
    }
}

fn main() {
    env_logger::init();
    // Get argv[1..]
    let args = std::env::args().skip(1).collect::<Vec<_>>();
    if args.len() == 0 {
        panic!("Usage: {} <program> [args...]", std::env::args().nth(0).unwrap());
    }
    let mut supervisor = Supervisor::new(2).unwrap();
    supervisor.insert_handler(ScmpSyscall::new("openat"), openat_handler);
    let mut cmd = Command::new(args[0].clone());
    let cmd = cmd.args(&args[1..]);
    let (mut child, thread_handle, pool) = supervisor.exec(cmd).unwrap();
    let _ = Supervisor::wait(&mut child, thread_handle, pool).unwrap();
}

运行此命令

> RUST_LOG=info cargo run --example openat -- cat /etc/passwd
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/home/taoky/Projects/greenhook/target/debug/deps/glibc-hwcaps/x86-64-v3/libc.so.6"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/home/taoky/Projects/greenhook/target/debug/deps/glibc-hwcaps/x86-64-v2/libc.so.6"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/home/taoky/Projects/greenhook/target/debug/deps/libc.so.6"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/home/taoky/Projects/greenhook/target/debug/glibc-hwcaps/x86-64-v3/libc.so.6"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/home/taoky/Projects/greenhook/target/debug/glibc-hwcaps/x86-64-v2/libc.so.6"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/home/taoky/Projects/greenhook/target/debug/libc.so.6"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/home/taoky/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/glibc-hwcaps/x86-64-v3/libc.so.6"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/home/taoky/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/glibc-hwcaps/x86-64-v2/libc.so.6"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/home/taoky/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libc.so.6"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/home/taoky/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/glibc-hwcaps/x86-64-v3/libc.so.6"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/home/taoky/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/glibc-hwcaps/x86-64-v2/libc.so.6"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/home/taoky/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/libc.so.6"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/etc/ld.so.cache"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/usr/lib/libc.so.6"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/usr/lib/locale/locale-archive"
[2023-05-27T14:39:57Z INFO  openat] open (path CStr): "/etc/passwd"
# Generated by NetworkManager
...

请参阅examples/binder.rs以获取更复杂的示例。它类似于Rust中https://github.com/pdlan/binder的逻辑。

限制

  1. 您的钩子函数是由管理进程(线程)执行的,而不是被管理的进程!这意味着当您需要代表被管理进程执行某些操作时,可能会遇到困难。
  2. 小心TOCTOU攻击!Seccomp unotify在处理系统调用时不会停止整个进程,因此存在这样的可能性:在管理进程检查它们之后,被管理进程可能会更改系统调用参数,而continue_syscall可能是有危险的(因此在这里标记为unsafe)。
  3. 处理信号可能很麻烦。信号可能会中断系统调用或重新启动它们,但管理进程对此一无所知。尝试在您的函数中检查请求的有效性以减轻这个问题。更多详细信息请参阅seccomp_unotify(2)

依赖

~4–16MB
~184K SLoC