| Crates.io | lazypoline-rs |
| lib.rs | lazypoline-rs |
| version | 0.2.0 |
| created_at | 2025-03-15 15:00:34.347892+00 |
| updated_at | 2025-03-16 02:11:52.278244+00 |
| description | A framework for building syscall interposers for user-space Linux applications |
| homepage | |
| repository | |
| max_upload_size | |
| id | 1593613 |
| size | 158,204 |
A comprehensive framework for building syscall interposers in Rust.
The Lazypoline Framework enables you to build efficient, exhaustive, and expressive syscall interposers for user-space Linux applications. It uses a hybrid interposition mechanism based on Syscall User Dispatch (SUD) and binary rewriting to exhaustively intercept all syscalls with maximum efficiency.
This framework is a Rust re-implementation and extension of the original lazypoline system described in the paper "System Call Interposition Without Compromise" (DSN'24 paper).
echo 0 | sudo tee /proc/sys/vm/mmap_min_addr)Add lazypoline to your Cargo.toml:
[dependencies]
lazypoline-rs = "0.2.0"
Here's a simple example that traces all syscalls:
use lazypoline::{self, SyscallContext, SyscallAction};
#[lazypoline::syscall_handler]
fn handle_open(ctx: &mut SyscallContext) -> SyscallAction {
println!("Open syscall: {}", unsafe { std::ffi::CStr::from_ptr(ctx.args.rdi as *const i8).to_string_lossy() });
SyscallAction::Allow
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize the interposer
let interposer = lazypoline::new()
.handler(HandleOpen::new())
.trace(true)
.build()?
.init()?;
// Your application code here
// The interposer is automatically cleaned up when dropped
Ok(())
}
You can use lazypoline to intercept syscalls in existing binaries:
LIBLAZYPOLINE="path/to/liblazypoline.so" LD_PRELOAD="path/to/libbootstrap.so" your_binary
The Lazypoline Framework provides a simple, composable API:
let interposer = lazypoline::new()
.handler(my_handler)
.filter(my_filter)
.trace(true)
.build()?
.init()?;
struct BlockWriteHandler;
impl SyscallHandler for BlockWriteHandler {
fn handle_syscall(&self, ctx: &mut SyscallContext) -> SyscallAction {
if ctx.syscall == Syscall::write {
println!("Blocking write to fd {}", ctx.args.rdi);
SyscallAction::Block(-libc::EPERM)
} else {
SyscallAction::Allow
}
}
}
use lazypoline::interposer::filter::BlockListFilter;
let mut filter = BlockListFilter::new([
Syscall::execve,
Syscall::fork,
Syscall::vfork
]);
let interposer = lazypoline::new()
.filter(filter)
.build()?
.init()?;
#[lazypoline::syscall_handler]
fn modify_args(ctx: &mut SyscallContext) -> SyscallAction {
if ctx.syscall == Syscall::open {
// Change the first argument (path)
let mut new_args = ctx.args;
new_args.rdi = "/dev/null\0".as_ptr() as u64;
SyscallAction::Modify(new_args)
} else {
SyscallAction::Allow
}
}
Build the libraries with Cargo:
cargo build --release --workspace
This builds:
target/release/liblazypoline.so - The main librarytarget/release/libbootstrap.so - The bootstrap loaderFor proper permissions:
sudo setcap cap_sys_admin,cap_sys_rawio+ep target/release/libbootstrap.so
The Lazypoline Framework consists of several components:
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the GPL v3 License - see the LICENSE file for details.
This is a Rust extension of the original lazypoline project by Adriaan Jacobs et al. Check out their paper and code at github.com/lazypoline/lazypoline.