pox/rc/
syscalls.rs

1use nix::{libc::user_regs_struct, Result, sys::{ptrace, wait::waitpid}, unistd::Pid};
2use tracing::debug;
3
4use crate::rc::{injector::RemoteOperation, senders::RemoteString};
5
6pub trait RemoteSyscall {
7	fn registers(&self, regs: &mut user_regs_struct);
8
9	fn prepare_registers(regs: &mut user_regs_struct, rax: u64, rdi: u64, rsi: u64, rdx: u64, r10: u64, r8: u64, r9: u64) {
10		regs.rax = rax;
11		regs.rdi = rdi;
12		regs.rsi = rsi;
13		regs.rdx = rdx;
14		regs.r10 = r10;
15		regs.r8  = r8;
16		regs.r9  = r9;
17	}
18}
19
20impl<T> RemoteOperation for T where T: RemoteSyscall {
21	fn inject(&mut self, pid: Pid, syscall: usize) -> Result<u64> {
22		let mut regs = ptrace::getregs(pid)?;
23		regs.rip = syscall as u64;
24		self.registers(&mut regs);
25		let syscall_nr = regs.rax;
26		ptrace::setregs(pid, regs)?;
27		ptrace::step(pid, None)?;
28		waitpid(pid, None)?;
29		regs = ptrace::getregs(pid)?;
30		debug!(target: "remote-syscall", "executed syscall #{} -> {}", syscall_nr, regs.rax);
31		Ok(regs.rax)
32	}
33
34	fn revert(&mut self, _pid: Pid, _syscall: usize) -> Result<u64> { Ok(0) }
35}
36
37pub struct RemoteMMap {
38	addr: u64,
39	len: usize,
40	prot: i32,
41	flags: i32,
42	fd: i64,
43	off: u64,
44}
45
46
47
48impl RemoteMMap {
49	pub fn args(addr: u64, len: usize, prot: i32, flags: i32, fd: i64, off: u64) -> Self {
50		RemoteMMap { addr, len, prot, flags, fd, off }
51	}
52}
53
54impl RemoteSyscall for RemoteMMap {
55	fn registers(&self, regs: &mut user_regs_struct) {
56		Self::prepare_registers(regs, 9, self.addr, self.len as u64, self.prot as u64, self.flags as u64, self.fd as u64, self.off);
57	}
58}
59
60pub struct RemoteMUnmap {
61	addr: usize,
62	len: usize,
63}
64
65impl RemoteMUnmap {
66	pub fn args(addr: usize, len: usize) -> Self {
67		RemoteMUnmap { addr, len }
68	}
69}
70
71impl RemoteSyscall for RemoteMUnmap {
72	fn registers(&self, regs: &mut user_regs_struct) {
73		regs.rax = 11;
74		regs.rdi = self.addr as u64;
75		regs.rsi = self.len as u64;
76	}
77}
78
79pub struct RemoteOpen {
80	filename: RemoteString,
81	flags: u64,
82	mode: u64,
83}
84
85#[allow(unused)]
86impl RemoteOpen {
87	pub fn args(filename: RemoteString, flags: u64, mode: u64) -> Self {
88		RemoteOpen { filename, flags, mode }
89	}
90}
91
92impl RemoteSyscall for RemoteOpen {
93	fn registers(&self, regs: &mut user_regs_struct) { // TODO handle this unwrap
94		regs.rax = 2;
95		regs.rdi = self.filename.ptr.unwrap() as u64;
96		regs.rsi = self.flags;
97		regs.rdx = self.mode;
98	}
99}
100
101pub struct RemoteWrite {
102	fd: i64,
103	ptr: u64,
104	len: u64,
105}
106
107#[allow(unused)]
108impl RemoteWrite {
109	pub fn args(fd: i64, ptr: u64, len: u64) -> Self {
110		RemoteWrite { fd, ptr, len }
111	}
112
113	pub fn string(fd: i64, txt: RemoteString) -> Self {
114		RemoteWrite { fd, ptr: txt.ptr.expect("remote write with uninjected remote str") as u64, len: txt.txt.len() as u64 }
115	}
116}
117
118impl RemoteSyscall for RemoteWrite {
119	fn registers(&self, regs: &mut user_regs_struct) {
120		regs.rax = 1;
121		regs.rdi = self.fd as u64;
122		regs.rsi = self.ptr;
123		regs.rdx = self.len;
124	}
125}
126
127pub struct RemoteExit {
128	code: i64,
129}
130
131#[allow(unused)]
132impl RemoteExit {
133	pub fn args(code: i64) -> Self {
134		RemoteExit { code }
135	}
136
137	/// since the exit syscall will never return, normal inject() will always return an error.
138	/// calling this will just return success once the syscall has been invoked
139	pub fn exit(&mut self, pid: Pid, syscall: usize) -> Result<u64> {
140		let mut regs = ptrace::getregs(pid)?;
141		regs.rip = syscall as u64;
142		self.registers(&mut regs);
143		ptrace::setregs(pid, regs)?;
144		ptrace::step(pid, None)?;
145		Ok(self.code as u64)
146	}
147}
148
149impl RemoteSyscall for RemoteExit {
150	fn registers(&self, regs: &mut user_regs_struct) {
151		regs.rax = 60;
152		regs.rdi = self.code as u64;
153	}
154}