This commit is contained in:
Audrey 2024-04-11 15:29:18 -07:00
commit e9d96712b2
4 changed files with 281 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

63
Cargo.lock generated Normal file
View File

@ -0,0 +1,63 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitflags"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "linux-personality"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c1a976577555db53f0a98e9b236e1ebff1a0a1793227ed1b88d7b9a03dda93"
dependencies = [
"bitflags 0.7.0",
"libc",
]
[[package]]
name = "nix"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.5.0",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
name = "ontology"
version = "0.1.0"
dependencies = [
"linux-personality",
"nix",
]

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "ontology"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nix = { version = "0.28.0", features = ["ptrace", "process"] }
linux-personality = "1.0.0"

207
src/main.rs Normal file
View File

@ -0,0 +1,207 @@
use linux_personality::{personality, ADDR_NO_RANDOMIZE};
use nix::{
sys::{
ptrace,
signal::Signal,
wait::{wait, WaitStatus},
},
unistd::{fork, ForkResult, Pid},
};
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
env::args,
ffi::{CStr, CString},
os::unix::{fs::FileExt, process::CommandExt},
process::Command,
};
#[derive(Debug)]
pub enum Event {
Fork { child: Pid },
Exec { prog: CString },
Exit { code: i32 },
}
#[derive(Clone, Debug)]
pub struct Identifier {
machine: i32,
pid: Pid,
}
#[derive(Debug)]
pub struct LogEntry {
ident: Identifier,
event: Event,
}
impl Event {
pub fn log(self, ident: Identifier, event_log: &mut Vec<LogEntry>) {
event_log.push(LogEntry { ident, event: self });
}
}
fn run_child() {
ptrace::traceme().unwrap();
personality(ADDR_NO_RANDOMIZE).unwrap();
let mut args = args();
args.next().unwrap();
let argv0 = args.next().unwrap();
panic!("exec: {}", Command::new(argv0).args(args).exec());
}
fn run_parent(first_child: Pid) {
//ptrace::attach(first_child).unwrap();
//assert!(matches!(dbg!(wait().unwrap()), WaitStatus::Stopped(c, s) if c == first_child && s as i32 == nix::libc::SIGSTOP));
//ptrace::cont(first_child, None).unwrap();
assert!(
matches!((wait().unwrap()), WaitStatus::Stopped(c, s) if c == first_child && s as i32 == nix::libc::SIGTRAP)
);
ptrace::setoptions(
first_child,
ptrace::Options::PTRACE_O_TRACESYSGOOD
| ptrace::Options::PTRACE_O_TRACEFORK
| ptrace::Options::PTRACE_O_TRACEVFORK
| ptrace::Options::PTRACE_O_TRACECLONE
| ptrace::Options::PTRACE_O_TRACEEXEC
| ptrace::Options::PTRACE_O_TRACEEXIT,
)
.unwrap();
ptrace::syscall(first_child, None).unwrap();
let events = RefCell::new(vec![]);
let fdcache = RefCell::new(HashMap::new());
let mut pending_execve = HashMap::new();
#[derive(PartialEq, Debug)]
enum ForkState {
SeenFork,
SeenStop,
}
let mut suppress_stops = HashMap::new();
fn root_pid(pid: Pid) -> Identifier {
Identifier { machine: 0, pid }
}
let log = |event: Event, ident: Identifier| {
events.borrow_mut().push(dbg!(LogEntry { event, ident }));
};
let readstr = |pid: Pid, addr: u64| -> std::io::Result<CString> {
let mut fdcache = fdcache.borrow_mut();
let fp = fdcache
.entry(pid)
.or_insert_with(|| std::fs::File::open(format!("/proc/{pid}/mem")).unwrap());
let mut buf = vec![];
loop {
buf.extend_from_slice(&[0; 1024]);
let len = buf.len();
fp.read_at(&mut buf[len - 1024..], addr)?;
match CStr::from_bytes_until_nul(&buf) {
Ok(cstr) => return Ok(cstr.to_owned()),
Err(_) => continue,
}
}
};
enum ResumeType {
Cont(Option<Signal>),
Syscall(Option<Signal>),
Hold,
}
loop {
let waited = dbg!(wait());
let signal = match waited {
Ok(WaitStatus::PtraceEvent(pid, _sig, event))
if event == ptrace::Event::PTRACE_EVENT_VFORK as i32
|| event == ptrace::Event::PTRACE_EVENT_FORK as i32
|| event == ptrace::Event::PTRACE_EVENT_CLONE as i32 =>
{
let newpid = Pid::from_raw(ptrace::getevent(pid).unwrap() as i32);
log(Event::Fork { child: newpid }, root_pid(pid));
if suppress_stops.insert(newpid, ForkState::SeenFork) == Some(ForkState::SeenStop) {
suppress_stops.remove(&newpid);
ResumeType::Syscall(None)
} else {
ResumeType::Hold
}
}
Ok(WaitStatus::PtraceEvent(pid, _sig, event))
if event == ptrace::Event::PTRACE_EVENT_EXEC as i32 =>
{
log(
Event::Exec {
prog: pending_execve.remove(&pid).unwrap(),
},
root_pid(pid),
);
ResumeType::Syscall(None)
}
Ok(WaitStatus::PtraceSyscall(pid)) => {
let regs = ptrace::getregs(pid).unwrap();
match (dbg!(regs.orig_rax)) as i64 {
nix::libc::SYS_execve => {
let Ok(name) = (readstr(pid, regs.rdi)) else {
continue;
};
pending_execve.insert(pid, name);
}
nix::libc::SYS_execveat => {
let Ok(name) = (readstr(pid, regs.rsi)) else {
continue;
};
pending_execve.insert(pid, name);
}
_ => {}
}
ResumeType::Syscall(None)
GROUP STOP????
}
Ok(WaitStatus::Exited(pid, code)) => {
fdcache.borrow_mut().remove(&pid);
log(Event::Exit { code }, root_pid(pid));
continue;
}
Ok(WaitStatus::Stopped(pid, signal)) => {
if signal == Signal::SIGSTOP {
if suppress_stops.insert(pid, ForkState::SeenStop) == Some(ForkState::SeenFork)
{
suppress_stops.remove(&pid);
ResumeType::Syscall(None)
} else {
ResumeType::Hold
}
} else if signal == Signal::SIGTRAP {
panic!("does this happen?");
ResumeType::Syscall(None)
} else {
ResumeType::Cont(Some(signal))
}
}
Err(nix::errno::Errno::ECHILD) => {
break;
}
_ => todo!(),
};
if let Some(pid) = waited.unwrap().pid() {
match signal {
ResumeType::Cont(signal) => ptrace::cont(pid, signal).unwrap(),
ResumeType::Syscall(signal) => ptrace::syscall(pid, signal).unwrap(),
ResumeType::Hold => {}
}
}
}
}
fn main() {
match unsafe { fork() } {
Ok(ForkResult::Child) => {
run_child();
}
Ok(ForkResult::Parent { child }) => {
run_parent(child);
}
Err(e) => panic!("fork: {e}"),
}
}