1use memflow::cglue;
2use memflow::os::process::*;
3use memflow::prelude::v1::*;
4
5use super::ProcessVirtualMemory;
6
7use libc::pid_t;
8
9use procfs::process::{MMPermissions, MMapExtension, MMapPath};
10
11use itertools::Itertools;
12
13pub struct LinuxProcess {
14 virt_mem: ProcessVirtualMemory,
15 proc: procfs::process::Process,
16 info: ProcessInfo,
17 cached_maps: Vec<procfs::process::MemoryMap>,
18 cached_module_maps: Vec<procfs::process::MemoryMap>,
19}
20
21impl Clone for LinuxProcess {
22 fn clone(&self) -> Self {
23 Self {
24 virt_mem: self.virt_mem.clone(),
25 proc: procfs::process::Process::new(self.proc.pid()).unwrap(),
26 info: self.info.clone(),
27 cached_maps: self.cached_maps.clone(),
28 cached_module_maps: self.cached_module_maps.clone(),
29 }
30 }
31}
32
33impl LinuxProcess {
34 pub fn try_new(info: ProcessInfo) -> Result<Self> {
35 Ok(Self {
36 virt_mem: ProcessVirtualMemory::new(&info),
37 proc: procfs::process::Process::new(info.pid as pid_t)
38 .map_err(|_| Error(ErrorOrigin::OsLayer, ErrorKind::UnableToReadDir))?,
39 info,
40 cached_maps: vec![],
41 cached_module_maps: vec![],
42 })
43 }
44
45 pub fn mmap_path_to_name_string(path: &MMapPath) -> ReprCString {
46 match path {
47 MMapPath::Path(buf) => buf
48 .file_name()
49 .and_then(|o| o.to_str())
50 .unwrap_or("unknown")
51 .into(),
52 MMapPath::Heap => "[heap]".into(),
53 MMapPath::Stack => "[stack]".into(),
54 MMapPath::TStack(_) => "[tstack]".into(),
55 MMapPath::Vdso => "[vdso]".into(),
56 MMapPath::Vvar => "[vvar]".into(),
57 MMapPath::Vsyscall => "[vsyscall]".into(),
58 MMapPath::Rollup => "[rollup]".into(),
59 MMapPath::Anonymous => "[anonymous]".into(),
60 MMapPath::Vsys(_) => "[vsys]".into(),
61 MMapPath::Other(s) => s.as_str().into(),
62 }
63 }
64
65 pub fn mmap_path_to_path_string(path: &MMapPath) -> ReprCString {
66 match path {
67 MMapPath::Path(buf) => buf.to_str().unwrap_or("unknown").into(),
68 MMapPath::Heap => "[heap]".into(),
69 MMapPath::Stack => "[stack]".into(),
70 MMapPath::TStack(_) => "[tstack]".into(),
71 MMapPath::Vdso => "[vdso]".into(),
72 MMapPath::Vvar => "[vvar]".into(),
73 MMapPath::Vsyscall => "[vsyscall]".into(),
74 MMapPath::Rollup => "[rollup]".into(),
75 MMapPath::Anonymous => "[anonymous]".into(),
76 MMapPath::Vsys(_) => "[vsys]".into(),
77 MMapPath::Other(s) => s.as_str().into(),
78 }
79 }
80}
81
82cglue_impl_group!(LinuxProcess, ProcessInstance, {});
83cglue_impl_group!(LinuxProcess, IntoProcessInstance, {});
84
85impl Process for LinuxProcess {
86 fn module_address_list_callback(
94 &mut self,
95 target_arch: Option<&ArchitectureIdent>,
96 mut callback: ModuleAddressCallback,
97 ) -> Result<()> {
98 self.cached_maps = self
99 .proc
100 .maps()
101 .map_err(|_| Error(ErrorOrigin::OsLayer, ErrorKind::UnableToReadDir))?
102 .memory_maps;
103
104 self.cached_module_maps = self
105 .cached_maps
106 .iter()
107 .filter(|map| matches!(map.pathname, MMapPath::Path(_)))
108 .cloned()
109 .coalesce(|m1, m2| {
110 if m1.address.1 == m2.address.0
111 && m1.dev == m2.dev
114 && m1.inode == m2.inode
115 {
116 Ok(procfs::process::MemoryMap {
117 address: (m1.address.0, m2.address.1),
118 perms: MMPermissions::NONE,
119 offset: m1.offset,
120 dev: m1.dev,
121 inode: m1.inode,
122 pathname: m1.pathname,
123 extension: MMapExtension::default(),
124 })
125 } else {
126 Err((m1, m2))
127 }
128 })
129 .collect();
130
131 self.cached_module_maps
132 .iter()
133 .enumerate()
134 .filter(|_| target_arch.is_none() || Some(&self.info().sys_arch) == target_arch)
135 .take_while(|(i, _)| {
136 callback.call(ModuleAddressInfo {
137 address: Address::from(*i as u64),
138 arch: self.info.proc_arch,
139 })
140 })
141 .for_each(|_| {});
142
143 Ok(())
144 }
145
146 fn module_by_address(
152 &mut self,
153 address: Address,
154 architecture: ArchitectureIdent,
155 ) -> Result<ModuleInfo> {
156 if architecture != self.info.sys_arch {
157 return Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotFound));
158 }
159
160 self.cached_module_maps
163 .get(address.to_umem() as usize)
164 .map(|map| ModuleInfo {
165 address,
166 parent_process: self.info.address,
167 base: Address::from(map.address.0),
168 size: (map.address.1 - map.address.0) as umem,
169 name: Self::mmap_path_to_name_string(&map.pathname),
170 path: Self::mmap_path_to_path_string(&map.pathname),
171 arch: self.info.sys_arch,
172 })
173 .ok_or(Error(ErrorOrigin::OsLayer, ErrorKind::NotFound))
174 }
175
176 fn module_import_list_callback(
177 &mut self,
178 info: &ModuleInfo,
179 callback: ImportCallback,
180 ) -> Result<()> {
181 memflow::os::util::module_import_list_callback(&mut self.virt_mem, info, callback)
182 }
183
184 fn module_export_list_callback(
185 &mut self,
186 info: &ModuleInfo,
187 callback: ExportCallback,
188 ) -> Result<()> {
189 memflow::os::util::module_export_list_callback(&mut self.virt_mem, info, callback)
190 }
191
192 fn module_section_list_callback(
193 &mut self,
194 info: &ModuleInfo,
195 callback: SectionCallback,
196 ) -> Result<()> {
197 memflow::os::util::module_section_list_callback(&mut self.virt_mem, info, callback)
198 }
199
200 fn primary_module_address(&mut self) -> Result<Address> {
204 Ok(Address::from(0))
206 }
207
208 fn info(&self) -> &ProcessInfo {
210 &self.info
211 }
212
213 fn state(&mut self) -> ProcessState {
215 ProcessState::Unknown
216 }
217
218 fn set_dtb(&mut self, _dtb1: Address, _dtb2: Address) -> Result<()> {
221 Ok(())
222 }
223
224 fn mapped_mem_range(
225 &mut self,
226 gap_size: imem,
227 start: Address,
228 end: Address,
229 out: MemoryRangeCallback,
230 ) {
231 if let Ok(maps) = self
232 .proc
233 .maps()
234 .map_err(|_| Error(ErrorOrigin::OsLayer, ErrorKind::UnableToReadDir))
235 {
236 self.cached_maps = maps.memory_maps;
237
238 self.cached_maps
239 .iter()
240 .filter(|map| {
241 Address::from(map.address.1) > start && Address::from(map.address.0) < end
242 })
243 .filter(|m| m.perms.contains(MMPermissions::READ))
244 .map(|map| {
245 (
246 Address::from(map.address.0),
247 (map.address.1 - map.address.0) as umem,
248 PageType::empty()
249 .noexec(!map.perms.contains(MMPermissions::EXECUTE))
250 .write(map.perms.contains(MMPermissions::WRITE)),
251 )
252 })
253 .map(|(s, sz, perms)| {
254 if s < start {
255 let diff = start - s;
256 (start, sz - diff as umem, perms)
257 } else {
258 (s, sz, perms)
259 }
260 })
261 .map(|(s, sz, perms)| {
262 if s + sz > end {
263 let diff = s - end;
264 (s, sz - diff as umem, perms)
265 } else {
266 (s, sz, perms)
267 }
268 })
269 .coalesce(|a, b| {
270 if gap_size >= 0 && a.0 + a.1 + gap_size as umem >= b.0 && a.2 == b.2 {
271 Ok((a.0, (b.0 - a.0) as umem + b.1, a.2))
272 } else {
273 Err((a, b))
274 }
275 })
276 .map(<_>::into)
277 .feed_into(out);
278 }
279 }
280}
281
282impl MemoryView for LinuxProcess {
283 fn read_raw_iter(&mut self, data: ReadRawMemOps) -> Result<()> {
284 self.virt_mem.read_raw_iter(data)
285 }
286
287 fn write_raw_iter(&mut self, data: WriteRawMemOps) -> Result<()> {
288 self.virt_mem.write_raw_iter(data)
289 }
290
291 fn metadata(&self) -> MemoryViewMetadata {
292 self.virt_mem.metadata()
293 }
294}