use crate::syscall_info::{SyscallArg, SyscallArgs};
use byteorder::{LittleEndian, WriteBytesExt};
use libc::{c_long, c_ulonglong, user_regs_struct};
use nix::sys::ptrace;
use nix::sys::ptrace::Options;
use nix::unistd::Pid;
use std::ffi::c_void;
use syscalls::Sysno;
#[cfg(any(target_arch = "aarch64", feature = "aarch64"))]
pub mod aarch64;
#[cfg(any(target_arch = "riscv64", feature = "riscv64"))]
pub mod riscv64;
#[cfg(any(target_arch = "x86_64", feature = "x86_64"))]
pub mod x86_64;
#[cfg(target_arch = "aarch64")]
pub use aarch64::*;
#[cfg(target_arch = "riscv64")]
pub use riscv64::*;
#[cfg(target_arch = "x86_64")]
pub use x86_64::*;
#[derive(Debug, Copy, Clone)]
pub enum SyscallArgType {
Int,
Str,
StrArray,
Addr,
}
pub fn read_string(pid: Pid, address: c_ulonglong) -> String {
let mut string = String::new();
let mut count = 0;
let word_size = 8;
'done: loop {
let address = unsafe { (address as *mut c_void).offset(count) };
let res: c_long = match ptrace::read(pid, address) {
Ok(c_long) => c_long,
Err(_) => {
return format!("{:#x}", address as usize);
}
};
let mut bytes: Vec<u8> = vec![];
bytes.write_i64::<LittleEndian>(res).unwrap_or_else(|err| {
panic!("Failed to write {res} as i64 LittleEndian: {err}");
});
for b in bytes {
if b == 0 {
break 'done;
}
string.push(b as char);
}
count += word_size;
}
string
}
pub fn ptrace_init_options(pid: Pid) -> nix::Result<()> {
ptrace::setoptions(
pid,
Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT | Options::PTRACE_O_TRACEEXEC,
)
}
pub fn ptrace_init_options_fork(pid: Pid) -> nix::Result<()> {
ptrace::setoptions(
pid,
Options::PTRACE_O_TRACESYSGOOD
| Options::PTRACE_O_TRACEEXIT
| Options::PTRACE_O_TRACEEXEC
| Options::PTRACE_O_TRACEFORK
| Options::PTRACE_O_TRACEVFORK
| Options::PTRACE_O_TRACECLONE,
)
}
#[allow(clippy::cast_sign_loss)]
#[must_use]
pub fn parse_args(pid: Pid, syscall: Sysno, registers: user_regs_struct) -> SyscallArgs {
SYSCALLS
.get(syscall.id() as usize)
.and_then(|option| option.as_ref())
.map_or_else(
|| SyscallArgs(vec![]),
|(_, args)| {
SyscallArgs(
args.iter()
.filter_map(Option::as_ref)
.enumerate()
.map(|(idx, arg_type)| map_arg(pid, registers, idx, *arg_type))
.collect(),
)
},
)
}
fn map_arg(pid: Pid, registers: user_regs_struct, idx: usize, arg: SyscallArgType) -> SyscallArg {
let value = get_arg_value(registers, idx);
match arg {
SyscallArgType::Int => SyscallArg::Int(value as i64),
SyscallArgType::Str => SyscallArg::Str(read_string(pid, value)),
SyscallArgType::StrArray => {
SyscallArg::StrVec(read_string_array(pid, value), Some(value as usize))
}
SyscallArgType::Addr => SyscallArg::Addr(value as usize),
}
}
pub fn read_string_array(pid: Pid, address: c_ulonglong) -> Vec<String> {
let mut vec = Vec::new();
if address == 0 {
return vec;
}
let mut offset = 0isize;
for _ in 0..1024 {
let ptr_addr = unsafe { (address as *mut c_void).offset(offset) };
let res: c_long = match ptrace::read(pid, ptr_addr) {
Ok(v) => v,
Err(_) => break,
};
let mut bytes: Vec<u8> = vec![];
bytes.write_i64::<LittleEndian>(res).ok();
let mut ptr_value: c_ulonglong = 0;
for (i, b) in bytes.iter().enumerate() {
ptr_value |= c_ulonglong::from(*b) << (i * 8);
}
if ptr_value == 0 {
break;
}
vec.push(read_string(pid, ptr_value));
offset += std::mem::size_of::<c_ulonglong>() as isize;
}
vec
}
pub fn read_string_array_count(pid: Pid, address: c_ulonglong) -> usize {
if address == 0 {
return 0;
}
let mut count = 0usize;
let mut offset = 0isize;
for _ in 0..1024 {
let ptr_addr = unsafe { (address as *mut c_void).offset(offset) };
let res: c_long = match ptrace::read(pid, ptr_addr) {
Ok(v) => v,
Err(_) => break,
};
let mut bytes: Vec<u8> = vec![];
bytes.write_i64::<LittleEndian>(res).ok();
let mut ptr_value: c_ulonglong = 0;
for (i, b) in bytes.iter().enumerate() {
ptr_value |= c_ulonglong::from(*b) << (i * 8);
}
if ptr_value == 0 {
break;
}
count += 1;
offset += std::mem::size_of::<c_ulonglong>() as isize;
}
count
}
pub fn escape_to_string(buf: &Vec<u8>) -> String {
let mut string = String::new();
for c in buf {
let code = *c;
if (0x20..=0x7f).contains(&code) {
if code == b'\\' {
string.push_str("\\\\");
} else {
string.push(char::from(code));
}
} else {
string.push_str(format!("\\{c:x}").as_str());
}
}
string
}