steroid 0.5.0

A lightweight framework for dynamic binary instrumentation
Documentation
//! Steroid is a dynamic binary instrumentation library with a strong emphasis on safety.
//!
//! This library aims at instrumenting other running processes, giving the user a high-level
//! interface to system calls such as [`ptrace(2)`] and the possibilities it gives. Steroid uses
//! lifetimes and mutability a lot in order to make sense of a remote process' running
//! state. The two main data types are [`TargetProcess`] and [`TargetController`].
//!
//! # The Process and Controller tandem
//!
//! The former represents a running process. An instance of this type is normally mutably available
//! while a process in running. This means the process can be stopped. Once a process is stopped
//! using [`wait`], a [`RunningState`] is returned. This object tells the user whether the process
//! is stopped but still alive or if it is dead because it has exited or was killed.
//!
//! ```
//! # use anyhow::Error;
//! # use steroid::process::spawn_process;
//! # use steroid::run::{Executing, RunningState};
//! # let process = spawn_process("/bin/ls", ["-l"])?;
//! match process.wait()? {
//!     RunningState::Alive(_) => println!("We can manipulate the stopped process"),
//!     RunningState::Exited { reason, .. } => println!("The process has exited: {}", reason),
//! }
//! # Ok::<(), Error>(())
//! ```
//!
//! If the process is just stopped, the user is provided with a [`TargetController`]. This
//! controller takes the ownership of the process, forbiding any manipulation of the
//! [`TargetProcess`] that requires the process running. On the other hand, the controller enables
//! any manipulation that requires a stopped process, such as reading the process' registers. The
//! method [`resume`] of the controller consumes the latter and returns the [`TargetProcess`],
//! allowing the process to be manipulated again. A steroid client usually consists of alternating
//! manipulations of a process and its successive controllers.
//!
//! ```
//! # use anyhow::Error;
//! use steroid::process::spawn_process;
//! use steroid::run::{Executing, RunningState, Reason};
//!
//! let process = spawn_process("/bin/ls", ["-l"])?;
//! if let RunningState::Alive(mut ctrl) = process.wait()? {
//!     // The process is now inaccessible as mutable.
//!     let regs = ctrl.get_registers()?;
//!     println!("{:#x?}", regs);
//!     let process = ctrl.resume()?;
//!     // The controller is dead, the process is available again.
//!     let pid = process.pid();
//!     // The wait method returns the process state:
//!     // * alive (with a controller)
//!     // * dead (with the reason why it died)
//!     let state = process.wait()?;
//!     assert!(state.has_exited());
//! }
//! # Ok::<(), Error>(())
//! ```
//!
//! In some cases, the user knows that when calling [`wait`], the process must be stopped and an
//! exit must be considered an error. Instead of using `match` and writing complicated code,
//! [`RunningState`] has a method [`assume_alive`] that returns a [`TargetController`] if the
//! process is indeed alive or fails with an error if it has exited.
//!
//! ```
//! # use anyhow::Error;
//! use steroid::process::spawn_process;
//! use steroid::run::Executing;
//!
//! let process = spawn_process("/bin/ls", ["-l"])?;
//! let mut ctrl = process.wait()?.assume_alive()?;
//! // The process is now inaccessible as mutable.
//! let regs = ctrl.get_registers()?;
//! println!("{:#x?}", regs);
//! # Ok::<(), Error>(())
//! ```
//!
//! ## `ptrace` implies `!Send + !Sync`
//!
//! Steroid heavily uses the system call [`ptrace(2)`] to control remote processes. It is important
//! to understand that a tracee is traced by a tracer thread. This means that in a steroid client,
//! only the thread that created a [`TargetProcess`], a [`Thread`] or a [`TargetController`] can
//! actually use them. For this reason, these three types are marked as both [`!Send`][Send] and
//! [`!Sync`][Sync].
//!
//! # Remote system calls
//!
//! Steroid has a whole module dedicated to call syscalls from the remote process it is
//! controlling. The insight is to take advantage of the process being stopped at some point to make
//! it execute pieces of code in its own context. For instance, it would be possible to make the
//! remote process allocate memory for future use by the steroid client:
//!
//! ```
//! # use std::path::PathBuf;
//! # use anyhow::Error;
//! use nix::libc::{MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE};
//! # use nix::sys::wait::WaitStatus;
//! # use steroid::process::spawn_process;
//! # use steroid::run::{Executing, Reason};
//! # use steroid::breakpoint::{breakpoint, Mode};
//! use steroid::syscall;
//!
//! # let mut path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
//! # path_buf.push("resources/test/say_hello_no_pie");
//! # let process = spawn_process::<_, _, &str>(path_buf, vec![])?;
//! # let mut ctrl_start = process.wait()?.assume_alive()?;
//! # breakpoint(&mut ctrl_start, 0x401126, Mode::OneShot)?;
//! # let process = ctrl_start.resume()?;
//! let mut ctrl = process.wait()?.assume_alive()?;
//! let flags = (MAP_PRIVATE | MAP_ANONYMOUS) as u64;
//! let prot = (PROT_READ | PROT_WRITE) as u64; // permissions: read and write
//! let size = 1000;
//! let fd = -1_i64 as u64; // fd must be -1, but syscall::mmap takes u64. It's ok here.
//! let address = syscall::mmap(&mut ctrl, 0, size, prot, flags, fd, 0)?;
//! println!("New page mapped at address {:#x}", address);
//!
//! let process = ctrl.resume()?;
//! # let pid = process.pid();
//! # let state = process.wait()?;
//! # assert!(state.has_exited(), "{}", state.reason());
//! # Ok::<(), Error>(())
//! ```
//!
//! # Limitations and useful crates
//!
//! Steroid is a dynamic binary instrumentation library that relies heavily on Linux features such
//! as [`ptrace(2)`] and [`procfs(5)`]. Therefore, it will only work on Linux. Some efforts can be
//! made to port it to Unix operating systems such as the main BSDs. In addition to that, steroid
//! only targets `x86_64`. It may be possible to port it to different architectures but it is far
//! from being a priority right now.
//!
//! Steroid is still very early in its development and does not provide many features. Nonetheless
//! some excellent crates complete it very well, giving missing features that fall out of the strict
//! scope of steroid's goals:
//!
//! - [`elf`]: crate providing a safe interface to read ELF object files
//! - [`gimli`]: crate providing features to read and write DWARF debugging format
//! - [`capstone`]: bindings to the capstone library disassembly framework
//!
//! [`ptrace(2)`]: https://man7.org/linux/man-pages/man2/ptrace.2.html
//! [`procfs(5)`]: https://man7.org/linux/man-pages/man5/proc.5.html
//! [`TargetProcess`]: process/struct.TargetProcess.html
//! [`TargetController`]: process/struct.TargetController.html
//! [`Thread`]: thread/struct.Thread.html
//! [`RunningState`]: process/enum.RunningState.html
//! [`assume_alive`]: process/enum.RunningState.html#method.assume_alive
//! [`wait`]: process/struct.TargetProcess.html#method.wait
//! [`resume`]: process/struct.TargetController.html#method.resume
//! [`elf`]: https://docs.rs/elf
//! [`gimli`]: https://docs.rs/gimli
//! [`capstone`]: https://docs.rs/capstone
#![warn(clippy::pedantic)]
#![warn(clippy::nursery)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::similar_names)]

pub mod breakpoint;
pub mod buffer;
pub mod error;
pub mod mapping;
pub mod process;
pub mod run;
pub mod syscall;
pub mod thread;