1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
//! Dex `Method` and supporting structures
use getset::{CopyGetters, Getters};
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use scroll::ctx;
use scroll::Pread;
use scroll::Uleb128;

use crate::cache::Ref;
use crate::code::CodeItem;
use crate::encoded_item::EncodedItem;
use crate::encoded_item::EncodedItemArray;
use crate::error::Error;
use crate::field::FieldId;
use crate::jtype::Type;
use crate::jtype::TypeId;
use crate::string::JString;
use crate::string::StringId;
use crate::uint;
use crate::ulong;
use crate::ushort;
use crate::utils;

bitflags! {
    /// Access flags of a `Dex` Method
    pub struct AccessFlags: ulong {
        const PUBLIC = 0x1;
        const PRIVATE = 0x2;
        const PROTECTED = 0x4;
        const STATIC = 0x8;
        const FINAL = 0x10;
        const SYNCHRONIZED = 0x20;
        const BRIDGE = 0x40;
        const VARARGS = 0x80;
        const NATIVE = 0x100;
        const ABSTRACT = 0x400;
        const STRICT = 0x800;
        const SYNTHETIC = 0x1000;
        const CONSTRUCTOR = 0x10000;
        const DECLARED_SYNCHRONIZED = 0x20000;
    }
}

/// Represents a `Class` method.
#[derive(Debug, Getters, CopyGetters)]
pub struct Method {
    /// Parent class of the method.
    #[get = "pub"]
    class: Type,
    /// Name of the method.
    #[get = "pub"]
    name: Ref<JString>,
    /// Access flags of the method.
    #[get_copy = "pub"]
    access_flags: AccessFlags,
    /// Types of the parameters of the method.
    #[get = "pub"]
    params: Option<Vec<Type>>,
    /// Shorty descriptor of the method. Conforms to
    /// https://source.android.com/devices/tech/dalvik/dex-format#shortydescriptor
    #[get = "pub"]
    shorty: Ref<JString>,
    /// Return type of the method.
    #[get = "pub"]
    return_type: Type,
    /// Code and DebugInfo of the method.
    #[get = "pub"]
    code: Option<CodeItem>,
}

/// Index into the `ProtoId`s list.
pub type ProtoId = ulong;

/// Method Prototypes.
/// [Android docs](https://source.android.com/devices/tech/dalvik/dex-format#proto-id-item)
#[derive(Pread, Debug, CopyGetters)]
#[get_copy = "pub"]
pub struct ProtoIdItem {
    /// Index into the string_ids list for the short-form descriptor string of this prototype
    shorty: StringId,
    /// Index into the type_ids list for the return type of this prototype.
    return_type: TypeId,
    /// Offset from the start of the file to the list of parameter types for this prototype, or `0`
    /// if this prototype has no params. The data at the location should be a list of types.
    params_off: uint,
}

impl ProtoIdItem {
    pub(crate) fn try_from_dex<S: AsRef<[u8]>>(
        dex: &super::Dex<S>,
        offset: ulong,
    ) -> super::Result<Self> {
        let source = dex.source.as_ref();
        Ok(source.pread_with(offset as usize, dex.get_endian())?)
    }
}

impl Method {
    pub(crate) fn try_from_dex<S: AsRef<[u8]>>(
        dex: &super::Dex<S>,
        encoded_method: &EncodedMethod,
    ) -> super::Result<Method> {
        debug!(target: "method", "encoded method: {:?}", encoded_method);
        let source = &dex.source;
        let method_item = dex.get_method_item(encoded_method.method_id)?;
        let name = dex.get_string(method_item.name_idx)?;
        debug!(target: "method", "name: {}, method id item: {:?}", name, method_item);
        let proto_item = dex.get_proto_item(ulong::from(method_item.proto_idx))?;
        debug!(target: "method", "method proto_item: {:?}", proto_item);
        let shorty = dex.get_string(proto_item.shorty)?;
        let return_type = dex.get_type(proto_item.return_type)?;
        let params = if proto_item.params_off != 0 {
            let offset = &mut (proto_item.params_off as usize);
            let endian = dex.get_endian();
            let len = source.gread_with::<uint>(offset, endian)?;
            let type_ids: Vec<ushort> = try_gread_vec_with!(source, offset, len, endian);
            Some(utils::get_types(dex, &type_ids)?)
        } else {
            None
        };
        debug!(target: "method", "code item offset: {}", encoded_method.code_offset);
        let code = dex.get_code_item(encoded_method.code_offset)?;
        Ok(Self {
            name,
            class: dex.get_type(uint::from(method_item.class_idx))?,
            access_flags: AccessFlags::from_bits(encoded_method.access_flags).ok_or_else(|| {
                Error::InvalidId(format!(
                    "Invalid access flags for method {}",
                    method_item.name_idx
                ))
            })?,
            shorty,
            return_type,
            params,
            code,
        })
    }
}

