rosu_mem/
linux.rs

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                // Formatting path
59                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}