rusty_dex/dex/
classes.rs

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