shuriken_bindings/
disassembler.rs

1//! Disassembler data structs
2
3use std::ffi::CStr;
4use std::slice::from_raw_parts;
5
6use crate::shuriken;
7use crate::parser::DvmMethod;
8
9/// Type alias for Shuriken's `dexinsttype_e`
10///
11/// Instruction types from the Dalvik Virtual Machine
12#[derive(Debug, Clone, Copy, PartialEq)]
13pub enum DexInstType {
14    DexInstruction00x,
15    DexInstruction10x,
16    DexInstruction12x,
17    DexInstruction11n,
18    DexInstruction11x,
19    DexInstruction10t,
20    DexInstruction20t,
21    DexInstruction20bc,
22    DexInstruction22x,
23    DexInstruction21t,
24    DexInstruction21s,
25    DexInstruction21h,
26    DexInstruction21c,
27    DexInstruction23x,
28    DexInstruction22b,
29    DexInstruction22t,
30    DexInstruction22s,
31    DexInstruction22c,
32    DexInstruction22cs,
33    DexInstruction30t,
34    DexInstruction32x,
35    DexInstruction31i,
36    DexInstruction31t,
37    DexInstruction31c,
38    DexInstruction35c,
39    DexInstruction3rc,
40    DexInstruction45cc,
41    DexInstruction4rcc,
42    DexInstruction51l,
43    DexPackedSwitch,
44    DexSparseSwitch,
45    DexFillArrayData,
46    DexDalvikIncorrect,
47    DexNoneOp = 99,
48}
49
50/// Type alias for Shuriken's `hdvminstruction_t`
51///
52/// Structure for an instruction in the dalvik virtual machine
53#[derive(Debug, PartialEq)]
54pub struct DvmInstruction {
55    instruction_type: DexInstType,
56    instruction_length: usize,
57    address: u64,
58    // TODO: replace with a enum of all opcodes maybe?
59    op: u32,
60    disassembly: String
61}
62
63impl DvmInstruction {
64    pub fn from_ins(ins: shuriken::hdvminstruction_t) -> Self {
65        let instruction_type = match ins.instruction_type {
66            0  => DexInstType::DexInstruction00x,
67            1  => DexInstType::DexInstruction10x,
68            2  => DexInstType::DexInstruction12x,
69            3  => DexInstType::DexInstruction11n,
70            4  => DexInstType::DexInstruction11x,
71            5  => DexInstType::DexInstruction10t,
72            6  => DexInstType::DexInstruction20t,
73            7  => DexInstType::DexInstruction20bc,
74            8  => DexInstType::DexInstruction22x,
75            9  => DexInstType::DexInstruction21t,
76            10 => DexInstType::DexInstruction21s,
77            11 => DexInstType::DexInstruction21h,
78            12 => DexInstType::DexInstruction21c,
79            13 => DexInstType::DexInstruction23x,
80            14 => DexInstType::DexInstruction22b,
81            15 => DexInstType::DexInstruction22t,
82            16 => DexInstType::DexInstruction22s,
83            17 => DexInstType::DexInstruction22c,
84            18 => DexInstType::DexInstruction22cs,
85            19 => DexInstType::DexInstruction30t,
86            20 => DexInstType::DexInstruction32x,
87            21 => DexInstType::DexInstruction31i,
88            22 => DexInstType::DexInstruction31t,
89            23 => DexInstType::DexInstruction31c,
90            24 => DexInstType::DexInstruction35c,
91            25 => DexInstType::DexInstruction3rc,
92            26 => DexInstType::DexInstruction45cc,
93            27 => DexInstType::DexInstruction4rcc,
94            28 => DexInstType::DexInstruction51l,
95            29 => DexInstType::DexPackedSwitch,
96            30 => DexInstType::DexSparseSwitch,
97            31 => DexInstType::DexFillArrayData,
98            99 => DexInstType::DexNoneOp,
99            _  => DexInstType::DexDalvikIncorrect,
100        };
101
102        let disassembly = unsafe {
103            CStr::from_ptr(ins.disassembly)
104                .to_str()
105                .expect("Error: string is not valid UTF-8")
106                .to_string()
107        };
108
109        DvmInstruction {
110            instruction_type,
111            instruction_length: ins.instruction_length as usize,
112            address: ins.address,
113            op: ins.op,
114            disassembly
115        }
116    }
117
118    /// Return the instruction type
119    pub fn instruction_type(&self) -> DexInstType {
120        self.instruction_type
121    }
122
123    /// Return the instruction length
124    pub fn instruction_length(&self) -> usize {
125        self.instruction_length
126    }
127
128    /// Return the instruction address
129    pub fn address(&self) -> u64 {
130        self.address
131    }
132
133    /// Return the instruction opcode
134    pub fn op(&self) -> u32 {
135        self.op
136    }
137
138    /// Return the instruction string disassembly representation
139    pub fn disassembly(&self) -> &str {
140        &self.disassembly
141    }
142}
143
144/// Type alias for Shuriken's `dvmhandler_data_t`
145///
146/// Structure that keeps information about a handler
147#[derive(Debug, PartialEq)]
148pub struct DvmHandlerData {
149    handler_type: String,
150    handler_start_addr: u64
151}
152
153impl DvmHandlerData {
154    fn from_ptr(ptr: shuriken::dvmhandler_data_t) -> Self {
155        let handler_type = unsafe {
156            CStr::from_ptr(ptr.handler_type)
157                .to_str()
158                .expect("Error: string is not valid UTF-8")
159                .to_string()
160        };
161
162        Self {
163            handler_type,
164            handler_start_addr: ptr.handler_start_addr
165        }
166    }
167
168    /// Return the handler type
169    pub fn handler_type(&self) -> &str {
170        &self.handler_type
171    }
172
173    /// Return the handler start address
174    pub fn handler_start_addr(&self) -> u64 {
175        self.handler_start_addr
176    }
177}
178
179/// Type alias for Shuriken's `dvmexceptions_data_t`
180///
181/// Structure with the information from the exceptions in the code
182#[derive(Debug, PartialEq)]
183pub struct DvmException {
184    try_value_start_addr: u64,
185    try_value_end_addr: u64,
186    n_of_handlers: usize,
187    handlers: Vec<DvmHandlerData>
188}
189
190impl DvmException {
191    fn from_ptr(ptr: shuriken::dvmexceptions_data_t) -> Self {
192        let handlers = unsafe {
193            from_raw_parts(ptr.handler, ptr.n_of_handlers)
194                .iter()
195                .map(|handler| DvmHandlerData::from_ptr(*handler))
196                .collect::<Vec<DvmHandlerData>>()
197        };
198
199        Self {
200            try_value_start_addr: ptr.try_value_start_addr,
201            try_value_end_addr: ptr.try_value_end_addr,
202            n_of_handlers: ptr.n_of_handlers,
203            handlers
204        }
205    }
206
207    /// Return the try value start address
208    pub fn try_value_start_addr(&self) -> u64 {
209        self.try_value_start_addr
210    }
211
212    /// Return the try value end address
213    pub fn try_value_end_addr(&self) -> u64 {
214        self.try_value_end_addr
215    }
216
217    /// Return the number of handlers
218    pub fn n_of_handlers(&self) -> usize {
219        self.n_of_handlers
220    }
221
222    /// Return a reference to the handlers
223    pub fn handlers(&self) -> &[DvmHandlerData] {
224        &self.handlers
225    }
226}
227
228/// Type alias for Shuriken's `dvmdisassembled_method_t`
229///
230/// Structure that represents a disassembled method from the dalvik file
231#[derive(Debug, PartialEq)]
232pub struct DvmDisassembledMethod {
233    // TODO: replace with ref maybe?
234    method_id: DvmMethod,
235    n_of_registers: usize,
236    n_of_exceptions: usize,
237    exception_information: Vec<DvmException>,
238    n_of_instructions: usize,
239    instructions: Vec<DvmInstruction>,
240    method_string: String,
241}
242
243impl DvmDisassembledMethod {
244    pub fn from_dvmdisassembled_method_t(
245        dvm_disas: shuriken::dvmdisassembled_method_t,
246        dvm_method: DvmMethod
247    ) -> Self
248    {
249
250        let method_string = unsafe {
251            CStr::from_ptr(dvm_disas.method_string)
252                .to_str()
253                .expect("Error: string is not valid UTF-8")
254                .to_string()
255        };
256
257        let exception_information = unsafe {
258            from_raw_parts(dvm_disas.exception_information, dvm_disas.n_of_exceptions)
259                .iter()
260                .map(|exc| DvmException::from_ptr(*exc))
261                .collect::<Vec<DvmException>>()
262        };
263
264        let instructions = unsafe {
265            from_raw_parts(dvm_disas.instructions, dvm_disas.n_of_instructions)
266                .iter()
267                .map(|ins| DvmInstruction::from_ins(*ins))
268                .collect::<Vec<DvmInstruction>>()
269        };
270
271        Self {
272            method_id: dvm_method,
273            n_of_registers: dvm_disas.n_of_registers.into(),
274            n_of_exceptions: dvm_disas.n_of_exceptions,
275            exception_information,
276            n_of_instructions: dvm_disas.n_of_instructions,
277            instructions,
278            method_string
279        }
280    }
281
282    /// Create from raw pointer
283    ///
284    /// This is basically a wrapper around [`from_dvmdisassembled_method_t`]
285    ///
286    /// [`from_dvmdisassembled_method_t`]: struct.DvmDisassembledMethod.html#method.from_dvmdisassembled_method_t
287    pub fn from_ptr(ptr: shuriken::dvmdisassembled_method_t) -> Self {
288        // Check if we have a non-null pointer to the `DvmMethod` object
289        if ptr.method_id.is_null() {
290            panic!("DvmMethod pointer is null");
291        }
292
293        let dvm_method = unsafe { DvmMethod::from_ptr(*ptr.method_id) };
294
295        DvmDisassembledMethod::from_dvmdisassembled_method_t(ptr, dvm_method)
296    }
297
298    /// Return a reference to the method id
299    pub fn method_id(&self) -> &DvmMethod {
300        &self.method_id
301    }
302
303    /// Return the number of registers
304    pub fn n_of_registers(&self) -> usize {
305        self.n_of_registers
306    }
307
308    /// Return the number of exceptions
309    pub fn n_of_exceptions(&self) -> usize {
310        self.n_of_exceptions
311    }
312
313    /// Return a reference to the exception information
314    pub fn exception_information(&self) -> &[DvmException] {
315        &self.exception_information
316    }
317
318    /// Return the number of instructions
319    pub fn n_of_instructions(&self) -> usize {
320        self.n_of_instructions
321    }
322
323    /// Return the method string
324    pub fn method_string(&self) -> &str {
325        &self.method_string
326    }
327
328    /// Return the instructions
329    pub fn instructions(&self) -> &[DvmInstruction] {
330        &self.instructions
331    }
332}