rusty_dex/dex/
classes.rs

1//! Representation of a class and encoded methods
2//!
3//! 
4
5use std::io::{Seek, SeekFrom};
6use lazy_static::lazy_static;
7use regex::Regex;
8use log::{ warn, debug };
9
10use crate::dex::reader::DexReader;
11use crate::dex::access_flags::{ AccessFlag, AccessFlagType };
12use crate::dex::code_item::CodeItem;
13
14use crate::dex::strings::DexStrings;
15use crate::dex::types::DexTypes;
16use crate::dex::fields::DexFields;
17use crate::dex::methods::DexMethods;
18
19/// Constant to represent the absence of index
20const NO_INDEX: u32 = 0xffffffff;
21
22lazy_static!{
23    /// Regex for method prototypes
24    static ref METHOD_REGEX: Regex = Regex::new(r"(?x)
25        (?P<class>L[a-zA-Z/$0-9]+;)
26        (->)
27        (?P<method><?[a-zA-Z0-9]+>?[\$\d+]*)
28        (?P<args>\(.*\).*)
29    ").unwrap();
30}
31
32/// Class definition
33#[derive(Debug)]
34pub struct ClassDefItem {
35    class_str: String,
36    access_flags: Vec<AccessFlag>,
37    superclass_str: Option<String>,
38    interfaces_off: u32,
39    source_file_str: Option<String>,
40    annotations_off: u32,
41    class_data_off: u32,
42    static_value_off: u32,
43    class_data: Option<ClassDataItem>
44}
45
46#[derive(Debug)]
47pub struct EncodedField {
48    field: String,
49    access_flags: Vec<AccessFlag>,
50}
51
52#[derive(Debug)]
53pub struct EncodedMethod {
54    pub proto: String,
55    pub access_flags: Vec<AccessFlag>,
56    pub code_item: Option<CodeItem>,
57}
58
59#[derive(Debug)]
60pub struct ClassDataItem {
61    static_fields: Vec<EncodedField>,
62    instance_fields: Vec<EncodedField>,
63    direct_methods: Vec<EncodedMethod>,
64    virtual_methods: Vec<EncodedMethod>,
65}
66
67#[derive(Debug)]
68pub struct DexClasses {
69    pub items: Vec<ClassDefItem>
70}
71
72impl DexClasses {
73    pub fn build(dex_reader: &mut DexReader,
74                 offset: u32,
75                 size: u32,
76                 fields_list: &DexFields,
77                 types_list: &DexTypes,
78                 strings_list: &DexStrings,
79                 methods_list: &DexMethods) -> Self {
80        dex_reader.bytes.seek(SeekFrom::Start(offset.into())).unwrap();
81
82        let mut methods = Vec::new();
83
84        for _ in 0..size {
85            let class_idx = dex_reader.read_u32().unwrap();
86            let access_flags = dex_reader.read_u32().unwrap();
87            let access_flags_decoded = AccessFlag::parse(access_flags,
88                                                         AccessFlagType::Class);
89
90            let superclass_idx   = dex_reader.read_u32().unwrap();
91            let interfaces_off   = dex_reader.read_u32().unwrap();
92            let source_file_idx  = dex_reader.read_u32().unwrap();
93            let annotations_off  = dex_reader.read_u32().unwrap();
94            let class_data_off   = dex_reader.read_u32().unwrap();
95            let static_value_off = dex_reader.read_u32().unwrap();
96
97            // Convert indexs into human-readable strings
98            let class_str = types_list.items
99                                      .get(class_idx as usize).unwrap();
100
101            let mut superclass_str = None;
102            if superclass_idx != NO_INDEX {
103                superclass_str = Some(types_list.items
104                                                .get(superclass_idx as usize)
105                                                .unwrap());
106            }
107
108            let mut source_file_str = None;
109            if source_file_idx != NO_INDEX {
110                source_file_str = Some(strings_list.strings
111                                                   .get(source_file_idx as usize)
112                                                   .unwrap());
113            }
114
115            // If class_data_off == 0 then we have no class data
116            let mut class_data = None;
117            if class_data_off != 0 {
118                // Start parse class data
119
120                // Keep track of current stream position
121                let current_offset = dex_reader.bytes.position();
122
123                // Go to class data offset
124                dex_reader.bytes.seek(SeekFrom::Start(class_data_off.into())).unwrap();
125
126                let (static_fields_size, _)   = dex_reader.read_uleb128().unwrap();
127                let (instance_fields_size, _) = dex_reader.read_uleb128().unwrap();
128                let (direct_methods_size, _)  = dex_reader.read_uleb128().unwrap();
129                let (virtual_methods_size, _) = dex_reader.read_uleb128().unwrap();
130
131                let mut static_fields   = Vec::<EncodedField>::with_capacity(static_fields_size as usize);
132                let mut instance_fields = Vec::<EncodedField>::with_capacity(instance_fields_size as usize);
133                let mut direct_methods  = Vec::<EncodedMethod>::with_capacity(direct_methods_size as usize);
134                let mut virtual_methods = Vec::<EncodedMethod>::with_capacity(virtual_methods_size as usize);
135
136                // Encoded fields
137                let mut field_idx = 0;
138                for _ in 0..static_fields_size {
139                    let (idx, _) = dex_reader.read_uleb128().unwrap();
140                    let (access_flags, _) = dex_reader.read_uleb128().unwrap();
141
142                    field_idx += idx;
143
144                    let decoded_field = fields_list.items.get(field_idx as usize)
145                                                         .unwrap()
146                                                         .to_string();
147                    let decoded_flags = AccessFlag::parse(access_flags,
148                                                          AccessFlagType::Field);
149
150                    static_fields.push(EncodedField {
151                        field: decoded_field,
152                        access_flags: decoded_flags
153                    });
154                }
155
156                let mut field_idx = 0;
157                for _ in 0..instance_fields_size {
158                    let (idx, _) = dex_reader.read_uleb128().unwrap();
159                    let (access_flags, _) = dex_reader.read_uleb128().unwrap();
160
161                    field_idx += idx;
162
163                    let decoded_field = fields_list.items.get(field_idx as usize)
164                                                         .unwrap()
165                                                         .to_string();
166                    let decoded_flags = AccessFlag::parse(access_flags,
167                                                          AccessFlagType::Field);
168
169                    instance_fields.push(EncodedField {
170                        field: decoded_field,
171                        access_flags: decoded_flags
172                    });
173                }
174
175                // Encoded methods
176                let mut method_idx = 0;
177                for _ in 0..direct_methods_size {
178                    let (idx, _) = dex_reader.read_uleb128().unwrap();
179                    let (access_flags, _) = dex_reader.read_uleb128().unwrap();
180                    let (code_offset, _) = dex_reader.read_uleb128().unwrap();
181
182                    method_idx += idx;
183
184                    let proto = methods_list.items.get(method_idx as usize)
185                                                  .unwrap()
186                                                  .to_string();
187                    let decoded_flags = AccessFlag::parse(access_flags,
188                                                          AccessFlagType::Method);
189
190                    if code_offset == 0 {
191                        // Abstract or native methods have no code
192                        direct_methods.push(EncodedMethod {
193                            proto: proto.to_string(),
194                            access_flags: decoded_flags,
195                            code_item: None
196                        });
197                    } else {
198                        let current_offset = dex_reader.bytes.position();
199                        let code_item = CodeItem::build(dex_reader,
200                                                        code_offset,
201                                                        types_list);
202                        dex_reader.bytes.seek(SeekFrom::Start(current_offset)).unwrap();
203
204                        direct_methods.push(EncodedMethod {
205                            proto: proto.to_string(),
206                            access_flags: decoded_flags,
207                            code_item: Some(code_item),
208                        });
209                    }
210                }
211
212                let mut method_idx = 0;
213                for _ in 0..virtual_methods_size {
214                    let (idx, _) = dex_reader.read_uleb128().unwrap();
215                    let (access_flags, _) = dex_reader.read_uleb128().unwrap();
216                    let (code_offset, _) = dex_reader.read_uleb128().unwrap();
217
218                    method_idx += idx;
219
220                    let proto = methods_list.items.get(method_idx as usize)
221                                                  .unwrap()
222                                                  .to_string();
223                    let decoded_flags = AccessFlag::parse(access_flags,
224                                                          AccessFlagType::Method);
225
226                    if code_offset == 0 {
227                        // Abstract or native methods have no code
228                        virtual_methods.push(EncodedMethod {
229                            proto: proto.to_string(),
230                            access_flags: decoded_flags,
231                            code_item: None
232                        });
233                    } else {
234                        let current_offset = dex_reader.bytes.position();
235                        let code_item = CodeItem::build(dex_reader,
236                                                        code_offset,
237                                                        types_list);
238                        dex_reader.bytes.seek(SeekFrom::Start(current_offset)).unwrap();
239
240                        virtual_methods.push(EncodedMethod {
241                            proto: proto.to_string(),
242                            access_flags: decoded_flags,
243                            code_item: Some(code_item),
244                        });
245                    }
246                }
247
248                // Go back to the previous offset
249                dex_reader.bytes.seek(SeekFrom::Start(current_offset)).unwrap();
250
251                class_data = Some(ClassDataItem {
252                    static_fields,
253                    instance_fields,
254                    direct_methods,
255                    virtual_methods,
256                });
257            }
258
259            methods.push(ClassDefItem {
260                class_str: class_str.to_string(),
261                access_flags: access_flags_decoded,
262                superclass_str: superclass_str.cloned(),
263                interfaces_off,
264                source_file_str: source_file_str.cloned(),
265                annotations_off,
266                class_data_off,
267                static_value_off,
268                class_data
269            });
270        }
271
272        DexClasses { items: methods }
273    }
274
275    pub fn get_class_def(&self, class_name: &String) -> Option<&ClassDefItem> {
276        for item in self.items.iter() {
277            if &item.class_str == class_name {
278                return Some(&item);
279            }
280        }
281
282        None
283    }
284}
285
286impl ClassDefItem {
287    pub fn get_class_name(&self) -> &String {
288        &self.class_str
289    }
290
291    pub fn get_access_flags(&self) -> String {
292        AccessFlag::vec_to_string(&self.access_flags)
293    }
294
295    pub fn get_methods(&self) -> Vec<&EncodedMethod> {
296        let mut methods = Vec::new();
297
298        if let Some(class_data) = &self.class_data {
299            methods.extend(&class_data.direct_methods);
300            methods.extend(&class_data.virtual_methods);
301        }
302
303        methods
304    }
305
306    pub fn get_encoded_method(&self, method_name: &String) -> Option<&EncodedMethod> {
307        if let Some(class_data) = &self.class_data {
308            for method in &class_data.direct_methods {
309                if method.get_method_name() == method_name {
310                    return Some(method);
311                }
312            }
313            for method in &class_data.virtual_methods {
314                if method.get_method_name() == method_name {
315                    return Some(method);
316                }
317            }
318        }
319        None
320    }
321}
322
323impl EncodedMethod {
324    pub fn get_proto(&self) -> &str {
325        &self.proto
326    }
327
328    pub fn get_method_name(&self) -> &str {
329        let matches = METHOD_REGEX.captures(&self.proto);
330        let method_name = match matches {
331            Some(matched) => {
332                match matched.name("method") {
333                    Some(name) => name.as_str(),
334                    None => ""
335                }
336            },
337            None => ""
338        };
339
340        if method_name.is_empty() {
341            warn!("Cannot retrieve method name from prototype");
342            debug!("Prototype: {}", &self.proto);
343        };
344
345        method_name
346    }
347
348    pub fn get_access_flags(&self) -> String {
349        AccessFlag::vec_to_string(&self.access_flags)
350    }
351}