memflow_native/linux/
mem.rs1use memflow::os::process::*;
2use memflow::prelude::v1::*;
3
4use libc::{iovec, pid_t, sysconf, _SC_IOV_MAX};
5use std::ffi::c_void;
6
7#[derive(Clone, Copy)]
8#[repr(transparent)]
9struct IoSendVec(iovec);
10
11unsafe impl Send for IoSendVec {}
12
13#[derive(Clone)]
14pub struct ProcessVirtualMemory {
15 pid: pid_t,
16 temp_iov: Box<[IoSendVec]>,
17 temp_meta: Box<[Address]>,
18}
19
20impl ProcessVirtualMemory {
21 pub fn new(info: &ProcessInfo) -> Self {
22 let iov_max = unsafe { sysconf(_SC_IOV_MAX) } as usize;
23
24 Self {
25 pid: info.pid as pid_t,
26 temp_iov: vec![
27 IoSendVec(iovec {
28 iov_base: std::ptr::null_mut::<c_void>(),
29 iov_len: 0
30 });
31 iov_max * 2
32 ]
33 .into_boxed_slice(),
34 temp_meta: vec![Address::INVALID; iov_max].into_boxed_slice(),
35 }
36 }
37
38 fn vm_error() -> Option<ErrorKind> {
39 let ret = match unsafe { *libc::__errno_location() } {
40 libc::EFAULT => return None,
41 libc::EINVAL => ErrorKind::ArgValidation,
42 libc::ENOMEM => return None,
43 libc::EPERM => ErrorKind::NotSupported, libc::ESRCH => ErrorKind::ProcessNotFound,
45 _ => ErrorKind::Unknown,
46 };
47
48 Some(ret)
49 }
50}
51
52trait RWSlice: core::ops::Deref<Target = [u8]> {
54 unsafe fn do_rw(
56 pid: pid_t,
57 iov_local: *const iovec,
58 iov_remote: *const iovec,
59 cnt: usize,
60 ) -> isize;
61
62 unsafe fn from_iovec(liov: iovec) -> Self;
64}
65
66impl<'a> RWSlice for CSliceRef<'a, u8> {
67 unsafe fn do_rw(
68 pid: pid_t,
69 iov_local: *const iovec,
70 iov_remote: *const iovec,
71 cnt: usize,
72 ) -> isize {
73 libc::process_vm_writev(pid, iov_local, cnt as _, iov_remote, cnt as _, 0)
74 }
75
76 unsafe fn from_iovec(liov: iovec) -> Self {
77 #[allow(clippy::unnecessary_cast)]
78 core::slice::from_raw_parts(liov.iov_base as *const _, liov.iov_len as usize).into()
79 }
80}
81
82impl<'a> RWSlice for CSliceMut<'a, u8> {
83 unsafe fn do_rw(
84 pid: pid_t,
85 iov_local: *const iovec,
86 iov_remote: *const iovec,
87 cnt: usize,
88 ) -> isize {
89 libc::process_vm_readv(pid, iov_local, cnt as _, iov_remote, cnt as _, 0)
90 }
91
92 unsafe fn from_iovec(liov: iovec) -> Self {
93 #[allow(clippy::unnecessary_cast)]
94 core::slice::from_raw_parts_mut(liov.iov_base as *mut _, liov.iov_len as usize).into()
95 }
96}
97
98impl ProcessVirtualMemory {
99 fn process_rw<T: RWSlice>(
101 &mut self,
102 MemOps {
103 mut inp,
104 mut out,
105 mut out_fail,
106 }: MemOps<CTup3<Address, Address, T>, CTup2<Address, T>>,
107 ) -> Result<()> {
108 let max_iov = self.temp_iov.len() / 2;
109 let (iov_local, iov_remote) = self.temp_iov.split_at_mut(max_iov);
110
111 let mut iov_iter = iov_local
112 .iter_mut()
113 .zip(iov_remote.iter_mut().zip(self.temp_meta.iter_mut()))
114 .enumerate();
115 let mut iov_next = iov_iter.next();
116
117 let mut elem = inp.next();
118
119 'exit: while let Some(CTup3(a, m, b)) = elem {
120 let (cnt, (liov, (riov, meta))) = iov_next.unwrap();
121
122 let iov_len = b.len();
123
124 liov.0 = iovec {
125 iov_base: b.as_ptr() as *mut c_void,
126 iov_len,
127 };
128
129 riov.0 = iovec {
130 iov_base: a.to_umem() as *mut c_void,
131 iov_len,
132 };
133
134 *meta = m;
135
136 iov_next = iov_iter.next();
137 elem = inp.next();
138
139 if elem.is_none() || iov_next.is_none() {
140 let mut offset = 0;
141
142 loop {
144 let cnt = cnt + 1 - offset;
145
146 if cnt == 0 {
147 break;
148 }
149
150 let libcret = unsafe {
151 T::do_rw(
152 self.pid,
153 iov_local.as_ptr().add(offset).cast(),
154 iov_remote.as_ptr().add(offset).cast(),
155 cnt,
156 )
157 };
158
159 let vm_err = if libcret == -1 {
160 Self::vm_error()
161 } else {
162 None
163 };
164
165 match vm_err {
166 Some(err) => return Err(Error(ErrorOrigin::OsLayer, err)),
167 _ => {
168 let mut remaining_written = if libcret == -1 {
169 0
170 } else {
171 libcret as usize + 1
172 };
173
174 for (liof, (_, meta)) in iov_local
175 .iter()
176 .take(cnt)
177 .zip(iov_remote.iter().zip(self.temp_meta.iter()))
178 {
179 offset += 1;
180 let to_write = remaining_written;
181
182 remaining_written =
183 remaining_written.saturating_sub(liof.0.iov_len);
184
185 if to_write > 0 {
186 if !opt_call(
187 out.as_deref_mut(),
188 CTup2(*meta, unsafe { T::from_iovec(liof.0) }),
189 ) {
190 break 'exit;
191 }
192 } else {
193 if !opt_call(
196 out_fail.as_deref_mut(),
197 CTup2(*meta, unsafe { T::from_iovec(liof.0) }),
198 ) {
199 break 'exit;
200 }
201 break;
202 }
203 }
204 }
205 }
206 }
207
208 iov_iter = iov_local
209 .iter_mut()
210 .zip(iov_remote.iter_mut().zip(self.temp_meta.iter_mut()))
211 .enumerate();
212 iov_next = iov_iter.next();
213 }
214 }
215
216 Ok(())
217 }
218}
219
220impl MemoryView for ProcessVirtualMemory {
221 fn read_raw_iter<'a>(&mut self, data: ReadRawMemOps) -> Result<()> {
222 self.process_rw(data)
223 }
224
225 fn write_raw_iter<'a>(&mut self, data: WriteRawMemOps) -> Result<()> {
226 self.process_rw(data)
227 }
228
229 fn metadata(&self) -> MemoryViewMetadata {
230 MemoryViewMetadata {
231 arch_bits: if cfg!(target_pointer_width = "64") {
232 64
233 } else {
234 32
235 },
236 little_endian: cfg!(target_endian = "little"),
237 max_address: Address::invalid(),
238 readonly: false,
239 real_size: 0,
240 }
241 }
242}