From 9ed99594cc08626318c6bff2feae863bbfe8d9c6 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Fri, 25 Aug 2023 19:05:05 +0200 Subject: [PATCH] add item related to class --- androscalpel_serializer/src/items/class.rs | 252 ++++++++++++++++++++ androscalpel_serializer/src/items/header.rs | 3 +- androscalpel_serializer/src/items/map.rs | 2 +- androscalpel_serializer/src/items/mod.rs | 42 +++- 4 files changed, 293 insertions(+), 6 deletions(-) create mode 100644 androscalpel_serializer/src/items/class.rs diff --git a/androscalpel_serializer/src/items/class.rs b/androscalpel_serializer/src/items/class.rs new file mode 100644 index 0000000..79e24a1 --- /dev/null +++ b/androscalpel_serializer/src/items/class.rs @@ -0,0 +1,252 @@ +//! Class definitions. + +use crate as androscalpel_serializer; +use crate::{ReadSeek, Result, Serializable, Uleb128}; +use std::io::Write; + +/// https://source.android.com/docs/core/runtime/dex-format#class-def-item +/// alignment: 4 bytes +#[derive(Serializable, Debug, Clone, Copy, PartialEq, Eq)] +pub struct ClassDefItem { + /// Index of a [`TypeIdItem`] in `type_ids`, must be a class. + pub class_idx: u32, + /// See [access-flags](https://source.android.com/docs/core/runtime/dex-format#access-flags) + pub access_flags: u32, + /// Either index of a [`TypeIdItem`] in `type_ids`, must be a class or NO_INDEX + pub superclass_idx: u32, + /// 0 if no interfaces, else offset to a [`TypeList`]. + pub interfaces_off: u32, + /// Index of a [`StringIdItem`] in `string_ids` or NO_INDEX. + pub source_file_idx: u32, + /// 0 if no annotation, else offset to a [`AnnotationDirectoryItem`]. + pub annotations_off: u32, + /// 0 if no data for this class, else offset to a [`ClassDataItem`]. + pub class_data_off: u32, + /// 0 if no static fields, else offset to an [`EncodedArrayItem`]. + /// + /// Notes: + /// + /// > The size of the array must be no larger than the number of static + /// > fields declared by this class, and the elements correspond to the + /// > static fields in the same order as declared in the corresponding + /// > field_list. The type of each array element must match the declared + /// > type of its corresponding field. If there are fewer elements in the + /// > array than there are static fields, then the leftover fields are + /// > initialized with a type-appropriate 0 or null. + pub static_values_off: u32, +} + +/// https://source.android.com/docs/core/runtime/dex-format#method-handle-item +/// alignment: 4 bytes +#[derive(Serializable, Debug, Clone, Copy, PartialEq, Eq)] +pub struct MethodHandleItem { + pub method_handle_type: MethodHandleType, + pub unused1: u16, + pub field_or_method_id: u16, + pub unused2: u16, +} + +/// https://source.android.com/docs/core/runtime/dex-format#method-handle-type-codes +#[derive(Serializable, Debug, Clone, Copy, PartialEq, Eq)] +#[prefix_type(u16)] +pub enum MethodHandleType { + #[prefix(0x00)] + StaticPut, + #[prefix(0x01)] + StaticGet, + #[prefix(0x02)] + InstancePut, + #[prefix(0x03)] + InstanceGet, + #[prefix(0x04)] + InvokeStatic, + #[prefix(0x05)] + InvokeInstance, + #[prefix(0x06)] + InvokeConstructor, + #[prefix(0x07)] + InvokeDirect, + #[prefix(0x08)] + InvokeInterface, +} + +/// https://source.android.com/docs/core/runtime/dex-format#class-data-item +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ClassDataItem { + // pub static_fields_size: Uleb128, + // pub instance_fields_size: Uleb128, + // pub direct_methods_size: Uleb128, + // pub virtual_methods_size: Uleb128, + pub static_fields: Vec, + pub instance_fields: Vec, + pub direct_methods: Vec, + pub virtual_methods: Vec, +} + +impl ClassDataItem { + pub fn static_fields_size_field(&self) -> Uleb128 { + Uleb128(self.static_fields.len() as u32) + } + pub fn instance_fields_size_field(&self) -> Uleb128 { + Uleb128(self.instance_fields.len() as u32) + } + pub fn direct_methods_size_field(&self) -> Uleb128 { + Uleb128(self.direct_methods.len() as u32) + } + pub fn virtual_methods_size_field(&self) -> Uleb128 { + Uleb128(self.virtual_methods.len() as u32) + } +} + +impl Serializable for ClassDataItem { + fn serialize(&self, output: &mut dyn Write) -> Result<()> { + self.static_fields_size_field().serialize(output)?; + self.instance_fields_size_field().serialize(output)?; + self.direct_methods_size_field().serialize(output)?; + self.virtual_methods_size_field().serialize(output)?; + for field in &self.static_fields { + field.serialize(output)?; + } + for field in &self.instance_fields { + field.serialize(output)?; + } + for method in &self.direct_methods { + method.serialize(output)?; + } + for method in &self.virtual_methods { + method.serialize(output)?; + } + Ok(()) + } + + fn deserialize(input: &mut dyn ReadSeek) -> Result { + let Uleb128(static_fields_size) = Uleb128::deserialize(input)?; + let Uleb128(instance_fields_size) = Uleb128::deserialize(input)?; + let Uleb128(direct_methods_size) = Uleb128::deserialize(input)?; + let Uleb128(virtual_methods_size) = Uleb128::deserialize(input)?; + let mut static_fields = vec![]; + let mut instance_fields = vec![]; + let mut direct_methods = vec![]; + let mut virtual_methods = vec![]; + for _ in 0..static_fields_size { + static_fields.push(EncodedField::deserialize(input)?); + } + for _ in 0..instance_fields_size { + instance_fields.push(EncodedField::deserialize(input)?); + } + for _ in 0..direct_methods_size { + direct_methods.push(EncodedMethod::deserialize(input)?); + } + for _ in 0..virtual_methods_size { + virtual_methods.push(EncodedMethod::deserialize(input)?); + } + Ok(Self { + // static_fields_size: Uleb128(static_fields_size), + // instance_fields_size: Uleb128(instance_fields_size), + // direct_methods_size: Uleb128(direct_methods_size), + // virtual_methods_size: Uleb128(virtual_methods_size), + static_fields, + instance_fields, + direct_methods, + virtual_methods, + }) + } + + fn size(&self) -> usize { + self.static_fields_size_field().size() + + self.instance_fields_size_field().size() + + self.direct_methods_size_field().size() + + self.virtual_methods_size_field().size() + + self + .static_fields + .iter() + .map(|val| val.size()) + .sum::() + + self + .instance_fields + .iter() + .map(|val| val.size()) + .sum::() + + self + .direct_methods + .iter() + .map(|val| val.size()) + .sum::() + + self + .virtual_methods + .iter() + .map(|val| val.size()) + .sum::() + } +} + +/// https://source.android.com/docs/core/runtime/dex-format#encoded-field-format +#[derive(Serializable, Debug, Clone, Copy, PartialEq, Eq)] +pub struct EncodedField { + /// Index into the `field_ids` list for the identity of this field + /// (includes the name and descriptor), represented as a difference + /// from the index of previous element in the list. The index of the + /// first element in a list is represented directly. + pub field_idx_diff: Uleb128, + pub access_flags: Uleb128, +} + +/// https://source.android.com/docs/core/runtime/dex-format#encoded-method +#[derive(Serializable, Debug, Clone, Copy, PartialEq, Eq)] +pub struct EncodedMethod { + /// Index into the `method_ids list` for the identity of this method + /// (includes the name and descriptor), represented as a difference + /// from the index of previous element in the list. The index of the + /// first element in a list is represented directly. + pub method_idx_diff: Uleb128, + pub access_flags: Uleb128, + /// 0 if abstract or native, else offset to a [`CodeItem`]. + pub code_off: Uleb128, +} + +/// https://source.android.com/docs/core/runtime/dex-format#type-list +/// alignment: 4 bytes +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TypeList { + // pub size: u32, + pub list: Vec, +} + +impl TypeList { + fn size_field(&self) -> u32 { + self.list.len() as u32 + } +} + +impl Serializable for TypeList { + fn serialize(&self, output: &mut dyn Write) -> Result<()> { + self.size_field().serialize(output)?; + for item in &self.list { + item.serialize(output)?; + } + Ok(()) + } + + fn deserialize(input: &mut dyn ReadSeek) -> Result { + let size = i32::deserialize(input)?; + let mut list = vec![]; + for _ in 0..size { + list.push(TypeItem::deserialize(input)?); + } + Ok(Self { + // size, + list, + }) + } + + fn size(&self) -> usize { + self.size_field().size() + self.list.iter().map(|val| val.size()).sum::() + } +} + +/// https://source.android.com/docs/core/runtime/dex-format#type-item-format +#[derive(Serializable, Debug, Clone, Copy, PartialEq, Eq)] +pub struct TypeItem { + /// Index into the `type_ids` list. + pub type_idx: u16, +} diff --git a/androscalpel_serializer/src/items/header.rs b/androscalpel_serializer/src/items/header.rs index cde287e..9b31915 100644 --- a/androscalpel_serializer/src/items/header.rs +++ b/androscalpel_serializer/src/items/header.rs @@ -1,6 +1,7 @@ //! The header item: https://source.android.com/docs/core/runtime/dex-format#header-item -use crate::{DexFileMagic, EndianConstant}; +use crate as androscalpel_serializer; +use crate::{DexFileMagic, EndianConstant, Serializable}; /// The header item: https://source.android.com/docs/core/runtime/dex-format#header-item /// diff --git a/androscalpel_serializer/src/items/map.rs b/androscalpel_serializer/src/items/map.rs index 5fbf34d..c119311 100644 --- a/androscalpel_serializer/src/items/map.rs +++ b/androscalpel_serializer/src/items/map.rs @@ -79,7 +79,7 @@ impl MapList { impl Serializable for MapList { fn serialize(&self, output: &mut dyn Write) -> Result<()> { self.size_field().serialize(output)?; - for map_item in self.list { + for map_item in &self.list { map_item.serialize(output)?; } Ok(()) diff --git a/androscalpel_serializer/src/items/mod.rs b/androscalpel_serializer/src/items/mod.rs index 41ee1da..d8c8c19 100644 --- a/androscalpel_serializer/src/items/mod.rs +++ b/androscalpel_serializer/src/items/mod.rs @@ -1,11 +1,13 @@ //! The items structures. use crate as androscalpel_serializer; -use crate::Serializable; +use crate::{EncodedArray, Serializable}; +pub mod class; pub mod header; pub mod map; +pub use class::*; pub use header::*; pub use map::*; @@ -34,7 +36,7 @@ pub struct ProtoIdItem { pub shorty_idx: u32, /// Index of a [`TypeIdItem`] in `type_ids`. pub return_type_idx: u32, - /// 0 if no parameter, else offset to a [`TypeList`. + /// 0 if no parameter, else offset to a [`TypeList`]. pub parameters_off: u32, } @@ -42,7 +44,7 @@ pub struct ProtoIdItem { /// alignment: 4 bytes #[derive(Serializable, Debug, Clone, Copy, PartialEq, Eq)] pub struct FieldIdItem { - /// Index of a [`TypeIdItem`] in `type_ids`], must be a class. + /// Index of a [`TypeIdItem`] in `type_ids`, must be a class. pub class_idx: u16, /// Index of a [`TypeIdItem`] in `type_ids`. pub type_idx: u16, @@ -54,10 +56,42 @@ pub struct FieldIdItem { /// alignment: 4 bytes #[derive(Serializable, Debug, Clone, Copy, PartialEq, Eq)] pub struct MethodIdItem { - /// Index of a [`TypeIdItem`] in `type_ids`], must be a class. + /// Index of a [`TypeIdItem`] in `type_ids`, must be a class. pub class_idx: u16, /// Index of a [`ProtoIdItem`] in `proto_ids`. pub proto_idx: u16, /// Index of a [`StringIdItem`] in [`HeaderItem.string_ids`]. pub name_idx: u32, } + +/// https://source.android.com/docs/core/runtime/dex-format#call-site-id-item +/// alignment: 4 bytes +#[derive(Serializable, Debug, Clone, Copy, PartialEq, Eq)] +pub struct CallSiteIdItem { + /// Offset to a [`CallSiteItem`]. + pub call_site_off: u32, +} + +/// https://source.android.com/docs/core/runtime/dex-format#encoded-array-item +/// alignment: none +#[derive(Serializable, Debug, Clone, PartialEq, Eq)] +pub struct EncodedArrayItem { + pub value: EncodedArray, +} + +/// https://source.android.com/docs/core/runtime/dex-format#call-site-item +/// +/// The call_site_item is an encoded_array_item whose elements correspond to +/// the arguments provided to a bootstrap linker method. +/// +/// The first three arguments are: +/// - A method handle representing the bootstrap linker method ([`EncodedValue::MethodHandle`]) +/// - A method name that the bootstrap linker should resolve ([`EncodedValue::String`]) +/// - A method type corresponding to the type of the method name to be resolved +/// ([`EncodedValue::MethodType`]) +/// +/// Any additional arguments are constant values passed to the bootstrap linker method. +/// These arguments are passed in order and without any type conversions. +/// +/// +pub type CallSiteItem = EncodedArrayItem;