Skip to main content

string_analyze/
rules.rs

1//string_analyze/src/rules.rs
2
3pub mod b64file;
4pub mod core;
5pub mod hexfile;
6pub mod coding;
7
8use std::collections::HashSet;
9use std::sync::LazyLock;
10
11use crate::entropy::composite_entropy;
12use crate::{AnyRule, Hit, RuleResult, State};
13
14pub struct Assertion {
15    pub offset: usize,
16    pub expected: bool,
17    pub find: Find,
18}
19
20pub enum Find {
21    Regex(regex::Regex),
22    Fn(fn(&mut State) -> bool),
23}
24
25impl Find {
26    pub fn find(&self, state: &mut State) -> bool {
27        match self {
28            Find::Regex(regex) => has_regex(state, regex),
29            Find::Fn(f) => f(state),
30        }
31    }
32    pub fn is_match(&self, input: &str) -> bool {
33        match self {
34            Find::Regex(regex) => regex.is_match(input),
35            Find::Fn(_) => {
36                let mut temp_state = State::new(input);
37                self.find(&mut temp_state)
38            }
39        }
40    }
41}
42
43pub struct RawRule {
44    pub find: Find,
45    pub describe: &'static str,
46    pub importance: u8,
47    pub min_entropy: Option<f64>,
48    pub before_assert: Option<Assertion>, // 向前断言
49    pub after_assert: Option<Assertion>,  // 向后断言
50    pub is_and_assert: bool,              // true 为“与”(AND),false 为“或”(OR),默认为 true
51    pub check: Option<fn(&str, &str) -> bool>,
52}
53
54impl RawRule {
55    pub fn check_custom(&self, state: &mut State) {
56        if let Some(check_fn) = self.check {
57            state.retain(|input, (start, end)| check_fn(&input[start..end], input));
58        }
59    }
60
61    fn check_assertions(&self, state: &mut State) {
62        state.retain(|input, (start, end)| {
63            // 1. 校验向前断言 (Lookbehind)
64            let before_pass = self.before_assert.as_ref().map(|assert| {
65                let mut check_start = start.saturating_sub(assert.offset);
66                while check_start > 0 && !input.is_char_boundary(check_start) {
67                    check_start -= 1;
68                }
69                let slice = &input[check_start..start];
70                assert.find.is_match(slice) == assert.expected
71            });
72
73            // 2. 校验向后断言 (Lookahead)
74            let after_pass = self.after_assert.as_ref().map(|assert| {
75                let mut check_end = (end + assert.offset).min(input.len());
76                while check_end < input.len() && !input.is_char_boundary(check_end) {
77                    check_end += 1;
78                }
79                let slice = &input[end..check_end];
80                assert.find.is_match(slice) == assert.expected
81            });
82
83            // 3. 结合两者的组合逻辑 (AND / OR)
84            match (before_pass, after_pass) {
85                (Some(b), Some(a)) => {
86                    if self.is_and_assert {
87                        b && a // 两个断言都必须满足
88                    } else {
89                        b || a // 满足任意一个断言即可
90                    }
91                }
92                (Some(b), None) => b,
93                (None, Some(a)) => a,
94                (None, None) => true,
95            }
96        });
97    }
98}
99pub struct RuleEntry {
100    pub name: &'static str,
101    pub module_path: &'static str,
102    pub importance: u8,
103    pub get_rule: fn() -> &'static RawRule,
104}
105
106inventory::collect!(RuleEntry);
107
108macro_rules! lazy_rule {
109
110    // ==========================================
111    // 基础公有匹配模式
112    // ==========================================
113
114    // 匹配闭包
115    ($name:ident = |$state:ident| $find:expr, $describe:literal, $importance:literal $(, $($rest:tt)+)?) => {
116        $crate::rules::lazy_rule!(@parse_args
117            $name,
118            $crate::rules::Find::Fn(|$state: &mut $crate::State| $find),
119            $describe,
120            $importance
121            $(, $($rest)+)?
122        );
123    };
124    // 匹配正则表达式
125    ($name:ident = $re:literal, $describe:literal, $importance:literal $(, $($rest:tt)+)?) => {
126        $crate::rules::lazy_rule!(@parse_args
127            $name,
128            $crate::rules::Find::Regex(regex::Regex::new($re).expect(concat!("Invalid regex in ", stringify!($name)))),
129            $describe,
130            $importance
131            $(, $($rest)+)?
132        );
133    };
134
135    // ==========================================
136    // 中间宏:用来构建单一的 Assertion
137    // ==========================================
138
139    (@build_assert None) => { None };
140
141    (@build_assert ($offset:expr, $expected:expr, $re:literal)) => {
142        Some($crate::rules::Assertion {
143            offset: $offset,
144            expected: $expected,
145            find: $crate::rules::Find::Regex(regex::Regex::new($re).expect("Invalid regex in assertion")),
146        })
147    };
148
149    (@build_assert ($offset:expr, $expected:expr, |$state:ident| $func:expr)) => {
150        Some($crate::rules::Assertion {
151            offset: $offset,
152            expected: $expected,
153            find: $crate::rules::Find::Fn(|$state: &mut $crate::State| $func),
154        })
155    };
156
157    // ==========================================
158    // 中间宏:用来构建 (before, after, is_and) 断言元组
159    // ==========================================
160
161    // 1. 显式指定 is_and 参数: ($before, $after, false/true)
162    (@build_assert_tuple ($before_assert:tt, $after_assert:tt, $is_and:expr)) => {
163        (
164            $crate::rules::lazy_rule!(@build_assert $before_assert),
165            $crate::rules::lazy_rule!(@build_assert $after_assert),
166            $is_and,
167        )
168    };
169    // 2. 隐式关系(默认为与 / true): ($before, $after)
170    (@build_assert_tuple ($before_assert:tt, $after_assert:tt)) => {
171        $crate::rules::lazy_rule!(@build_assert_tuple ($before_assert, $after_assert, true))
172    };
173    // 3. 仅向前断言: ($before, )
174    (@build_assert_tuple ($before_assert:tt, )) => {
175        (
176            $crate::rules::lazy_rule!(@build_assert $before_assert),
177            None,
178            true,
179        )
180    };
181    // 4. 仅向后断言: (, $after)
182    (@build_assert_tuple (, $after_assert:tt)) => {
183        (
184            None,
185            $crate::rules::lazy_rule!(@build_assert $after_assert),
186            true,
187        )
188    };
189
190
191    // ==========================================
192    // 根基解析逻辑
193    // ==========================================
194    (@parse_args $name:ident, $filter:expr, $describe:literal, $importance:literal, $min_entropy:expr, $asserts:expr, $check:expr $(,)?) => {
195        pub static $name: std::sync::LazyLock<$crate::rules::RawRule> = std::sync::LazyLock::new(|| {
196            let asserts = $asserts;
197            $crate::rules::RawRule {
198                find: $filter,
199                describe: $describe,
200                importance: $importance,
201                min_entropy: $min_entropy,
202                before_assert: asserts.0,
203                after_assert: asserts.1,
204                is_and_assert: asserts.2,
205                check: $check,
206            }
207        });
208        inventory::submit! {
209            $crate::rules::RuleEntry {
210                name: stringify!($name),
211                module_path: module_path!(),
212                importance: $importance,
213                get_rule: || &$name
214            }
215        }
216    };
217
218    // ==========================================
219    // 参数组合重载解析
220    // ==========================================
221
222    // === 6 字段: 包含熵, 断言元组, 自定义 check ===
223    (@parse_args $name:ident, $filter:expr, $describe:literal, $importance:literal, $min_entropy:expr, ($($asserts:tt)+), |$slice:pat_param,$input:pat_param| $check:expr $(,)?) => {
224        $crate::rules::lazy_rule!(@parse_args $name, $filter, $describe, $importance, Some($min_entropy),
225            $crate::rules::lazy_rule!(@build_assert_tuple ($($asserts)+)),
226            Some(|$slice,$input| $check)
227        );
228    };
229
230    // === 5 字段组合 ===
231    // 无熵, 断言元组, 自定义 check
232    (@parse_args $name:ident, $filter:expr, $describe:literal, $importance:literal, ($($asserts:tt)+), |$slice:pat_param,$input:pat_param| $check:expr $(,)?) => {
233        $crate::rules::lazy_rule!(@parse_args $name, $filter, $describe, $importance, None,
234            $crate::rules::lazy_rule!(@build_assert_tuple ($($asserts)+)),
235            Some(|$slice,$input| $check)
236        );
237    };
238    // 包含熵, 断言元组, 无 check
239    (@parse_args $name:ident, $filter:expr, $describe:literal, $importance:literal, $min_entropy:expr, ($($asserts:tt)+) $(,)?) => {
240        $crate::rules::lazy_rule!(@parse_args $name, $filter, $describe, $importance, Some($min_entropy),
241            $crate::rules::lazy_rule!(@build_assert_tuple ($($asserts)+)),
242            None
243        );
244    };
245    // 包含熵, 无断言元组, 有 check
246    (@parse_args $name:ident, $filter:expr, $describe:literal, $importance:literal, $min_entropy:expr, |$slice:pat_param,$input:pat_param| $check:expr $(,)?) => {
247        $crate::rules::lazy_rule!(@parse_args $name, $filter, $describe, $importance, Some($min_entropy), (None, None, true), Some(|$slice,$input| $check));
248    };
249
250    // === 4 字段组合 ===
251    // 无熵, 无断言元组, 有 check
252    (@parse_args $name:ident, $filter:expr, $describe:literal, $importance:literal, |$slice:pat_param,$input:pat_param| $check:expr $(,)?) => {
253        $crate::rules::lazy_rule!(@parse_args $name, $filter, $describe, $importance, None, (None, None, true), Some(|$slice,$input| $check));
254    };
255    // 无熵, 有断言元组, 无 check
256    (@parse_args $name:ident, $filter:expr, $describe:literal, $importance:literal, ($($asserts:tt)+) $(,)?) => {
257        $crate::rules::lazy_rule!(@parse_args $name, $filter, $describe, $importance, None,
258            $crate::rules::lazy_rule!(@build_assert_tuple ($($asserts)+)),
259            None
260        );
261    };
262    // 有熵, 无断言元组, 无 check
263    (@parse_args $name:ident, $filter:expr, $describe:literal, $importance:literal, $min_entropy:expr $(,)?) => {
264        $crate::rules::lazy_rule!(@parse_args $name, $filter, $describe, $importance, Some($min_entropy), (None, None, true), None);
265    };
266
267    // === 3 字段 (最简版本) ===
268    (@parse_args $name:ident, $filter:expr, $describe:literal, $importance:literal $(,)?) => {
269        $crate::rules::lazy_rule!(@parse_args $name, $filter, $describe, $importance, None, (None, None, true), None);
270    };
271}
272
273use crate::tool::has_regex;
274pub(crate) use lazy_rule;
275
276pub static ALL_RULES: LazyLock<Vec<AnyRule>> = LazyLock::new(|| get_rules(|_, _| true));
277
278pub fn get_rules<F>(filter: F) -> Vec<AnyRule>
279where
280    F: Fn(&str, u8) -> bool,
281{
282    let mut rules = Vec::new();
283    let mut seen_names = HashSet::new();
284
285    for entry in inventory::iter::<RuleEntry> {
286        if filter(entry.module_path, entry.importance) {
287
288            if !seen_names.insert(entry.name) {
289                continue;
290            }
291
292            let r: &'static RawRule = (entry.get_rule)();
293
294            rules.push(
295                AnyRule::new(|state| Hit {
296                    describe: r.describe.into(),
297                    importance: r.importance,
298                    data: RuleResult::from(state),
299                })
300                .add_flow(move |state| r.find.find(state))
301                .add_flow(move |state| {
302                    if let Some(min_ent) = r.min_entropy {
303                        state.retain(|input, (start, end)| {
304                            composite_entropy((&input[start..end]).as_bytes()) >= min_ent
305                        });
306                    }
307                    !state.ranges.is_empty()
308                })
309                .add_flow(move |state| {
310                    r.check_assertions(state);
311                    !state.ranges.is_empty()
312                })
313                .add_flow(move |state| {
314                    r.check_custom(state);
315                    !state.ranges.is_empty()
316                }),
317            );
318        }
319    }
320
321    rules
322}