wip
This commit is contained in:
commit
e9d96712b2
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -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",
|
||||
]
|
|
@ -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"
|
|
@ -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}"),
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue