scanmem alternative with concurrent memory scanning
1use anyhow::Result;
2use log::info;
3use std::fs;
4use std::path::Path;
5use std::str::FromStr;
6
7pub struct Process {
8 pub pid: i32,
9 #[allow(dead_code)]
10 pub name: String,
11}
12
13#[derive(Debug, Clone)]
14pub struct MemoryRegion {
15 pub start_addr: usize,
16 pub end_addr: usize,
17 #[allow(dead_code)]
18 pub permissions: String,
19 #[allow(dead_code)]
20 pub offset: usize,
21 #[allow(dead_code)]
22 pub pathname: String,
23}
24
25impl Process {
26 pub fn find_by_name(name: &str) -> Result<Self> {
27 // Search for process using pgrep-like functionality
28 let proc_dir = Path::new("/proc");
29 let mut found_pid = None;
30
31 for entry in fs::read_dir(proc_dir)? {
32 let entry = entry?;
33 let path = entry.path();
34
35 if !path.is_dir() {
36 continue;
37 }
38
39 let file_name = path.file_name().unwrap().to_string_lossy();
40 if !file_name.chars().all(|c| c.is_digit(10)) {
41 continue;
42 }
43
44 let pid = i32::from_str(&file_name)?;
45 let cmdline_path = path.join("cmdline");
46
47 if !cmdline_path.exists() {
48 continue;
49 }
50
51 let cmdline = fs::read_to_string(cmdline_path)?;
52 if cmdline.contains(name) {
53 println!("Process found with PID: {}", pid);
54 found_pid = Some(pid);
55 break;
56 }
57 }
58
59 match found_pid {
60 Some(pid) => Ok(Process {
61 pid,
62 name: name.to_string(),
63 }),
64 None => anyhow::bail!("Process '{}' not found", name),
65 }
66 }
67
68 pub fn get_memory_maps(&self) -> Result<Vec<MemoryRegion>> {
69 let maps_path = format!("/proc/{}/maps", self.pid);
70 info!("maps file located at {} opened.", maps_path);
71
72 let maps_content = fs::read_to_string(&maps_path)?;
73 let mut regions = Vec::new();
74
75 for line in maps_content.lines() {
76 let parts: Vec<&str> = line.split_whitespace().collect();
77 if parts.len() < 5 {
78 continue;
79 }
80
81 let addr_range: Vec<&str> = parts[0].split('-').collect();
82 if addr_range.len() != 2 {
83 continue;
84 }
85
86 let start_addr = usize::from_str_radix(addr_range[0], 16)?;
87 let end_addr = usize::from_str_radix(addr_range[1], 16)?;
88 let permissions = parts[1].to_string();
89
90 // Only add regions with read permission
91 if permissions.contains('r') {
92 regions.push(MemoryRegion {
93 start_addr,
94 end_addr,
95 permissions,
96 offset: usize::from_str_radix(parts[2], 16)?,
97 pathname: if parts.len() > 5 {
98 parts[5..].join(" ")
99 } else {
100 String::new()
101 },
102 });
103 }
104 }
105
106 info!("{} suitable regions found.", regions.len());
107 Ok(regions)
108 }
109}