memflow_native/linux/
mod.rs

1use memflow::os::process::*;
2use memflow::prelude::v1::*;
3
4use libc::pid_t;
5use log::error;
6
7use procfs::KernelModule;
8
9use itertools::Itertools;
10
11pub mod mem;
12use mem::ProcessVirtualMemory;
13
14pub mod process;
15pub use process::LinuxProcess;
16
17pub struct LinuxOs {
18    info: OsInfo,
19    cached_modules: Vec<KernelModule>,
20}
21
22impl LinuxOs {
23    pub fn new(_: &OsArgs) -> Result<Self> {
24        Ok(Default::default())
25    }
26}
27
28impl Clone for LinuxOs {
29    fn clone(&self) -> Self {
30        Self {
31            info: self.info.clone(),
32            cached_modules: vec![],
33        }
34    }
35}
36
37impl Default for LinuxOs {
38    fn default() -> Self {
39        let info = OsInfo {
40            base: Address::NULL,
41            size: 0,
42            arch: ArchitectureIdent::X86(64, false),
43        };
44
45        Self {
46            info,
47            cached_modules: vec![],
48        }
49    }
50}
51
52impl Os for LinuxOs {
53    type ProcessType<'a> = LinuxProcess;
54    type IntoProcessType = LinuxProcess;
55
56    /// Walks a process list and calls a callback for each process structure address
57    ///
58    /// The callback is fully opaque. We need this style so that C FFI can work seamlessly.
59    fn process_address_list_callback(&mut self, mut callback: AddressCallback) -> Result<()> {
60        procfs::process::all_processes()
61            .map_err(|e| {
62                error!("{e}");
63                Error(ErrorOrigin::OsLayer, ErrorKind::UnableToReadDir)
64            })?
65            .filter_map(|p| p.map(|p| p.pid() as usize).ok())
66            .map(Address::from)
67            .take_while(|a| callback.call(*a))
68            .for_each(|_| {});
69
70        Ok(())
71    }
72
73    /// Find process information by its internal address
74    fn process_info_by_address(&mut self, address: Address) -> Result<ProcessInfo> {
75        self.process_info_by_pid(address.to_umem() as _)
76    }
77
78    fn process_info_by_pid(&mut self, pid: Pid) -> Result<ProcessInfo> {
79        let proc = procfs::process::Process::new(pid as pid_t)
80            .map_err(|_| Error(ErrorOrigin::OsLayer, ErrorKind::UnableToReadDir))?;
81
82        let command_line = proc
83            .cmdline()
84            .ok()
85            .map(|v| v.join(" ").split('\0').collect_vec().join(" "))
86            .unwrap_or_else(String::new)
87            .into();
88
89        let path = proc
90            .cmdline()
91            .ok()
92            .and_then(|l| {
93                l.first()
94                    .map(|s| s.split('\0').next().unwrap_or("").to_string())
95            })
96            .unwrap_or_else(|| {
97                proc.status()
98                    .ok()
99                    .map(|s| s.name)
100                    .unwrap_or_else(|| "unknown".to_string())
101            });
102
103        let name = path.split(&['/', '\\'][..]).last().unwrap().into();
104
105        let path = path.into();
106
107        Ok(ProcessInfo {
108            address: (proc.pid() as umem).into(),
109            pid,
110            command_line,
111            path,
112            name,
113            sys_arch: ArchitectureIdent::X86(64, false),
114            proc_arch: ArchitectureIdent::X86(64, false),
115            state: ProcessState::Alive,
116            // dtb is not known/used here
117            dtb1: Address::invalid(),
118            dtb2: Address::invalid(),
119        })
120    }
121
122    /// Construct a process by its info, borrowing the OS
123    ///
124    /// It will share the underlying memory resources
125    fn process_by_info(&mut self, info: ProcessInfo) -> Result<Self::ProcessType<'_>> {
126        LinuxProcess::try_new(info)
127    }
128
129    /// Construct a process by its info, consuming the OS
130    ///
131    /// This function will consume the Kernel instance and move its resources into the process
132    fn into_process_by_info(mut self, info: ProcessInfo) -> Result<Self::IntoProcessType> {
133        self.process_by_info(info)
134    }
135
136    /// Walks the OS module list and calls the provided callback for each module structure
137    /// address
138    ///
139    /// # Arguments
140    /// * `callback` - where to pass each matching module to. This is an opaque callback.
141    fn module_address_list_callback(&mut self, mut callback: AddressCallback) -> Result<()> {
142        self.cached_modules = procfs::modules()
143            .map_err(|_| Error(ErrorOrigin::OsLayer, ErrorKind::UnableToReadDir))?
144            .into_values()
145            .collect();
146
147        (0..self.cached_modules.len())
148            .map(Address::from)
149            .take_while(|a| callback.call(*a))
150            .for_each(|_| {});
151
152        Ok(())
153    }
154
155    /// Retrieves a module by its structure address
156    ///
157    /// # Arguments
158    /// * `address` - address where module's information resides in
159    fn module_by_address(&mut self, address: Address) -> Result<ModuleInfo> {
160        self.cached_modules
161            .get(address.to_umem() as usize)
162            .map(|km| ModuleInfo {
163                address,
164                size: km.size as umem,
165                base: Address::NULL,
166                name: km
167                    .name
168                    .split('/')
169                    .last()
170                    .or(Some(""))
171                    .map(ReprCString::from)
172                    .unwrap(),
173                arch: self.info.arch,
174                path: km.name.clone().into(),
175                parent_process: Address::INVALID,
176            })
177            .ok_or(Error(ErrorOrigin::OsLayer, ErrorKind::NotFound))
178    }
179
180    /// Retrieves address of the primary module structure of the process
181    ///
182    /// This will generally be for the initial executable that was run
183    fn primary_module_address(&mut self) -> Result<Address> {
184        // TODO: Is it always 0th mod?
185        Ok(Address::from(0))
186    }
187
188    /// Retrieves a list of all imports of a given module
189    fn module_import_list_callback(
190        &mut self,
191        _info: &ModuleInfo,
192        _callback: ImportCallback,
193    ) -> Result<()> {
194        //memflow::os::util::module_import_list_callback(&mut self.virt_mem, info, callback)
195        Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotImplemented))
196    }
197
198    /// Retrieves a list of all exports of a given module
199    fn module_export_list_callback(
200        &mut self,
201        _info: &ModuleInfo,
202        _callback: ExportCallback,
203    ) -> Result<()> {
204        //memflow::os::util::module_export_list_callback(&mut self.virt_mem, info, callback)
205        Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotImplemented))
206    }
207
208    /// Retrieves a list of all sections of a given module
209    fn module_section_list_callback(
210        &mut self,
211        _info: &ModuleInfo,
212        _callback: SectionCallback,
213    ) -> Result<()> {
214        //memflow::os::util::module_section_list_callback(&mut self.virt_mem, info, callback)
215        Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotImplemented))
216    }
217
218    /// Retrieves the OS info
219    fn info(&self) -> &OsInfo {
220        &self.info
221    }
222}