finish implementing IdMethodType

This commit is contained in:
Jean-Marie Mineau 2023-10-02 15:08:45 +02:00
parent b2c4da413c
commit cdf68c506a
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
3 changed files with 105 additions and 31 deletions

View file

@ -148,7 +148,7 @@ impl Apk {
EncodedValue::MethodType(val) => { EncodedValue::MethodType(val) => {
let proto = dex.get_proto_id(*val as usize)?; let proto = dex.get_proto_id(*val as usize)?;
let shorty: DexString = dex.get_string(proto.shorty_idx)?.into(); let shorty: DexString = dex.get_string(proto.shorty_idx)?.into();
let return_type: DexType = DexType( let return_type: IdType = IdType(
dex.get_string( dex.get_string(
dex.get_type_id(proto.return_type_idx as usize)? dex.get_type_id(proto.return_type_idx as usize)?
.descriptor_idx, .descriptor_idx,
@ -159,16 +159,16 @@ impl Apk {
vec![] vec![]
} else { } else {
let type_list = dex.get_struct_at_offset::<TypeList>(proto.parameters_off)?; let type_list = dex.get_struct_at_offset::<TypeList>(proto.parameters_off)?;
let mut parameters: Vec<DexType> = vec![]; let mut parameters: Vec<IdType> = vec![];
for ty in type_list.list { for ty in type_list.list {
parameters.push(DexType( parameters.push(IdType(
dex.get_string(dex.get_type_id(ty.type_idx as usize)?.descriptor_idx)? dex.get_string(dex.get_type_id(ty.type_idx as usize)?.descriptor_idx)?
.into(), .into(),
)); ));
} }
parameters parameters
}; };
Ok(DexValue::MethodType(DexMethodType { Ok(DexValue::MethodType(IdMethodType {
shorty, shorty,
return_type, return_type,
parameters, parameters,
@ -177,29 +177,29 @@ impl Apk {
// TODO: need method to be implemented first // TODO: need method to be implemented first
EncodedValue::MethodHandle(_val) => todo!(), //Ok(DexValue::MethodHandle(DexMethodHandle(*val))), EncodedValue::MethodHandle(_val) => todo!(), //Ok(DexValue::MethodHandle(DexMethodHandle(*val))),
EncodedValue::String(val) => Ok(DexValue::String(dex.get_string(*val)?.into())), EncodedValue::String(val) => Ok(DexValue::String(dex.get_string(*val)?.into())),
EncodedValue::Type(val) => Ok(DexValue::Type(DexType( EncodedValue::Type(val) => Ok(DexValue::Type(IdType(
dex.get_string(dex.get_type_id(*val as usize)?.descriptor_idx)? dex.get_string(dex.get_type_id(*val as usize)?.descriptor_idx)?
.into(), .into(),
))), ))),
EncodedValue::Field(val) => { EncodedValue::Field(val) => {
let field = dex.get_field_id(*val as usize)?; let field = dex.get_field_id(*val as usize)?;
let name = dex.get_string(field.name_idx)?.into(); let name = dex.get_string(field.name_idx)?.into();
let type_ = DexType( let type_ = IdType(
dex.get_string(dex.get_type_id(field.type_idx as usize)?.descriptor_idx)? dex.get_string(dex.get_type_id(field.type_idx as usize)?.descriptor_idx)?
.into(), .into(),
); );
let class_ = DexType( let class_ = IdType(
dex.get_string(dex.get_type_id(field.class_idx as usize)?.descriptor_idx)? dex.get_string(dex.get_type_id(field.class_idx as usize)?.descriptor_idx)?
.into(), .into(),
); );
Ok(DexValue::Field(DexField { Ok(DexValue::Field(IdField {
name, name,
type_, type_,
class_, class_,
})) }))
} }
EncodedValue::Method(val) => Ok(DexValue::Method(DexMethod(*val))), EncodedValue::Method(val) => Ok(DexValue::Method(IdMethod(*val))),
EncodedValue::Enum(val) => Ok(DexValue::Enum(DexEnum(*val))), EncodedValue::Enum(val) => Ok(DexValue::Enum(IdEnum(*val))),
EncodedValue::Array(_val) => todo!(), //Ok(DexValue::Array(DexArray(*val))), EncodedValue::Array(_val) => todo!(), //Ok(DexValue::Array(DexArray(*val))),
EncodedValue::Annotation(_val) => todo!(), //Ok(DexValue::Annotation(DexAnnotation(*val))), EncodedValue::Annotation(_val) => todo!(), //Ok(DexValue::Annotation(DexAnnotation(*val))),
EncodedValue::Null => Ok(DexValue::Null(DexNull)), EncodedValue::Null => Ok(DexValue::Null(DexNull)),
@ -246,10 +246,10 @@ impl Apk {
))); )));
} }
}; };
let descriptor = DexField { let descriptor = IdField {
name, name,
type_: DexType(ty), type_: IdType(ty),
class_: DexType(class), class_: IdType(class),
}; };
fields.push(Field { fields.push(Field {
descriptor, descriptor,

View file

@ -2,14 +2,15 @@
use pyo3::prelude::*; use pyo3::prelude::*;
use crate::DexString; use crate::{DexString, Error, Result};
use androscalpel_serializer::{StringDataItem, Uleb128}; use androscalpel_serializer::{StringDataItem, Uleb128};
#[pyclass] #[pyclass]
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct IdMethodType { pub struct IdMethodType {
/// Type formated as described by <https://source.android.com/docs/core/runtime/dex-format#shortydescriptor> /// Type formated as described by <https://source.android.com/docs/core/runtime/dex-format#shortydescriptor>
pub(crate) shorty: DexString, pub(crate) shorty: DexString, // Redondant, but same as in the encoding, keep it in cas we ever
// need it
pub(crate) return_type: IdType, pub(crate) return_type: IdType,
pub(crate) parameters: Vec<IdType>, pub(crate) parameters: Vec<IdType>,
} }
@ -20,7 +21,6 @@ pub struct IdMethodType {
impl IdMethodType { impl IdMethodType {
#[new] #[new]
pub fn new(return_type: IdType, parameters: Vec<IdType>) -> Self { pub fn new(return_type: IdType, parameters: Vec<IdType>) -> Self {
// TODO: check format
Self { Self {
shorty: Self::get_shorty(&return_type, &parameters), shorty: Self::get_shorty(&return_type, &parameters),
return_type, return_type,
@ -29,12 +29,19 @@ impl IdMethodType {
} }
pub fn __str__(&self) -> String { pub fn __str__(&self) -> String {
self.__repr__() format!(
"({}){}",
self.parameters
.iter()
.map(|param| param.__str__())
.collect::<Vec<String>>()
.join(" "),
self.return_type.__str__()
)
} }
pub fn __repr__(&self) -> String { pub fn __repr__(&self) -> String {
let repr: String = (&self.shorty).into(); format!("DexMethodType({})", self.__str__())
format!("DexMethodType({repr})")
} }
} }
@ -42,7 +49,12 @@ impl IdMethodType {
/// Compute the format for the shorty as described in /// Compute the format for the shorty as described in
/// <https://source.android.com/docs/core/runtime/dex-format#shortydescriptor> /// <https://source.android.com/docs/core/runtime/dex-format#shortydescriptor>
pub fn get_shorty(return_type: &IdType, parameters: &[IdType]) -> DexString { pub fn get_shorty(return_type: &IdType, parameters: &[IdType]) -> DexString {
todo!() let mut shorty: String = return_type.get_shorty().into();
for ty in parameters {
let ty: String = ty.get_shorty().into();
shorty.push_str(&ty);
}
shorty.into()
} }
} }
@ -74,14 +86,24 @@ impl DexMethodHandle {
/// A type. /// A type.
/// Type represented by [`DexString`] that follow the TypeDescriptor format /// Type represented by [`DexString`] that follow the TypeDescriptor format
/// as described here <https://source.android.com/docs/core/runtime/dex-format#typedescriptor> /// as described here <https://source.android.com/docs/core/runtime/dex-format#typedescriptor>
// Not a clean rust enum because we want to be compatible with python, and maybe support strange
// malware edge case?
#[pyclass] #[pyclass]
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct IdType(pub(crate) DexString); pub struct IdType(pub(crate) DexString);
#[pymethods] #[pymethods]
impl IdType { impl IdType {
#[new] #[new]
pub fn _new(ty: DexString) -> Self { pub fn _new(ty: DexString) -> Result<Self> {
// TODO: check format // TODO: check format
let ty = Self(ty);
ty.check_format()?;
Ok(ty)
}
/// Return a type from its string representation without checking its format
#[staticmethod]
pub fn unchecked_new(ty: DexString) -> Self {
Self(ty) Self(ty)
} }
@ -213,28 +235,28 @@ impl IdType {
/// Check if the type is a class /// Check if the type is a class
pub fn is_class(&self) -> bool { pub fn is_class(&self) -> bool {
self.0.get_utf16_size() == 0 self.0.get_utf16_size() >= 2
&& self.0.get_bytes().is_empty() && !self.0.get_bytes().len() > 2
&& self.0.get_bytes()[0] != 0x76 && self.0.get_bytes()[0] == b'L'
// Check if first char is an L && *self.0.get_bytes().last().unwrap() == b';'
} }
/// Check if the type is an array /// Check if the type is an array
pub fn is_array(&self) -> bool { pub fn is_array(&self) -> bool {
self.0.get_utf16_size() == 0 self.0.get_utf16_size() != 0
&& self.0.get_bytes().is_empty() && !self.0.get_bytes().is_empty()
&& self.0.get_bytes()[0] != 0x5b && self.0.get_bytes()[0] == b'['
// Check if first char is an [
} }
/// If the type is a class, return the name of the class, /// If the type is a class, return the name of the class,
/// else None. /// else None.
pub fn get_class_name(&self) -> Option<DexString> { pub fn get_class_name(&self) -> Option<DexString> {
if self.is_class() { if self.is_class() {
let bytes = self.0.get_bytes();
Some( Some(
StringDataItem { StringDataItem {
utf16_size: Uleb128(self.0.get_utf16_size() - 1), utf16_size: Uleb128(self.0.get_utf16_size() - 2),
data: self.0.get_bytes()[1..].to_vec(), data: bytes[1..bytes.len() - 1].to_vec(),
} }
.into(), .into(),
) )
@ -259,6 +281,51 @@ impl IdType {
} }
} }
/// Check if the type is a value representation.
/// They should not be any needs to use this method externaly?
pub fn check_format(&self) -> Result<()> {
if self.is_void()
|| self.is_boolean()
|| self.is_byte()
|| self.is_short()
|| self.is_char()
|| self.is_int()
|| self.is_long()
|| self.is_float()
|| self.is_double()
|| self.is_class()
|| self
.get_element_type()
.map(|elt| elt.check_format().is_ok())
.unwrap_or(false)
{
Ok(())
} else {
let format: String = (&self.0).into();
Err(Error::InconsistantStruct(format!(
"{format} is not a valid type"
)))
}
}
/// Return the shorty repr of this type (ID: the type if it's a scalar, 'L' else)
pub fn get_shorty(&self) -> DexString {
if self.is_void()
|| self.is_boolean()
|| self.is_byte()
|| self.is_short()
|| self.is_char()
|| self.is_int()
|| self.is_long()
|| self.is_float()
|| self.is_double()
{
self.0.clone()
} else {
"L".into()
}
}
// TODO: TESTS // TODO: TESTS
} }

7
test.sh Executable file
View file

@ -0,0 +1,7 @@
#!/usr/bin/env sh
source venv_maturin/bin/activate
cd androscalpel
maturin develop
cd ..
python test.py