/// Method identifier.
/// [Android docs](https://source.android.com/devices/tech/dalvik/dex-format#method-id-item)
#[derive(Pread, Debug, CopyGetters)]
#[get_copy = "pub"]
pub struct MethodIdItem {
    /// Index into the `TypeId`s list for the definer of this method.
    class_idx: ushort,
    /// Index into the `ProtoId`s list for the prototype of this method.
    proto_idx: ushort,
    /// Index into the `StringId`s list for the name of this method.
    name_idx: StringId,
}

impl MethodIdItem {
    pub(crate) fn try_from_dex<S: AsRef<[u8]>>(
        dex: &super::Dex<S>,
        offset: ulong,
    ) -> super::Result<Self> {
        let source = &dex.source;
        Ok(source.pread_with(offset as usize, dex.get_endian())?)
    }
}

/// Index into the `MethodId`s list.
pub type MethodId = ulong;

/// Contains a `MethodId` along with its access flags and code.
/// [Android docs](https://source.android.com/devices/tech/dalvik/dex-format#encoded-method)
#[derive(Debug, Getters, CopyGetters)]
pub struct EncodedMethod {
    /// Index into the `MethodId`s list for the identity of this method represented as
    /// a difference from the index of previous element in the list.
    #[get_copy = "pub(crate)"]
    pub(crate) method_id: MethodId,
    /// Access flags for this method.
    #[get = "pub"]
    access_flags: ulong,
    /// Offset from the start of the file to the code structure for this method, or `0` if this
    /// method is either abstract or native.  The format of the data is specified by `CodeItem`.
    #[get = "pub"]
    code_offset: ulong,
}

impl EncodedItem for EncodedMethod {
    fn id(&self) -> ulong {
        self.method_id
    }
}

/// List of `EncodedMethod`s
pub(crate) type EncodedMethodArray = EncodedItemArray<EncodedMethod>;

impl<'a> ctx::TryFromCtx<'a, ulong> for EncodedMethod {
    type Error = Error;
    type Size = usize;

    fn try_from_ctx(source: &'a [u8], prev_id: ulong) -> super::Result<(Self, Self::Size)> {
        let offset = &mut 0;
        let id = Uleb128::read(source, offset)?;
        let access_flags = Uleb128::read(source, offset)?;
        let code_offset = Uleb128::read(source, offset)?;
        Ok((
            Self {
                method_id: prev_id + id,
                code_offset,
                access_flags,
            },
            *offset,
        ))
    }
}

/// Type of the method handle.
/// [Android docs](https://source.android.com/devices/tech/dalvik/dex-format#method-handle-type-codes)
#[derive(FromPrimitive, Debug, Clone, Copy)]
pub enum MethodHandleType {
    StaticPut = 0x00,
    StaticGet = 0x01,
    InstancePut = 0x02,
    InstanceGet = 0x03,
    InvokeStatic = 0x04,
    InvokeInstance = 0x05,
    InvokeConstructor = 0x06,
    InvokeDirect = 0x07,
    InvokeInterface = 0x08,
}

#[derive(Debug, Clone, Copy)]
pub enum FieldOrMethodId {
    Field(FieldId),
    Method(MethodId),
}

/// A method handle.
/// [Android docs](https://source.android.com/devices/tech/dalvik/dex-format#method-handle-item)
#[derive(Debug, CopyGetters)]
#[get_copy = "pub"]
pub struct MethodHandleItem {
    ///  The type of this MethodHandleItem.
    handle_type: MethodHandleType,
    /// `FieldId` or `MethodId`  depending on whether the method handle type is an accessor or
    /// a method invoker
    id: FieldOrMethodId,
}

impl<'a, S: AsRef<[u8]>> ctx::TryFromCtx<'a, &super::Dex<S>> for MethodHandleItem {
    type Error = Error;
    type Size = usize;

    fn try_from_ctx(source: &'a [u8], dex: &super::Dex<S>) -> super::Result<(Self, Self::Size)> {
        let endian = dex.get_endian();
        let offset = &mut 0;
        let handle_type: ushort = source.gread_with(offset, endian)?;
        let handle_type = MethodHandleType::from_u16(handle_type)
            .ok_or_else(|| Error::InvalidId(format!("Invalid handle type {}", handle_type)))?;
        let _: ushort = source.gread_with(offset, endian)?;
        let id: ushort = source.gread_with(offset, endian)?;
        let _: ushort = source.gread_with(offset, endian)?;
        let id = match handle_type {
            MethodHandleType::StaticPut
            | MethodHandleType::StaticGet
            | MethodHandleType::InstancePut
            | MethodHandleType::InstanceGet => FieldOrMethodId::Field(FieldId::from(id)),
            _ => FieldOrMethodId::Method(MethodId::from(id)),
        };

        Ok((Self { handle_type, id }, *offset))
    }
}