1use crate::syscall_info::{SyscallArg, SyscallArgs};
2use byteorder::{LittleEndian, WriteBytesExt};
3use libc::{c_long, c_ulonglong, user_regs_struct};
4use nix::sys::ptrace;
5use nix::sys::ptrace::Options;
6use nix::unistd::Pid;
7use std::ffi::c_void;
8use syscalls::Sysno;
9
10#[cfg(any(target_arch = "aarch64", feature = "aarch64"))]
11pub mod aarch64;
12#[cfg(any(target_arch = "riscv64", feature = "riscv64"))]
23pub mod riscv64;
24#[cfg(any(target_arch = "x86_64", feature = "x86_64"))]
33pub mod x86_64;
34
35#[cfg(target_arch = "aarch64")]
36pub use aarch64::*;
37#[cfg(target_arch = "riscv64")]
48pub use riscv64::*;
49#[cfg(target_arch = "x86_64")]
58pub use x86_64::*;
59
60#[derive(Debug, Copy, Clone)]
61pub enum SyscallArgType {
62 Int,
64 Str,
66 Addr,
68}
69
70pub fn read_string(pid: Pid, address: c_ulonglong) -> String {
71 let mut string = String::new();
72 let mut count = 0;
74 let word_size = 8;
75
76 'done: loop {
77 let address = unsafe { (address as *mut c_void).offset(count) };
78
79 let res: c_long = match ptrace::read(pid, address) {
80 Ok(c_long) => c_long,
81 Err(_) => break 'done,
82 };
83
84 let mut bytes: Vec<u8> = vec![];
85 bytes.write_i64::<LittleEndian>(res).unwrap_or_else(|err| {
86 panic!("Failed to write {res} as i64 LittleEndian: {err}");
87 });
88 for b in bytes {
89 if b == 0 {
90 break 'done;
91 }
92 string.push(b as char);
93 }
94
95 count += word_size;
96 }
97
98 string
99}
100
101pub fn ptrace_init_options(pid: Pid) -> nix::Result<()> {
102 ptrace::setoptions(
103 pid,
104 Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT | Options::PTRACE_O_TRACEEXEC,
105 )
106}
107
108pub fn ptrace_init_options_fork(pid: Pid) -> nix::Result<()> {
109 ptrace::setoptions(
110 pid,
111 Options::PTRACE_O_TRACESYSGOOD
112 | Options::PTRACE_O_TRACEEXIT
113 | Options::PTRACE_O_TRACEEXEC
114 | Options::PTRACE_O_TRACEFORK
115 | Options::PTRACE_O_TRACEVFORK
116 | Options::PTRACE_O_TRACECLONE,
117 )
118}
119
120#[allow(clippy::cast_sign_loss)]
121#[must_use]
122pub fn parse_args(pid: Pid, syscall: Sysno, registers: user_regs_struct) -> SyscallArgs {
124 SYSCALLS
125 .get(syscall.id() as usize)
126 .and_then(|option| option.as_ref())
127 .map_or_else(
128 || SyscallArgs(vec![]),
129 |(_, args)| {
130 SyscallArgs(
131 args.iter()
132 .filter_map(Option::as_ref)
133 .enumerate()
134 .map(|(idx, arg_type)| map_arg(pid, registers, idx, *arg_type))
135 .collect(),
136 )
137 },
138 )
139}
140
141fn map_arg(pid: Pid, registers: user_regs_struct, idx: usize, arg: SyscallArgType) -> SyscallArg {
142 let value = get_arg_value(registers, idx);
143 match arg {
144 SyscallArgType::Int => SyscallArg::Int(value as i64),
145 SyscallArgType::Str => SyscallArg::Str(read_string(pid, value)),
146 SyscallArgType::Addr => SyscallArg::Addr(value as usize),
147 }
148}
149
150pub fn escape_to_string(buf: &Vec<u8>) -> String {
151 let mut string = String::new();
152 for c in buf {
153 let code = *c;
154 if (0x20..=0x7f).contains(&code) {
155 if code == b'\\' {
156 string.push_str("\\\\");
157 } else {
158 string.push(char::from(code));
159 }
160 } else {
161 string.push_str(format!("\\{c:x}").as_str());
162 }
163 }
164 string
165}