darklua_core/rules/rename_variables/
rename_processor.rs

1use crate::nodes::{Expression, Identifier, LocalFunctionStatement};
2use crate::process::utils::{identifier_permutator, CharPermutator};
3use crate::process::{utils::KEYWORDS, NodeProcessor, Scope};
4
5use std::cmp::Ordering;
6use std::collections::{HashMap, HashSet};
7use std::iter::FromIterator;
8use std::mem;
9
10#[derive(Debug)]
11pub struct RenameProcessor {
12    real_to_obfuscated: Vec<HashMap<String, (String, bool)>>,
13    permutator: CharPermutator,
14    avoid_identifier: HashSet<String>,
15    reuse_identifiers: Vec<String>,
16    include_functions: bool,
17}
18
19impl RenameProcessor {
20    pub fn new<I: IntoIterator<Item = String>>(iter: I, include_functions: bool) -> Self {
21        let mut avoid_identifier = HashSet::from_iter(iter);
22        avoid_identifier.extend(KEYWORDS.iter().map(|s| (*s).to_owned()));
23
24        Self {
25            real_to_obfuscated: Vec::new(),
26            permutator: identifier_permutator(),
27            avoid_identifier,
28            reuse_identifiers: Vec::new(),
29            include_functions,
30        }
31    }
32
33    pub fn add(&mut self, real: String, obfuscated: String, reuse: bool) {
34        if let Some(dictionary) = self.real_to_obfuscated.last_mut() {
35            dictionary.insert(real, (obfuscated, reuse));
36        } else {
37            let mut dictionary = HashMap::new();
38            dictionary.insert(real, (obfuscated, reuse));
39            self.real_to_obfuscated.push(dictionary);
40        }
41    }
42
43    pub fn get_obfuscated_name(&self, real: &str) -> Option<&String> {
44        self.real_to_obfuscated
45            .iter()
46            .rev()
47            .find_map(|dictionary| dictionary.get(real).map(|(name, _)| name))
48    }
49
50    pub fn generate_identifier(&mut self) -> String {
51        if let Some(identifier) = self.reuse_identifiers.pop() {
52            identifier
53        } else {
54            let generated = self.permutator.next().unwrap();
55
56            if self.filter_identifier(&generated) {
57                generated
58            } else {
59                self.generate_identifier()
60            }
61        }
62    }
63
64    fn filter_identifier(&self, identifier: &str) -> bool {
65        !self.avoid_identifier.contains(identifier)
66            && !identifier.chars().next().unwrap().is_ascii_digit()
67    }
68
69    fn replace_identifier(&mut self, identifier: &mut String) {
70        let original = mem::take(identifier);
71        let obfuscated_name = self.generate_identifier();
72
73        identifier.push_str(&obfuscated_name);
74
75        self.add(original, obfuscated_name, true);
76    }
77}
78
79fn sort_char(a: char, b: char) -> Ordering {
80    if a == b {
81        Ordering::Equal
82    } else if a.is_ascii_digit() && b.is_ascii_digit()
83        || a.is_lowercase() && b.is_lowercase()
84        || a.is_uppercase() && b.is_uppercase()
85    {
86        a.cmp(&b)
87    } else {
88        match (a, b) {
89            (a, _) if a.is_ascii_digit() => Ordering::Greater,
90            (_, b) if b.is_ascii_digit() => Ordering::Less,
91            ('_', _) => Ordering::Greater,
92            (_, '_') => Ordering::Less,
93            (a, _) if a.is_lowercase() => Ordering::Less,
94            (_, b) if b.is_lowercase() => Ordering::Greater,
95            _ => Ordering::Equal,
96        }
97    }
98}
99
100fn sort_identifiers(a: &str, b: &str) -> Ordering {
101    let mut b_chars = b.chars();
102
103    for a_char in a.chars() {
104        if let Some(b_char) = b_chars.next() {
105            match sort_char(a_char, b_char) {
106                Ordering::Less => return Ordering::Less,
107                Ordering::Greater => return Ordering::Greater,
108                Ordering::Equal => {}
109            }
110        } else {
111            return Ordering::Greater;
112        }
113    }
114
115    if b_chars.next().is_some() {
116        Ordering::Less
117    } else {
118        Ordering::Equal
119    }
120}
121
122impl Scope for RenameProcessor {
123    fn push(&mut self) {
124        self.real_to_obfuscated.push(HashMap::new())
125    }
126
127    fn pop(&mut self) {
128        if let Some(dictionary) = self.real_to_obfuscated.pop() {
129            self.reuse_identifiers.extend(
130                dictionary
131                    .into_values()
132                    .filter_map(|(name, reuse)| reuse.then_some(name)),
133            );
134            self.reuse_identifiers
135                .sort_by(|a, b| sort_identifiers(a, b).reverse());
136        }
137    }
138
139    fn insert(&mut self, identifier: &mut String) {
140        self.replace_identifier(identifier);
141    }
142
143    fn insert_self(&mut self) {
144        self.add("self".to_owned(), "self".to_owned(), false);
145    }
146
147    fn insert_local(&mut self, identifier: &mut String, _value: Option<&mut Expression>) {
148        self.replace_identifier(identifier);
149    }
150
151    fn insert_local_function(&mut self, function: &mut LocalFunctionStatement) {
152        if self.include_functions {
153            self.replace_identifier(function.mutate_identifier().mutate_name());
154        } else {
155            let name = function.mutate_identifier().get_name();
156            self.add(name.clone(), name.to_owned(), false);
157        }
158    }
159}
160
161impl NodeProcessor for RenameProcessor {
162    fn process_variable_expression(&mut self, variable: &mut Identifier) {
163        if let Some(obfuscated_name) = self.get_obfuscated_name(variable.get_name()) {
164            variable.set_name(obfuscated_name);
165        }
166    }
167}
168
169#[cfg(test)]
170mod test {
171    use super::*;
172
173    fn new_scope() -> RenameProcessor {
174        RenameProcessor::new(Vec::new(), true)
175    }
176
177    #[test]
178    fn pop_root_should_not_panic() {
179        new_scope().pop();
180    }
181
182    #[test]
183    fn should_get_mapped_name_from_inserted_names() {
184        let mut scope = new_scope();
185        let real = "a".to_owned();
186        let obfuscated = "b".to_owned();
187
188        scope.add(real.clone(), obfuscated.clone(), true);
189
190        assert_eq!(&obfuscated, scope.get_obfuscated_name(&real).unwrap());
191    }
192
193    #[test]
194    fn mapped_name_should_not_exist_after_pop() {
195        let mut scope = new_scope();
196        let real = "a".to_owned();
197        let obfuscated = "def".to_owned();
198
199        scope.push();
200        scope.add(real.clone(), obfuscated, true);
201        scope.pop();
202
203        assert_eq!(None, scope.get_obfuscated_name(&real));
204    }
205
206    #[test]
207    fn remapped_name_should_exist_after_pop() {
208        let mut scope = new_scope();
209        let real = "a".to_owned();
210        let obfuscated = "b".to_owned();
211        let other_obfuscated = "c".to_owned();
212
213        scope.add(real.clone(), obfuscated.clone(), true);
214
215        scope.push();
216        scope.add(real.clone(), other_obfuscated, true);
217        scope.pop();
218
219        assert_eq!(&obfuscated, scope.get_obfuscated_name(&real).unwrap());
220    }
221
222    #[test]
223    fn sort_char_digits() {
224        assert_eq!(sort_char('0', '1'), Ordering::Less);
225        assert_eq!(sort_char('1', '2'), Ordering::Less);
226        assert_eq!(sort_char('4', '2'), Ordering::Greater);
227        assert_eq!(sort_char('5', '5'), Ordering::Equal);
228    }
229
230    #[test]
231    fn sort_char_lowercase_letters() {
232        assert_eq!(sort_char('a', 'f'), Ordering::Less);
233        assert_eq!(sort_char('y', 'i'), Ordering::Greater);
234        assert_eq!(sort_char('t', 't'), Ordering::Equal);
235    }
236
237    #[test]
238    fn sort_char_uppercase_letters() {
239        assert_eq!(sort_char('A', 'F'), Ordering::Less);
240        assert_eq!(sort_char('Y', 'I'), Ordering::Greater);
241        assert_eq!(sort_char('T', 'T'), Ordering::Equal);
242    }
243
244    #[test]
245    fn sort_char_underscore_is_less_than_digit() {
246        for digit in "0123456789".chars() {
247            assert_eq!(sort_char('_', digit), Ordering::Less);
248        }
249    }
250
251    #[test]
252    fn sort_char_underscore_is_greather_than_letters() {
253        assert_eq!(sort_char('_', 'a'), Ordering::Greater);
254        assert_eq!(sort_char('_', 'A'), Ordering::Greater);
255        assert_eq!(sort_char('a', '_'), Ordering::Less);
256        assert_eq!(sort_char('A', '_'), Ordering::Less);
257    }
258
259    #[test]
260    fn sort_char_lowercase_is_less_than_uppercase() {
261        assert_eq!(sort_char('a', 'A'), Ordering::Less);
262        assert_eq!(sort_char('A', 'a'), Ordering::Greater);
263        assert_eq!(sort_char('z', 'A'), Ordering::Less);
264        assert_eq!(sort_char('A', 'z'), Ordering::Greater);
265    }
266
267    #[test]
268    fn sort_identifiers_compare_chars() {
269        assert_eq!(sort_identifiers("foo", "foo"), Ordering::Equal);
270        assert_eq!(sort_identifiers("aA", "ab"), Ordering::Greater);
271        assert_eq!(sort_identifiers("foo1", "foo9"), Ordering::Less);
272    }
273
274    #[test]
275    fn sort_identifiers_shorter_is_less() {
276        assert_eq!(sort_identifiers("foo", "fooo"), Ordering::Less);
277    }
278
279    #[test]
280    fn sort_identifiers_longer_is_greather() {
281        assert_eq!(sort_identifiers("foo", "fo"), Ordering::Greater);
282    }
283}