Work on adding a puppet for the server side aswell

This commit is contained in:
Michael Mikovsky
2025-08-24 10:59:46 -06:00
parent 5f38d85e6c
commit 319c0fc222
13 changed files with 255 additions and 19 deletions
+154
View File
@@ -0,0 +1,154 @@
use libc::forkpty;
use nix::sys::wait::wait;
use nix::{
errno::Errno,
sys::{ptrace::Options, signal::Signal},
};
use std::{
env::args,
os::unix::process::CommandExt,
process::{self, Command},
thread,
time::Duration,
};
use log::{debug, error, info, trace, warn};
use nix::{
sys::{
ptrace,
wait::{self, WaitStatus},
},
unistd::{ForkResult, Pid, fork},
};
// pub type Error = Result<(), std::fmt::Error>;
#[derive(Debug)]
enum HavocError {
Wait,
Register(Errno),
Ptrace(Errno),
}
fn main() -> Result<(), std::fmt::Error> {
pretty_env_logger::formatted_builder()
.filter_level(log::LevelFilter::Trace)
.init();
let args = &(args().collect::<Vec<String>>())[1..];
match unsafe { fork() } {
Ok(ForkResult::Child) => run_child(args),
Ok(ForkResult::Parent { child }) => run_parent(child),
Err(e) => panic!("Could not fork main process: {}", e),
};
Ok(())
}
fn run_child(args: &[String]) {
info!("havoc child process executing as {}", process::id());
info!("Running: {:?}", args);
ptrace::traceme().expect("OS could not be bothered to trace me");
// loop {
// println!("test");
// thread::sleep(Duration::from_secs(1));
// }
// unsafe { exit(0) };
let e = Command::new(args.join(" ")).exec();
}
fn run_parent(pid: Pid) {
// wait for our child process to be ready
let ws = wait().expect("Parent failed waiting for child");
// info!("Child process ready with signal: {ws:?}, will ask it to continue untill syscall");
setup_tracing(pid).expect(format!("Parent failed to set up tracing for {pid:?}").as_str());
// trace_syscall(pid, None).expect(format!("Parent failed tracing for {pid:?}").as_str());
loop {
match ptrace::syscall(pid, None).map_err(HavocError::Ptrace) {
Ok(_) => { /* nop */ }
Err(e) => {
error!("error: {:?}", e);
break;
}
}
}
println!("pid: {}", pid);
}
fn wait_for_signal() -> Result<(), HavocError> {
match wait() {
Ok(WaitStatus::Stopped(pid_t, sig_num)) => handle_child_stopped(sig_num, pid_t),
Ok(WaitStatus::Exited(pid, exit_status)) => {
debug!("Child with pid: {} exited with status {}", pid, exit_status);
Ok(())
}
Ok(status) => {
warn!("Received unhandled wait status: {:?}", status);
Ok(())
}
Err(err) => {
error!("An error occurred: {:?}", err);
Err(HavocError::Wait)
}
}
}
fn handle_sigtrap(pid_t: Pid) -> Result<(), HavocError> {
let regs = ptrace::getregs(pid_t).map_err(HavocError::Register)?;
if regs.orig_rax == libc::SYS_msync as u64 {
if regs.rax == -libc::ENOSYS as u64 {
info!("Entry of syscall in {pid_t} : {}", regs.orig_rax);
} else {
info!("Exit of syscall in {pid_t} : {}", regs.orig_rax);
// handle_msync(msync_counter, regs, pid_t)?;
}
}
Ok(())
}
fn handle_child_stopped(sig_num: Signal, pid_t: Pid) -> Result<(), HavocError> {
match sig_num {
Signal::SIGTRAP => {
handle_sigtrap(pid_t)?;
trace_syscall(pid_t, None)
}
Signal::SIGSTOP => trace_syscall(pid_t, Some(Signal::SIGSTOP)),
// ... some corner cases like SIGWINCH
_ => trace_syscall(pid_t, Some(sig_num)),
}
}
fn trace_syscall(pid: Pid, sig_num: Option<Signal>) -> Result<(), HavocError> {
ptrace::syscall(pid, None).map_err(HavocError::Ptrace)?;
trace!("{} -> {:?}", pid, sig_num);
Ok(())
}
/// Setup the preace options to also trace fork, clone and vfork
fn setup_tracing(pid: Pid) -> Result<(), HavocError> {
// ptrace::setoptions(
// pid,
// Options::PTRACE_O_TRACEFORK
// .union(Options::PTRACE_O_TRACECLONE)
// .union(Options::PTRACE_O_TRACEVFORK),
// )
ptrace::attach(pid).map_err(HavocError::Ptrace)
}
// fn next_syscall(child_pid: libc::pid_t) -> Result<i64, OsError> {
// match WaitPID::from_process(child_pid)? {
// WaitPID::SysCall { .. } => (),
// x => {
// return Err(OsError::new(
// ErrorKind::Other,
// format!("Expected syscall event, got {:?} instead", x),
// ));
// }
// }
// let number = get_syscall_number(child_pid)?;
// p_trace_syscall(child_pid, None)?;
// Ok(number)
// }