1use std::{fs, io::IoSliceMut, path::PathBuf};
2
3use nix::{
4 errno::Errno,
5 sys::uio::{process_vm_readv, RemoteIoVec},
6 unistd::Pid,
7};
8
9use crate::{
10 error::ProcessError,
11 process::{MemoryRegion, Process, ProcessTraits},
12};
13
14use super::signature::{find_signature, Signature};
15
16impl ProcessTraits for Process {
17 fn initialize(
18 proc_name: &str,
19 exclude: &[&str],
20 ) -> Result<Process, super::error::ProcessError> {
21 let process = Process::find_process(proc_name, exclude)?;
22 process.read_regions()
23 }
24
25 fn find_process(
26 proc_name: &str,
27 exclude: &[&str],
28 ) -> Result<Process, ProcessError> {
29 let paths = fs::read_dir("/proc")?;
30
31 'path_loop: for path in paths {
32 let p = path?.path();
33
34 if !p.is_dir() {
35 continue;
36 }
37
38 let cmd_line = p.join("cmdline");
39
40 if !cmd_line.exists() {
41 continue;
42 }
43
44 let mut cmd_buff = fs::read_to_string(cmd_line)?;
45
46 let line = cmd_buff.split(' ').next().unwrap();
47
48 if line.contains(proc_name) {
49 for exclude_word in exclude {
50 if line.contains(exclude_word) {
51 continue 'path_loop;
52 }
53 }
54
55 let stat = p.join("stat");
56 let buff = fs::read_to_string(stat)?;
57
58 cmd_buff.retain(|c| c != '\0');
60 cmd_buff = cmd_buff.replace('\\', "/");
61
62 cmd_buff.remove(0);
63 cmd_buff.remove(0);
64
65 let executable_path = PathBuf::from(cmd_buff);
66 let executable_dir =
67 executable_path.parent().map(|v| v.to_path_buf());
68
69 let pid_str = buff.split(' ').next().unwrap();
70
71 let pid = pid_str.parse()?;
72
73 return Ok(Self {
74 pid,
75 maps: Vec::new(),
76 executable_dir,
77 });
78 }
79 }
80
81 Err(ProcessError::ProcessNotFound)
82 }
83
84 fn read_regions(mut self) -> Result<Process, ProcessError> {
85 let path = format!("/proc/{}/maps", &self.pid);
86 let mut v = Vec::new();
87
88 let buff = fs::read_to_string(path)?;
89
90 for line in buff.split('\n') {
91 if line.is_empty() {
92 break;
93 }
94
95 let mut split = line.split_whitespace();
96 let range_raw = split.next().unwrap();
97 let mut range_split = range_raw.split('-');
98
99 let from_str = range_split.next().unwrap();
100 let to_str = range_split.next().unwrap();
101
102 let from = usize::from_str_radix(from_str, 16)?;
103
104 let to = usize::from_str_radix(to_str, 16)?;
105
106 v.push(MemoryRegion {
107 from,
108 size: to - from,
109 });
110 }
111
112 self.maps = v;
113 Ok(self)
114 }
115
116 fn read_signature(&self, sign: &Signature) -> Result<i32, ProcessError> {
117 let mut buff = Vec::new();
118
119 for region in &self.maps {
120 let remote = RemoteIoVec {
121 base: region.from,
122 len: region.size,
123 };
124
125 buff.resize(region.size, 0);
126
127 let slice = IoSliceMut::new(buff.as_mut_slice());
128
129 let res = process_vm_readv(
130 Pid::from_raw(self.pid),
131 &mut [slice],
132 &[remote],
133 );
134
135 if let Err(e) = res {
136 match e {
137 Errno::EPERM | Errno::ESRCH => return Err(e.into()),
138 _ => continue,
139 }
140 }
141
142 if let Some(offset) = find_signature(buff.as_slice(), sign) {
143 return Ok((remote.base + offset) as i32);
144 }
145 }
146
147 Err(ProcessError::SignatureNotFound(sign.to_string()))
148 }
149
150 fn read(
151 &self,
152 addr: i32,
153 len: usize,
154 buff: &mut [u8],
155 ) -> Result<(), ProcessError> {
156 let remote = RemoteIoVec {
157 base: addr as usize,
158 len,
159 };
160
161 let slice = IoSliceMut::new(buff);
162
163 let res =
164 process_vm_readv(Pid::from_raw(self.pid), &mut [slice], &[remote]);
165
166 match res {
167 Ok(_) => (),
168 Err(e) => match e {
169 nix::errno::Errno::EFAULT => {
170 return Err(ProcessError::BadAddress(addr as usize, len))
171 }
172 _ => return Err(e.into()),
173 },
174 }
175
176 Ok(())
177 }
178}