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
+8 -5
View File
@@ -6,14 +6,17 @@ edition = "2024"
[dependencies] [dependencies]
ctor = "0.5.0" ctor = "0.5.0"
libc = "0.2.175" libc = "0.2.175"
log = "0.4.27"
nix = { version = "0.30.1", features = ["process", "fs", "feature", "ptrace"] }
pretty_env_logger = "0.5.0" pretty_env_logger = "0.5.0"
subprocess = "0.2.9"
# syscalls = "0.6.18" # syscalls = "0.6.18"
syscall_lib = { path = "syscall_lib" } syscall_lib = { path = "syscall_lib" }
syscaller = "0.2.2" syscaller = "0.2.2"
[build-dependencies] # [build-dependencies]
cmake = "0.1" # cmake = "0.1"
[lib] # [lib]
name = "intercept" # name = "intercept"
crate-type = ["rlib", "cdylib"] # crate-type = ["rlib", "cdylib"]
Executable
BIN
View File
Binary file not shown.
View File
+7 -2
View File
@@ -10,6 +10,7 @@ use std::{
use log::{error, info, trace}; use log::{error, info, trace};
use nix::libc::{self, MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE, user_regs_struct}; use nix::libc::{self, MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE, user_regs_struct};
use nix::sys::ptrace;
use nix::unistd::Pid; use nix::unistd::Pid;
use subprocess::{Popen, PopenConfig, Redirection}; use subprocess::{Popen, PopenConfig, Redirection};
use syscall_lib::Syscall; use syscall_lib::Syscall;
@@ -61,7 +62,7 @@ fn handle_connection(stream: &mut TcpStream) -> Result<(), std::io::Error> {
let syscall = Syscall::decode(&buf).unwrap(); let syscall = Syscall::decode(&buf).unwrap();
trace!("{:?}", syscall); // trace!("{:?}", syscall);
// let result = match decoded { // let result = match decoded {
// Syscall::Write(..) => 0, // Syscall::Write(..) => 0,
@@ -82,7 +83,9 @@ fn handle_connection(stream: &mut TcpStream) -> Result<(), std::io::Error> {
} }
}; };
let result = result.rax; // ptrace:;
let result = result.gs as i64;
// let result: u64 = result.unwrap().rax; // let result: u64 = result.unwrap().rax;
@@ -90,6 +93,8 @@ fn handle_connection(stream: &mut TcpStream) -> Result<(), std::io::Error> {
// let result = 0; // let result = 0;
// ptrace::
trace!("{:?} -> {:?}", syscall, result); trace!("{:?} -> {:?}", syscall, result);
let bytes: [u8; 8] = result.to_be_bytes(); let bytes: [u8; 8] = result.to_be_bytes();
+1 -1
View File
@@ -196,7 +196,7 @@ impl UserProcess {
r8: u64, r8: u64,
r9: u64, r9: u64,
) -> HostResult<user_regs_struct> { ) -> HostResult<user_regs_struct> {
log::trace!("UserProcess {} Syscall: {:#?}", self.pid, sys_call); // log::trace!("UserProcess {} Syscall: {:#?}", self.pid, sys_call);
let syscall_instruction = [0x0Fu8, 0x05u8]; let syscall_instruction = [0x0Fu8, 0x05u8];
// Cache original registers, original instruction pointer (rip), and the original instructions // Cache original registers, original instruction pointer (rip), and the original instructions
+1 -1
View File
@@ -43,7 +43,7 @@ impl Host {
let mut response = [0u8; 8]; let mut response = [0u8; 8];
self.reader.read_exact(&mut response).unwrap(); self.reader.read_exact(&mut response).unwrap();
let response = u64::from_be_bytes(response); let response = i64::from_be_bytes(response);
println!("{:?}", response); println!("{:?}", response);
View File
+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)
// }
+1
View File
@@ -9,6 +9,7 @@ bincode = "2.0.1"
# kernel = "6.17" # kernel = "6.17"
# kernel = "1.0.0" # kernel = "1.0.0"
libc = "0.2.175" libc = "0.2.175"
log = "0.4.27"
nix = "0.30.1" nix = "0.30.1"
syscaller = "0.2.2" syscaller = "0.2.2"
-1
View File
@@ -6,7 +6,6 @@ const CONFIG: bincode::config::Configuration = bincode::config::standard();
use std::{ use std::{
collections::HashMap, collections::HashMap,
fmt::Debug, fmt::Debug,
marker::PhantomData,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
+13 -4
View File
@@ -1,4 +1,5 @@
use bincode::{Decode, Encode}; use bincode::{Decode, Encode};
use log::error;
use crate::{types, types::*}; use crate::{types, types::*};
@@ -12,11 +13,19 @@ impl Syscall {
} }
pub fn decode(bytes: &[u8]) -> Option<Self> { pub fn decode(bytes: &[u8]) -> Option<Self> {
if let Ok((decoded, _)) = bincode::decode_from_slice(&bytes[..], crate::CONFIG) { match bincode::decode_from_slice(&bytes[..], crate::CONFIG) {
Some(decoded) Ok((decoded, _)) => Some(decoded),
} else { Err(e) => {
None error!("Decoding error: {}", e);
None
}
} }
// if let Ok((decoded, _)) = bincode::decode_from_slice(&bytes[..], crate::CONFIG) {
// Some(decoded)
// } else {
// None
// }
} }
} }
+62 -5
View File
@@ -1,7 +1,12 @@
use std::ffi::CStr;
use std::fmt::Debug; use std::fmt::Debug;
use std::ptr;
use std::{ffi::CStr, mem::transmute};
use bincode::{Decode, Encode}; use bincode::de::read::Reader;
use bincode::enc::write::Writer;
use bincode::error::DecodeError;
use bincode::{BorrowDecode, Decode, Encode};
use log::trace;
use crate::{AsPtr, types::MAX_STR_LEN}; use crate::{AsPtr, types::MAX_STR_LEN};
@@ -15,12 +20,11 @@ fn trunc(str: &str) -> String {
out_str out_str
} }
#[derive(Encode, Decode)] pub struct StrRef(pub *const libc::c_char);
pub struct StrRef(pub libc::c_ulong);
impl StrRef { impl StrRef {
pub fn new(ptr: libc::c_ulong) -> Self { pub fn new(ptr: libc::c_ulong) -> Self {
Self(ptr) Self(ptr as *const libc::c_char)
} }
pub fn read(&self) -> String { pub fn read(&self) -> String {
let ptr = self.0 as *const libc::c_char; let ptr = self.0 as *const libc::c_char;
@@ -43,3 +47,56 @@ impl AsPtr for StrRef {
self.0 as libc::c_ulong self.0 as libc::c_ulong
} }
} }
impl<'de> Encode for StrRef {
fn encode<E: bincode::enc::Encoder>(
&self,
encoder: &mut E,
) -> Result<(), bincode::error::EncodeError> {
let data = unsafe { CStr::from_ptr(self.0 as *const libc::c_char) };
let data = data.to_bytes_with_nul();
// println!("len: {}, data: {:?}", data.len(), data);
encoder.writer().write(&data.len().to_be_bytes())?;
data.encode(encoder)?;
Ok(())
}
}
impl<'de, Context> Decode<Context> for StrRef {
fn decode<D: bincode::de::Decoder<Context = Context>>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
let mut len = [0u8; 8];
decoder.reader().read(&mut len)?;
let len = usize::from_be_bytes(len) + 1;
let data = &decoder.reader().peek_read(len).unwrap()[1..];
// [0u8; 32];
// decoder.reader().read(&mut data)?;
// trace!("len: {}, data: {:?}", len, data);
let data = CStr::from_bytes_with_nul(&data).unwrap();
let ptr = data.as_ptr();
Ok(StrRef(ptr))
}
}
impl<'de, Context> BorrowDecode<'de, Context> for StrRef {
fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = Context>>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
let mut len = [0u8; 8];
decoder.reader().read(&mut len)?;
let len = usize::from_be_bytes(len) + 1;
let data = &decoder.reader().peek_read(len).unwrap()[1..];
let data = CStr::from_bytes_with_nul(&data).unwrap();
let ptr = data.as_ptr();
Ok(StrRef(ptr))
}
}
+8
View File
@@ -0,0 +1,8 @@
#include <stdio.h>
const char line[] = "12345";
int main() {
printf("My pointer is: %p\n", *line);
return 0;
}