memflow_native/linux/
process.rs

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    /// Walks the process' module list and calls the provided callback for each module structure
87    /// address
88    ///
89    /// # Arguments
90    /// * `target_arch` - sets which architecture to retrieve the modules for (if emulated). Choose
91    ///   between `Some(ProcessInfo::sys_arch())`, and `Some(ProcessInfo::proc_arch())`. `None` for all.
92    /// * `callback` - where to pass each matching module to. This is an opaque callback.
93    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                    // When the file gets mapped in memory, offsets change.
112                    // && m2.offset - m1.offset == m1.address.1 - m1.address.0
113                    && 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    /// Retrieves a module by its structure address and architecture
147    ///
148    /// # Arguments
149    /// * `address` - address where module's information resides in
150    /// * `architecture` - architecture of the module. Should be either `ProcessInfo::proc_arch`, or `ProcessInfo::sys_arch`.
151    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        // TODO: create cached_module_maps if its empty
161
162        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    /// Retrieves address of the primary module structure of the process
201    ///
202    /// This will generally be for the initial executable that was run
203    fn primary_module_address(&mut self) -> Result<Address> {
204        // TODO: Is it always 0th mod?
205        Ok(Address::from(0))
206    }
207
208    /// Retrieves the process info
209    fn info(&self) -> &ProcessInfo {
210        &self.info
211    }
212
213    /// Retrieves the state of the process
214    fn state(&mut self) -> ProcessState {
215        ProcessState::Unknown
216    }
217
218    /// Changes the dtb this process uses for memory translations.
219    /// This function serves no purpose in memflow-native.
220    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}