memscan/
lib.rs

1use rayon::prelude::*;
2use std::str::FromStr;
3use subslice_index::subslice_index;
4
5/// A memory pattern. Wildcards are represented in raw buffers
6/// as null bytes. Must not be empty
7pub struct Pattern {
8    buf: Vec<u8>,
9}
10
11impl Pattern {
12    /// Tests if a pattern matches a slice of bytes exactly
13    ///
14    /// # Arguments
15    ///
16    /// `buf`: The slice of bytes
17    pub fn matches(&self, buf: &[u8]) -> bool {
18        self.buf
19            .par_iter()
20            .zip(buf.par_iter())
21            .all(|(&pattern_byte, &buffer_byte)| {
22                pattern_byte == 0x00 || pattern_byte == buffer_byte
23            })
24    }
25}
26
27impl From<&[u8]> for Pattern {
28    fn from(buf: &[u8]) -> Pattern {
29        Pattern {
30            buf: Vec::from(buf),
31        }
32    }
33}
34
35/// This is not recommended, but is provided as a compatibility layer for
36/// those who have signatures in IDA format, for example
37impl FromStr for Pattern {
38    type Err = usize;
39
40    /// The index of the bad pattern character is returned
41    fn from_str(buf_str: &str) -> Result<Pattern, Self::Err> {
42        let mut buf = Vec::new();
43        for byte_str in buf_str.split_ascii_whitespace() {
44            if byte_str == "?" {
45                buf.push(0x00);
46            } else {
47                match u8::from_str_radix(byte_str, 16) {
48                    Ok(byte) => {
49                        buf.push(byte);
50                    }
51                    Err(_) => return Err(subslice_index(buf_str.as_bytes(), byte_str.as_bytes())),
52                };
53            }
54        }
55        Ok(Pattern { buf })
56    }
57}
58
59/// Finds a pattern in a buffer
60///
61/// # Arguments
62///
63/// `buf`: The buffer to search for the pattern in
64/// `pattern`: The pattern to find. Must not be empty
65pub fn find_pattern(buf: &[u8], pattern: Pattern) -> Vec<&[u8]> {
66    buf.par_windows(pattern.buf.len())
67        .filter(|&window| pattern.matches(window))
68        .collect()
69}
70
71#[cfg(test)]
72mod test {
73    use super::*;
74
75    #[test]
76    #[should_panic]
77    fn empty_pattern() {
78        let buf = &[1, 2, 3];
79        let pattern = Pattern::from(&[][..]);
80        find_pattern(buf, pattern);
81    }
82
83    #[test]
84    fn not_empty() {
85        let buf = &[1, 2, 3];
86        let pattern = Pattern::from(&[1, 2][..]);
87        assert!(!find_pattern(buf, pattern).is_empty());
88    }
89
90    #[test]
91    fn empty() {
92        let buf = &[1, 2, 3];
93        let pattern = Pattern::from(&[1, 3][..]);
94        assert!(find_pattern(buf, pattern).is_empty());
95    }
96
97    #[test]
98    fn simple_wildcard_not_empty() {
99        let buf = &[1, 2, 3];
100        let pattern = Pattern::from(&[1, 0x00, 3][..]);
101        assert!(!find_pattern(buf, pattern).is_empty());
102    }
103
104    #[test]
105    fn simple_wildcard_empty() {
106        let buf = &[1, 2, 3];
107        let pattern = Pattern::from(&[1, 0x00, 4][..]);
108        assert!(find_pattern(buf, pattern).is_empty());
109    }
110
111    #[test]
112    fn wildcard_start_not_empty() {
113        let buf = &[1, 2, 3];
114        let pattern = Pattern::from(&[0x00, 3][..]);
115        assert!(!find_pattern(buf, pattern).is_empty());
116    }
117
118    #[test]
119    fn wildcard_start_empty() {
120        let buf = &[1, 2, 3];
121        let pattern = Pattern::from(&[0x00, 1][..]);
122        assert!(find_pattern(buf, pattern).is_empty());
123    }
124
125    #[test]
126    fn wildcard_end_not_empty() {
127        let buf = &[1, 2, 3];
128        let pattern = Pattern::from(&[1, 2, 0x00][..]);
129        assert!(!find_pattern(buf, pattern).is_empty());
130    }
131
132    #[test]
133    fn wildcard_end_empty() {
134        let buf = &[1, 2, 3];
135        let pattern = Pattern::from(&[2, 3, 0x00][..]);
136        assert!(find_pattern(buf, pattern).is_empty());
137    }
138
139    #[test]
140    fn multi_match() {
141        let buf = &[1, 2, 3, 4, 3, 2, 1, 2, 3];
142        let pattern = Pattern::from(&[1, 2, 0x00][..]);
143        assert_eq!(find_pattern(buf, pattern).len(), 2);
144    }
145
146    #[test]
147    fn function_signature() {
148        let buf = include_bytes!("..\\test\\crt.exe");
149        let pattern = Pattern::from(
150            &[
151                0xe8, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0xd8,
152            ][..],
153        );
154        let result = find_pattern(buf, pattern);
155        assert!(!result.is_empty());
156        assert_eq!(
157            result[0],
158            &[0xe8, 0x1c, 0x04, 0x00, 0x00, 0xe8, 0xcb, 0x05, 0x00, 0x00, 0x48, 0x8b, 0xd8][..]
159        );
160        assert_eq!(subslice_index(buf, result[0]), 0x5bb);
161    }
162}