use memflow::os::process::*;
use memflow::prelude::v1::*;
use libc::pid_t;
use log::error;
use procfs::KernelModule;
use itertools::Itertools;
pub mod mem;
use mem::ProcessVirtualMemory;
pub mod process;
use process::LinuxProcess;
pub struct LinuxOs {
info: OsInfo,
cached_modules: Vec<KernelModule>,
}
impl LinuxOs {
pub fn new(_: &OsArgs) -> Result<Self> {
Ok(Default::default())
}
}
impl Clone for LinuxOs {
fn clone(&self) -> Self {
Self {
info: self.info.clone(),
cached_modules: vec![],
}
}
}
impl Default for LinuxOs {
fn default() -> Self {
let info = OsInfo {
base: Address::NULL,
size: 0,
arch: ArchitectureIdent::X86(64, false),
};
Self {
info,
cached_modules: vec![],
}
}
}
impl Os for LinuxOs {
type ProcessType<'a> = LinuxProcess;
type IntoProcessType = LinuxProcess;
fn process_address_list_callback(&mut self, mut callback: AddressCallback) -> Result<()> {
procfs::process::all_processes()
.map_err(|e| {
error!("{e}");
Error(ErrorOrigin::OsLayer, ErrorKind::UnableToReadDir)
})?
.filter_map(|p| p.map(|p| p.pid() as usize).ok())
.map(Address::from)
.take_while(|a| callback.call(*a))
.for_each(|_| {});
Ok(())
}
fn process_info_by_address(&mut self, address: Address) -> Result<ProcessInfo> {
self.process_info_by_pid(address.to_umem() as _)
}
fn process_info_by_pid(&mut self, pid: Pid) -> Result<ProcessInfo> {
let proc = procfs::process::Process::new(pid as pid_t)
.map_err(|_| Error(ErrorOrigin::OsLayer, ErrorKind::UnableToReadDir))?;
let command_line = proc
.cmdline()
.ok()
.map(|v| v.join(" ").split('\0').collect_vec().join(" "))
.unwrap_or_else(String::new)
.into();
let path = proc
.cmdline()
.ok()
.and_then(|l| {
l.get(0)
.map(|s| s.split('\0').next().unwrap_or("").to_string())
})
.unwrap_or_else(|| {
proc.status()
.ok()
.map(|s| s.name)
.unwrap_or_else(|| "unknown".to_string())
});
let name = path.split(&['/', '\\'][..]).last().unwrap().into();
let path = path.into();
Ok(ProcessInfo {
address: (proc.pid() as umem).into(),
pid,
command_line,
path,
name,
sys_arch: ArchitectureIdent::X86(64, false),
proc_arch: ArchitectureIdent::X86(64, false),
state: ProcessState::Alive,
dtb1: Address::invalid(),
dtb2: Address::invalid(),
})
}
fn process_by_info(&mut self, info: ProcessInfo) -> Result<Self::ProcessType<'_>> {
LinuxProcess::try_new(info)
}
fn into_process_by_info(mut self, info: ProcessInfo) -> Result<Self::IntoProcessType> {
self.process_by_info(info)
}
fn module_address_list_callback(&mut self, mut callback: AddressCallback) -> Result<()> {
self.cached_modules = procfs::modules()
.map_err(|_| Error(ErrorOrigin::OsLayer, ErrorKind::UnableToReadDir))?
.into_values()
.collect();
(0..self.cached_modules.len())
.map(Address::from)
.take_while(|a| callback.call(*a))
.for_each(|_| {});
Ok(())
}
fn module_by_address(&mut self, address: Address) -> Result<ModuleInfo> {
self.cached_modules
.get(address.to_umem() as usize)
.map(|km| ModuleInfo {
address,
size: km.size as umem,
base: Address::NULL,
name: km
.name
.split('/')
.last()
.or(Some(""))
.map(ReprCString::from)
.unwrap(),
arch: self.info.arch,
path: km.name.clone().into(),
parent_process: Address::INVALID,
})
.ok_or(Error(ErrorOrigin::OsLayer, ErrorKind::NotFound))
}
fn primary_module_address(&mut self) -> Result<Address> {
Ok(Address::from(0))
}
fn module_import_list_callback(
&mut self,
_info: &ModuleInfo,
_callback: ImportCallback,
) -> Result<()> {
Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotImplemented))
}
fn module_export_list_callback(
&mut self,
_info: &ModuleInfo,
_callback: ExportCallback,
) -> Result<()> {
Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotImplemented))
}
fn module_section_list_callback(
&mut self,
_info: &ModuleInfo,
_callback: SectionCallback,
) -> Result<()> {
Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotImplemented))
}
fn info(&self) -> &OsInfo {
&self.info
}
}