forensic_rs/utils/
testing.rs

1use crate::{
2    err::ForensicError,
3    traits::registry::{RegHiveKey, RegValue, RegistryKeyInfo, RegistryReader},
4};
5use std::{cell::RefCell, collections::BTreeMap};
6
7use super::time::Filetime;
8
9/// Basic Registry for testing. Includes the user profile "S-1-5-21-1366093794-4292800403-1155380978-513"
10#[derive(Clone, Debug)]
11pub struct TestingRegistry {
12    pub cell: BTreeMap<String, MountedCell>,
13    pub cached: RefCell<BTreeMap<RegHiveKey, String>>,
14    pub counter: RefCell<isize>,
15}
16
17impl Default for TestingRegistry {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23impl TestingRegistry {
24    pub fn empty() -> Self {
25        Self {
26            cell: BTreeMap::new(),
27            cached: RefCell::new(basic_cache()),
28            counter: RefCell::default(),
29        }
30    }
31    pub fn new() -> Self {
32        Self {
33            cell: basic_registry(),
34            cached: RefCell::new(basic_cache()),
35            counter: RefCell::new(0),
36        }
37    }
38    pub fn increase_counter(&self) -> isize {
39        let mut borrowed = self.counter.borrow_mut();
40        let ret = *borrowed;
41        *borrowed += 1;
42        ret
43    }
44    pub fn add_value(&mut self, path: &str, value: &str, data: RegValue) {
45        let (hkey, rest) = match path.split_once(|v| v == '/' || v == '\\') {
46            Some(v) => v,
47            None => {
48                return self
49                    .cell
50                    .entry(path.to_string())
51                    .or_insert(MountedCell::new(path))
52                    .add_value("", value, data)
53            }
54        };
55        self.cell
56            .entry(hkey.to_string())
57            .or_insert(MountedCell::new(hkey))
58            .add_value(rest, value, data);
59    }
60    pub fn contains(&self, path: &str) -> bool {
61        let (hkey, rest) = match path.split_once(|v| v == '/' || v == '\\') {
62            Some(v) => v,
63            None => return self.cell.contains_key(path),
64        };
65        let hive = match self.cell.get(hkey) {
66            Some(v) => v,
67            None => return false,
68        };
69        hive.contains_key(rest)
70    }
71    pub fn get_value(&self, path: &str, value: &str) -> Option<RegValue> {
72        let (hkey, rest) = match path.split_once(|v| v == '/' || v == '\\') {
73            Some(v) => v,
74            None => (path, ""),
75        };
76        let hive = match self.cell.get(hkey) {
77            Some(v) => v,
78            None => return None,
79        };
80        hive.get_value(rest, value)
81    }
82    pub fn get_values(&self, path: &str) -> Option<Vec<String>> {
83        let (hkey, rest) = match path.split_once(|v| v == '/' || v == '\\') {
84            Some(v) => v,
85            None => (path, ""),
86        };
87        let hive = match self.cell.get(hkey) {
88            Some(v) => v,
89            None => return None,
90        };
91        Some(hive.get_values(rest))
92    }
93    pub fn get_keys(&self, path: &str) -> Option<Vec<String>> {
94        let (hkey, rest) = match path.split_once(|v| v == '/' || v == '\\') {
95            Some(v) => v,
96            None => (path, ""),
97        };
98        let hive = match self.cell.get(hkey) {
99            Some(v) => v,
100            None => return None,
101        };
102        Some(hive.get_keys(rest))
103    }
104}
105
106#[derive(Clone, Debug, Default)]
107pub struct MountedCell {
108    pub name: String,
109    pub keys: BTreeMap<String, MountedCell>,
110    pub values: BTreeMap<String, RegValue>,
111}
112impl MountedCell {
113    pub fn new(name: &str) -> Self {
114        Self {
115            name: name.into(),
116            keys: BTreeMap::new(),
117            values: BTreeMap::new(),
118        }
119    }
120    pub fn add_key(&mut self, path: &str) {
121        if path.is_empty() {
122            return;
123        }
124        let (first, rest) = match path.split_once(|v| v == '/' || v == '\\') {
125            Some(v) => v,
126            None => {
127                self.keys
128                    .entry(path.to_string())
129                    .or_insert(MountedCell::new(path))
130                    .add_key(path);
131                return;
132            }
133        };
134        self.keys
135            .entry(first.to_string())
136            .or_insert(MountedCell::new(first))
137            .add_key(rest);
138    }
139    pub fn contains_key(&self, path: &str) -> bool {
140        let (first, rest) = match path.split_once(|v| v == '/' || v == '\\') {
141            Some(v) => v,
142            None => return self.keys.contains_key(path),
143        };
144        let hive = match self.keys.get(first) {
145            Some(v) => v,
146            None => return false,
147        };
148        hive.contains_key(rest)
149    }
150    pub fn add_value(&mut self, path: &str, value: &str, data: RegValue) {
151        if path.is_empty() {
152            self.values.insert(value.into(), data);
153            return;
154        }
155        let (first, rest) = match path.split_once(|v| v == '/' || v == '\\') {
156            Some(v) => v,
157            None => {
158                self.keys
159                    .entry(path.to_string())
160                    .or_insert(MountedCell::new(path))
161                    .add_value("", value, data);
162                return;
163            }
164        };
165        self.keys
166            .entry(first.to_string())
167            .or_insert(MountedCell::new(first))
168            .add_value(rest, value, data);
169    }
170    pub fn get_value(&self, path: &str, value: &str) -> Option<RegValue> {
171        if path.is_empty() {
172            return self.values.get(value).cloned();
173        }
174        let (first, rest) = match path.split_once(|v| v == '/' || v == '\\') {
175            Some(v) => v,
176            None => return self.keys.get(path)?.get_value("", value),
177        };
178        self.keys.get(first)?.get_value(rest, value)
179    }
180    pub fn get_values(&self, path: &str) -> Vec<String> {
181        if path.is_empty() {
182            return self
183                .values
184                .keys()
185                .map(|v| v.to_string())
186                .collect();
187        }
188        let (first, rest) = match path.split_once(|v| v == '/' || v == '\\') {
189            Some(v) => v,
190            None => {
191                return match self.keys.get(path) {
192                    Some(v) => v.get_values(""),
193                    None => Vec::new(),
194                }
195            }
196        };
197        match self.keys.get(first) {
198            Some(v) => v.get_values(rest),
199            None => Vec::new(),
200        }
201    }
202    pub fn get_keys(&self, path: &str) -> Vec<String> {
203        if path.is_empty() {
204            return self
205                .keys
206                .keys()
207                .map(|v| v.to_string())
208                .collect();
209        }
210        let (first, rest) = match path.split_once(|v| v == '/' || v == '\\') {
211            Some(v) => v,
212            None => {
213                return match self.keys.get(path) {
214                    Some(v) => v.get_keys(""),
215                    None => Vec::new(),
216                }
217            }
218        };
219        match self.keys.get(first) {
220            Some(v) => v.get_keys(rest),
221            None => Vec::new(),
222        }
223    }
224}
225
226impl RegistryReader for TestingRegistry {
227    fn from_file(
228        &self,
229        _file: Box<dyn crate::traits::vfs::VirtualFile>,
230    ) -> crate::err::ForensicResult<Box<dyn RegistryReader>> {
231        Ok(Box::new(TestingRegistry::new()))
232    }
233
234    fn from_fs(
235        &self,
236        _fs: Box<dyn crate::traits::vfs::VirtualFileSystem>,
237    ) -> crate::err::ForensicResult<Box<dyn RegistryReader>> {
238        Ok(Box::new(TestingRegistry::new()))
239    }
240
241    fn open_key(
242        &self,
243        hkey: crate::traits::registry::RegHiveKey,
244        key_name: &str,
245    ) -> crate::err::ForensicResult<crate::traits::registry::RegHiveKey> {
246        let mut borrowed = self.cached.borrow_mut();
247        let (hkey, path) = match borrowed.get(&hkey) {
248            Some(v) => {
249                let full_path = format!("{}\\{}", v, key_name);
250                if !self.contains(&full_path) {
251                    return Err(ForensicError::missing_string(format!(
252                        "Key path {} not found",
253                        full_path
254                    )));
255                }
256                let handle = self.increase_counter();
257                (handle, full_path)
258            }
259            None => return Err(ForensicError::missing_str("Hkey not found")),
260        };
261        borrowed.insert(RegHiveKey::Hkey(hkey), path);
262        Ok(RegHiveKey::Hkey(hkey))
263    }
264
265    fn read_value(
266        &self,
267        hkey: crate::traits::registry::RegHiveKey,
268        value_name: &str,
269    ) -> crate::err::ForensicResult<RegValue> {
270        let borrowed = self.cached.borrow();
271        let key_path = borrowed
272            .get(&hkey)
273            .ok_or_else(|| ForensicError::missing_str("HKey not found"))?;
274        let value = self.get_value(key_path, value_name).ok_or_else(|| {
275            ForensicError::missing_string(format!("Value {}\\{} not found", key_path, value_name))
276        })?;
277        Ok(value)
278    }
279
280    fn enumerate_values(
281        &self,
282        hkey: crate::traits::registry::RegHiveKey,
283    ) -> crate::err::ForensicResult<Vec<String>> {
284        let borrowed = self.cached.borrow();
285        let key_path = borrowed
286            .get(&hkey)
287            .ok_or_else(|| ForensicError::missing_str("HKey not found"))?;
288        let value = self.get_values(key_path).ok_or_else(|| {
289            ForensicError::missing_string(format!("Values for {} not found", key_path))
290        })?;
291        Ok(value)
292    }
293
294    fn enumerate_keys(
295        &self,
296        hkey: crate::traits::registry::RegHiveKey,
297    ) -> crate::err::ForensicResult<Vec<String>> {
298        let borrowed = self.cached.borrow();
299        let key_path = borrowed
300            .get(&hkey)
301            .ok_or_else(|| ForensicError::missing_str("HKey not found"))?;
302        let value = self.get_keys(key_path).ok_or_else(|| {
303            ForensicError::missing_string(format!("Keys for {} not found", key_path))
304        })?;
305        Ok(value)
306    }
307
308    fn key_at(
309        &self,
310        hkey: crate::traits::registry::RegHiveKey,
311        pos: u32,
312    ) -> crate::err::ForensicResult<String> {
313        let borrowed = self.cached.borrow();
314        let key_path = borrowed
315            .get(&hkey)
316            .ok_or_else(|| ForensicError::missing_str("HKey not found"))?;
317        let mut value = self.get_keys(key_path).ok_or_else(|| {
318            ForensicError::missing_string(format!("Keys for {} not found", key_path))
319        })?;
320        let pos = pos as usize;
321        if pos > value.len() {
322            return Err(ForensicError::NoMoreData);
323        }
324        Ok(value.remove(pos))
325    }
326
327    fn value_at(
328        &self,
329        hkey: crate::traits::registry::RegHiveKey,
330        pos: u32,
331    ) -> crate::err::ForensicResult<String> {
332        let borrowed = self.cached.borrow();
333        let key_path = borrowed
334            .get(&hkey)
335            .ok_or_else(|| ForensicError::missing_str("HKey not found"))?;
336        let mut value = self.get_values(key_path).ok_or_else(|| {
337            ForensicError::missing_string(format!("Values for {} not found", key_path))
338        })?;
339        let pos = pos as usize;
340        if pos > value.len() {
341            return Err(ForensicError::NoMoreData);
342        }
343        Ok(value.remove(pos))
344    }
345
346    fn key_info(&self, hkey: RegHiveKey) -> crate::err::ForensicResult<crate::traits::registry::RegistryKeyInfo> {
347        let borrowed = self.cached.borrow();
348        let key_path = borrowed
349            .get(&hkey)
350            .ok_or_else(|| ForensicError::missing_str("HKey not found"))?;
351        let value = self.get_values(key_path).ok_or_else(|| {
352            ForensicError::missing_string(format!("Values for {} not found", key_path))
353        })?;
354        let keys = self.get_keys(key_path).ok_or_else(|| {
355            ForensicError::missing_string(format!("Values for {} not found", key_path))
356        })?;
357        Ok(RegistryKeyInfo {
358            last_write_time : Filetime::new(0),
359            subkeys : keys.len() as u32,
360            values : value.len() as u32,
361            max_subkey_name_length : keys.iter().map(|v| v.len()).fold(0, |acc, e| e.max(acc)) as u32,
362            max_value_name_length: value.iter().map(|v| v.len()).fold(0, |acc, e| e.max(acc)) as u32,
363            max_value_length: 0,
364        })
365    }
366}
367fn basic_cache() -> BTreeMap<RegHiveKey, String> {
368    {
369        let mut map = BTreeMap::new();
370        for (k, p) in [
371            (RegHiveKey::HkeyLocalMachine, "HKLM"),
372            (RegHiveKey::HkeyCurrentUser, "HKCU"),
373            (RegHiveKey::HkeyUsers, "HKU"),
374            (RegHiveKey::HkeyClassesRoot, "HKCR"),
375        ] {
376            map.insert(k, p.to_string());
377        }
378        map
379    }
380}
381
382fn basic_registry() -> BTreeMap<String, MountedCell> {
383    let mut map = BTreeMap::new();
384    for k in ["HKLM", "HKCU", "HKCR"] {
385        map.insert(k.to_string(), MountedCell::new(k));
386    }
387    let mut hkcu_cell = MountedCell::new("HKU");
388    hkcu_cell.add_value(
389        "S-1-5-21-1366093794-4292800403-1155380978-513\\Volatile Environment",
390        "USERPROFILE",
391        RegValue::from_str(r"C:\Users\Tester"),
392    );
393    hkcu_cell.add_value(
394        "S-1-5-21-1366093794-4292800403-1155380978-513\\Volatile Environment",
395        "APPDATA",
396        RegValue::from_str(r"C:\Users\Tester\AppData\Roaming"),
397    );
398    hkcu_cell.add_value(
399        "S-1-5-21-1366093794-4292800403-1155380978-513\\Volatile Environment",
400        "LOCALAPPDATA",
401        RegValue::from_str(r"C:\Users\Tester\AppData\Local"),
402    );
403    hkcu_cell.add_value(
404        "S-1-5-21-1366093794-4292800403-1155380978-513\\Volatile Environment",
405        "USERDOMAIN",
406        RegValue::from_str(r"TestMachine"),
407    );
408    hkcu_cell.add_value(
409        "S-1-5-21-1366093794-4292800403-1155380978-513\\Volatile Environment",
410        "USERNAME",
411        RegValue::from_str(r"Tester"),
412    );
413    map.insert("HKU".into(), hkcu_cell);
414    map
415}
416
417pub fn init_testing_logger() {
418    let rcv = crate::notifications::testing_notifier_dummy();
419    std::thread::spawn(move || loop {
420        let msg = match rcv.recv() {
421            Ok(v) => v,
422            Err(_) => return,
423        };
424        println!(
425            "{:?} - {} - {}:{} - {}",
426            msg.r#type, msg.module, msg.file, msg.line, msg.data
427        );
428    });
429    let rcv = crate::logging::testing_logger_dummy();
430    std::thread::spawn(move || loop {
431        let msg = match rcv.recv() {
432            Ok(v) => v,
433            Err(_) => return,
434        };
435        println!(
436            "{:?} - {} - {}:{} - {}",
437            msg.level, msg.module, msg.file, msg.line, msg.data
438        );
439    });
440}