From 2ec3fe2c9d516c58334c2fde4b336a54d219aa7f Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Mon, 4 Sep 2023 16:36:15 +0200 Subject: [PATCH] some work on DexType and DexMethodType --- androscalpel/src/apk.rs | 74 ++++++++++++++++++++-- androscalpel/src/field.rs | 60 ++++++++++++------ androscalpel_serializer/src/file_reader.rs | 14 +++- androscalpel_serializer/src/value.rs | 7 ++ 4 files changed, 127 insertions(+), 28 deletions(-) diff --git a/androscalpel/src/apk.rs b/androscalpel/src/apk.rs index 5cf9852..7b96a25 100644 --- a/androscalpel/src/apk.rs +++ b/androscalpel/src/apk.rs @@ -2,9 +2,13 @@ use log::info; use pyo3::prelude::*; -use crate::{Class, DexString, Error, Field, FieldVisibility, Result}; +use crate::*; use androscalpel_serializer::*; +// Prioritize Results from this crate +use crate::Error; +use crate::Result; + /// Represent an apk. #[pyclass] #[derive(Debug, Clone)] @@ -87,8 +91,8 @@ impl Apk { let data_off = class_item.class_data_off; if data_off != 0 { let data = dex.get_struct_at_offset::(data_off)?; - static_fields = self.get_field_list_from_dex(&data.static_fields, dex)?; - instance_fields = self.get_field_list_from_dex(&data.instance_fields, dex)?; + static_fields = Self::get_field_list_from_dex(&data.static_fields, dex)?; + instance_fields = Self::get_field_list_from_dex(&data.instance_fields, dex)?; } if class_item.static_values_off != 0 { @@ -105,7 +109,9 @@ impl Apk { static_fields.len() ))); } - //for (i, value) in values.iter().enumerate() {} + for (i, value) in values.iter().enumerate() { + static_fields[i].value = Some(Self::encoded_value_to_dex_value(value, dex)?); + } for field in static_fields.iter_mut().skip(values.len()) { field.value = None; } @@ -127,8 +133,64 @@ impl Apk { }) } - fn get_field_list_from_dex( - &self, + pub fn encoded_value_to_dex_value( + encoded_value: &EncodedValue, + dex: &DexFileReader, + ) -> Result { + match encoded_value { + EncodedValue::Byte(val) => Ok(DexValue::Byte(DexByte(*val))), + EncodedValue::Short(val) => Ok(DexValue::Short(DexShort(*val))), + EncodedValue::Char(val) => Ok(DexValue::Char(DexChar(*val))), + EncodedValue::Int(val) => Ok(DexValue::Int(DexInt(*val))), + EncodedValue::Long(val) => Ok(DexValue::Long(DexLong(*val))), + EncodedValue::Float(val) => Ok(DexValue::Float(DexFloat(*val))), + EncodedValue::Double(val) => Ok(DexValue::Double(DexDouble(*val))), + EncodedValue::MethodType(val) => { + let proto = dex.get_proto_id(*val as usize)?; + let shorty: DexString = dex.get_string(proto.shorty_idx)?.into(); + let return_type: DexType = DexType( + dex.get_string( + dex.get_type_id(proto.return_type_idx as usize)? + .descriptor_idx, + )? + .into(), + ); + let parameters = if proto.parameters_off == 0 { + vec![] + } else { + let type_list = dex.get_struct_at_offset::(proto.parameters_off)?; + let mut parameters: Vec = vec![]; + for ty in type_list.list { + parameters.push(DexType( + dex.get_string(dex.get_type_id(ty.type_idx as usize)?.descriptor_idx)? + .into(), + )); + } + parameters + }; + Ok(DexValue::MethodType(DexMethodType { + shorty, + return_type, + parameters, + })) + } + EncodedValue::MethodHandle(_val) => todo!(), //Ok(DexValue::MethodHandle(DexMethodHandle(*val))), + EncodedValue::String(val) => Ok(DexValue::String(dex.get_string(*val)?.into())), + EncodedValue::Type(val) => Ok(DexValue::Type(DexType( + dex.get_string(dex.get_type_id(*val as usize)?.descriptor_idx)? + .into(), + ))), + EncodedValue::Field(val) => Ok(DexValue::Field(DexField(*val))), + EncodedValue::Method(val) => Ok(DexValue::Method(DexMethod(*val))), + EncodedValue::Enum(val) => Ok(DexValue::Enum(DexEnum(*val))), + EncodedValue::Array(_val) => todo!(), //Ok(DexValue::Array(DexArray(*val))), + EncodedValue::Annotation(_val) => todo!(), //Ok(DexValue::Annotation(DexAnnotation(*val))), + EncodedValue::Null => Ok(DexValue::Null(DexNull)), + EncodedValue::Boolean(val) => Ok(DexValue::Boolean(DexBoolean(*val))), + } + } + + pub fn get_field_list_from_dex( encoded_fields: &[EncodedField], dex: &DexFileReader, ) -> Result> { diff --git a/androscalpel/src/field.rs b/androscalpel/src/field.rs index b8dd7d6..251757d 100644 --- a/androscalpel/src/field.rs +++ b/androscalpel/src/field.rs @@ -282,19 +282,27 @@ impl DexDouble { } } -/* #[pyclass] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct DexMethodType(pub u32); +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DexMethodType { + /// Type formated as described by + shorty: DexString, + return_type: DexType, + parameters: Vec, +} + #[pymethods] +/// The type of a method. The shorty is formated as described in +/// impl DexMethodType { #[new] - pub fn new(val: u32) -> Self { - Self(val) - } - - pub fn get_value(&self) -> u32 { - self.0 + pub fn new(return_type: DexType, parameters: Vec) -> Self { + // TODO: check format + Self { + shorty: Self::get_shorty(&return_type, ¶meters), + return_type, + parameters, + } } pub fn __str__(&self) -> String { @@ -302,10 +310,16 @@ impl DexMethodType { } pub fn __repr__(&self) -> String { - format!("DexMethodType({})", self.0) + let repr: String = (&self.shorty).into(); + format!("DexMethodType({repr})") + } +} + +impl DexMethodType { + pub fn get_shorty(return_type: &DexType, parameters: &[DexType]) -> DexString { + todo!() } } -*/ /* #[pyclass] @@ -357,26 +371,31 @@ impl DexString { } */ +/// A type. +/// Type a represented by [`DexString`] that follow the TypeDescriptor format +/// as described here #[pyclass] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct DexType(pub u32); +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DexType(pub(crate) DexString); #[pymethods] impl DexType { #[new] - pub fn new(val: u32) -> Self { - Self(val) + pub fn _new(ty: DexString) -> Self { + // TODO: check format + Self(ty) } - pub fn get_value(&self) -> u32 { + pub fn get_name(&self) -> DexString { self.0 } pub fn __str__(&self) -> String { - self.__repr__() + self.0.into() } pub fn __repr__(&self) -> String { - format!("DexType({})", self.0) + let name: String = self.0.into(); + format!("DexType({name})") } } @@ -546,7 +565,7 @@ pub enum DexValue { Long(DexLong), Float(DexFloat), Double(DexDouble), - //MethodType(DexMethodType), + MethodType(DexMethodType), //MethodHandle(DexMethodHandle), String(DexString), Type(DexType), @@ -593,7 +612,8 @@ impl<'source> FromPyObject<'source> for DexValue { Ok(Self::Null(val)) } else if let Ok(val) = DexBoolean::extract(ob) { Ok(Self::Boolean(val)) - // } else if let Ok(val) = DexMethodType::extract(ob) { Ok(Self::MethodType(val)) + } else if let Ok(val) = DexMethodType::extract(ob) { + Ok(Self::MethodType(val)) // } else if let Ok(val) = DexMethodHandle::extract(ob) { Ok(Self::MethodHandle(val)) } else { Err(PyErr::new::(format!( diff --git a/androscalpel_serializer/src/file_reader.rs b/androscalpel_serializer/src/file_reader.rs index d16e0db..548aac2 100644 --- a/androscalpel_serializer/src/file_reader.rs +++ b/androscalpel_serializer/src/file_reader.rs @@ -143,7 +143,7 @@ impl<'a> DexFileReader<'a> { }) } - /// Return a [`&TypeIdItem`] from its idx. + /// Return a [`TypeIdItem`] reference from its idx. pub fn get_type_id(&self, idx: usize) -> Result<&TypeIdItem> { self.type_ids .get(idx) @@ -154,7 +154,7 @@ impl<'a> DexFileReader<'a> { ))) } - /// Return a [`&FieldIdItem`] from its idx. + /// Return a [`FieldIdItem`] reference from its idx. pub fn get_field_id(&self, idx: usize) -> Result<&FieldIdItem> { self.field_ids .get(idx) @@ -164,6 +164,16 @@ impl<'a> DexFileReader<'a> { ))) } + /// Return a [`ProtoIdItem`] reference from its idx. + pub fn get_proto_id(&self, idx: usize) -> Result<&ProtoIdItem> { + self.proto_ids + .get(idx) + .ok_or(Error::InconsistantStruct(format!( + "prototype idx {idx} is out of bound (|proto_ids|={})", + self.proto_ids.len() + ))) + } + fn sanity_check(&self) -> Result<()> { if self.header.magic.version != [0x30, 0x33, 0x39] { warn!( diff --git a/androscalpel_serializer/src/value.rs b/androscalpel_serializer/src/value.rs index 2fc09fb..2119759 100644 --- a/androscalpel_serializer/src/value.rs +++ b/androscalpel_serializer/src/value.rs @@ -14,12 +14,19 @@ pub enum EncodedValue { Long(i64), Float(f32), Double(f64), + /// Index of a [`crate::ProtoIdItem`] in `proto_ids` MethodType(u32), + /// Index of a [`crate::MethodHandleItem`] in `method_handles` MethodHandle(u32), + /// Index of a [`crate::StringIdItem`] in `string_ids` String(u32), + /// Index of a [`crate::TypeIdItem`] in `type_ids` Type(u32), + /// Index of a [`crate::FieldIdItem`] in `field_ids` Field(u32), + /// Index of a [`crate::MethodIdItem`] in `method_ids` Method(u32), + /// Index of a [`crate::FieldIdItem`] in `field_ids` representing an enum. Enum(u32), Array(EncodedArray), Annotation(EncodedAnnotation),