diff --git a/Cargo.toml b/Cargo.toml index 16b2c99..da545c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,14 +6,17 @@ edition = "2024" [dependencies] ctor = "0.5.0" libc = "0.2.175" +log = "0.4.27" +nix = { version = "0.30.1", features = ["process", "fs", "feature", "ptrace"] } pretty_env_logger = "0.5.0" +subprocess = "0.2.9" # syscalls = "0.6.18" syscall_lib = { path = "syscall_lib" } syscaller = "0.2.2" -[build-dependencies] -cmake = "0.1" +# [build-dependencies] +# cmake = "0.1" -[lib] -name = "intercept" -crate-type = ["rlib", "cdylib"] +# [lib] +# name = "intercept" +# crate-type = ["rlib", "cdylib"] diff --git a/a.out b/a.out new file mode 100755 index 0000000..4ed5aec Binary files /dev/null and b/a.out differ diff --git a/build.rs b/build_old.rs similarity index 100% rename from build.rs rename to build_old.rs diff --git a/client/src/main.rs b/client/src/main.rs index 2b8fa46..6972006 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -10,6 +10,7 @@ use std::{ use log::{error, info, trace}; use nix::libc::{self, MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE, user_regs_struct}; +use nix::sys::ptrace; use nix::unistd::Pid; use subprocess::{Popen, PopenConfig, Redirection}; use syscall_lib::Syscall; @@ -61,7 +62,7 @@ fn handle_connection(stream: &mut TcpStream) -> Result<(), std::io::Error> { let syscall = Syscall::decode(&buf).unwrap(); - trace!("{:?}", syscall); + // trace!("{:?}", syscall); // let result = match decoded { // 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; @@ -90,6 +93,8 @@ fn handle_connection(stream: &mut TcpStream) -> Result<(), std::io::Error> { // let result = 0; + // ptrace:: + trace!("{:?} -> {:?}", syscall, result); let bytes: [u8; 8] = result.to_be_bytes(); diff --git a/client/src/proc.rs b/client/src/proc.rs index 12eb8f4..b1d4690 100644 --- a/client/src/proc.rs +++ b/client/src/proc.rs @@ -196,7 +196,7 @@ impl UserProcess { r8: u64, r9: u64, ) -> HostResult { - log::trace!("UserProcess {} Syscall: {:#?}", self.pid, sys_call); + // log::trace!("UserProcess {} Syscall: {:#?}", self.pid, sys_call); let syscall_instruction = [0x0Fu8, 0x05u8]; // Cache original registers, original instruction pointer (rip), and the original instructions diff --git a/src/host.rs b/src/host.rs index e9a9e2d..afc5d5c 100644 --- a/src/host.rs +++ b/src/host.rs @@ -43,7 +43,7 @@ impl Host { let mut response = [0u8; 8]; self.reader.read_exact(&mut response).unwrap(); - let response = u64::from_be_bytes(response); + let response = i64::from_be_bytes(response); println!("{:?}", response); diff --git a/src/lib.rs b/src/lib_old.rs similarity index 100% rename from src/lib.rs rename to src/lib_old.rs diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..6522df7 --- /dev/null +++ b/src/main.rs @@ -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::>())[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) -> 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 { +// 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) +// } diff --git a/syscall_lib/Cargo.toml b/syscall_lib/Cargo.toml index a79fc57..2849e0a 100644 --- a/syscall_lib/Cargo.toml +++ b/syscall_lib/Cargo.toml @@ -9,6 +9,7 @@ bincode = "2.0.1" # kernel = "6.17" # kernel = "1.0.0" libc = "0.2.175" +log = "0.4.27" nix = "0.30.1" syscaller = "0.2.2" diff --git a/syscall_lib/src/lib.rs b/syscall_lib/src/lib.rs index 659b032..721eb08 100644 --- a/syscall_lib/src/lib.rs +++ b/syscall_lib/src/lib.rs @@ -6,7 +6,6 @@ const CONFIG: bincode::config::Configuration = bincode::config::standard(); use std::{ collections::HashMap, fmt::Debug, - marker::PhantomData, sync::{Arc, Mutex}, }; diff --git a/syscall_lib/src/syscall/mod.rs b/syscall_lib/src/syscall/mod.rs index aab3f44..e55ea3c 100644 --- a/syscall_lib/src/syscall/mod.rs +++ b/syscall_lib/src/syscall/mod.rs @@ -1,4 +1,5 @@ use bincode::{Decode, Encode}; +use log::error; use crate::{types, types::*}; @@ -12,11 +13,19 @@ impl Syscall { } pub fn decode(bytes: &[u8]) -> Option { - if let Ok((decoded, _)) = bincode::decode_from_slice(&bytes[..], crate::CONFIG) { - Some(decoded) - } else { - None + match bincode::decode_from_slice(&bytes[..], crate::CONFIG) { + Ok((decoded, _)) => Some(decoded), + Err(e) => { + error!("Decoding error: {}", e); + None + } } + + // if let Ok((decoded, _)) = bincode::decode_from_slice(&bytes[..], crate::CONFIG) { + // Some(decoded) + // } else { + // None + // } } } diff --git a/syscall_lib/src/types/str_ref.rs b/syscall_lib/src/types/str_ref.rs index f1e075d..810790a 100644 --- a/syscall_lib/src/types/str_ref.rs +++ b/syscall_lib/src/types/str_ref.rs @@ -1,7 +1,12 @@ -use std::ffi::CStr; 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}; @@ -15,12 +20,11 @@ fn trunc(str: &str) -> String { out_str } -#[derive(Encode, Decode)] -pub struct StrRef(pub libc::c_ulong); +pub struct StrRef(pub *const libc::c_char); impl StrRef { pub fn new(ptr: libc::c_ulong) -> Self { - Self(ptr) + Self(ptr as *const libc::c_char) } pub fn read(&self) -> String { let ptr = self.0 as *const libc::c_char; @@ -43,3 +47,56 @@ impl AsPtr for StrRef { self.0 as libc::c_ulong } } + +impl<'de> Encode for StrRef { + fn encode( + &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 for StrRef { + fn decode>( + decoder: &mut D, + ) -> Result { + 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>( + decoder: &mut D, + ) -> Result { + 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)) + } +} diff --git a/test.c b/test.c new file mode 100644 index 0000000..bbe0bc6 --- /dev/null +++ b/test.c @@ -0,0 +1,8 @@ +#include + +const char line[] = "12345"; + +int main() { + printf("My pointer is: %p\n", *line); + return 0; +}