start implementing field

This commit is contained in:
Jean-Marie 'Histausse' Mineau 2023-09-01 16:50:46 +02:00
parent 0ae6ce5e88
commit c84e3e36cc
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
5 changed files with 145 additions and 32 deletions

View file

@ -2,7 +2,7 @@
use log::info;
use pyo3::prelude::*;
use crate::{Class, DexString, Error, Result};
use crate::{Class, DexString, Field, Result};
use androscalpel_serializer::*;
/// Represent an apk.
@ -18,38 +18,27 @@ impl Apk {
pub fn add_dex_file(&mut self, data: &[u8]) -> Result<()> {
let dex = DexFileReader::new(data)?;
for class in dex.get_class_defs() {
self.add_class_from_dex_file(class, &dex)?;
self.classes
.push(self.get_class_from_dex_file(class, &dex)?);
}
Ok(())
}
/// Add a class from a dex file reader.
fn add_class_from_dex_file(
&mut self,
/// Extract a class from a dex file reader.
fn get_class_from_dex_file(
&self,
class_item: &ClassDefItem,
dex: &DexFileReader,
) -> Result<()> {
) -> Result<Class> {
let name_idx = dex
.get_type_ids()
.get(class_item.class_idx as usize)
.ok_or(Error::InconsistantStruct(format!(
"type idx {} out of bound of type_ids (|type_ids| = {})",
class_item.class_idx,
dex.get_type_ids().len()
)))?
.get_type_id(class_item.class_idx as usize)?
.descriptor_idx;
let name: DexString = dex.get_string(name_idx)?.into();
let superclass = if class_item.class_idx == NO_INDEX.0 {
None
} else {
let superclass_idx = dex
.get_type_ids()
.get(class_item.class_idx as usize)
.ok_or(Error::InconsistantStruct(format!(
"type idx {} out of bound of type_ids (|type_ids| = {})",
class_item.class_idx,
dex.get_type_ids().len()
)))?
.get_type_id(class_item.class_idx as usize)?
.descriptor_idx;
Some(dex.get_string(superclass_idx)?.into())
};
@ -59,13 +48,7 @@ impl Apk {
let type_list = dex.get_struct_at_offset::<TypeList>(class_item.interfaces_off)?;
let mut list = vec![];
for ty in type_list.list {
let ty = dex.get_type_ids().get(ty.type_idx as usize).ok_or(
Error::InconsistantStruct(format!(
"type idx {} out of bound of type_ids (|type_ids| = {})",
ty.type_idx,
dex.get_type_ids().len()
)),
)?;
let ty = dex.get_type_id(ty.type_idx as usize)?;
let ty = dex.get_string(ty.descriptor_idx)?.into();
list.push(ty);
}
@ -99,7 +82,15 @@ impl Apk {
class_item.access_flags
);
}
self.classes.push(Class {
let mut static_fields = vec![];
let mut instance_fields = vec![];
let data_off = class_item.class_data_off;
if data_off != 0 {
let data = dex.get_struct_at_offset::<ClassDataItem>(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)?;
}
Ok(Class {
name,
superclass,
interfaces,
@ -111,8 +102,29 @@ impl Apk {
is_synthetic,
is_annotation,
is_enum,
});
Ok(())
instance_fields,
static_fields,
})
}
fn get_field_list_from_dex(
&self,
encoded_fields: &[EncodedField],
dex: &DexFileReader,
) -> Result<Vec<Field>> {
let mut idx = 0;
let mut fields = vec![];
for field in encoded_fields {
idx += field.field_idx_diff.0;
// TODO: flags
let id_item = dex.get_field_id(idx as usize)?;
// Check class_idx == class? ¯\_(ツ)/¯
let ty = dex.get_type_id(id_item.type_idx as usize)?;
let ty = dex.get_string(ty.descriptor_idx)?.into();
let name = dex.get_string(id_item.name_idx)?.into();
fields.push(Field { name, type_: ty })
}
Ok(fields)
}
}

View file

@ -2,7 +2,7 @@
use pyo3::prelude::*;
use crate::DexString;
use crate::{DexString, Field};
/// Represent an apk
#[pyclass]
@ -45,6 +45,18 @@ pub struct Class {
/// Name of the source file where this class is defined.
#[pyo3(get, set)]
pub source_file: Option<DexString>,
/// The static fields
#[pyo3(get, set)]
pub static_fields: Vec<Field>,
/// The instance fields
#[pyo3(get, set)]
pub instance_fields: Vec<Field>,
// /// The static methods
// #[pyo3(get, set)]
// pub methods: Vec<()>,
// Dont think we need to distinguish direct (static + private) and virtual (all the other) methods
//
// pub annotations: Option<()> // TODO
// pub data: Option<()> // TODO
// pub static_values: Option<()> // TODO
@ -67,6 +79,8 @@ impl Class {
is_synthetic: false,
is_annotation: false,
is_enum: false,
static_fields: vec![],
instance_fields: vec![],
}
}
@ -99,6 +113,7 @@ impl Class {
}
pub fn __repr__(&self) -> String {
(&self.name).into()
let name: String = (&self.name).into();
format!("Class({name})")
}
}

63
androscalpel/src/field.rs Normal file
View file

@ -0,0 +1,63 @@
//! Representation of the fields of a class.
use pyo3::prelude::*;
use crate::DexString;
/// Represent a field.
#[pyclass]
#[derive(Debug, Clone)]
pub struct Field {
/// The name of the field, format described at
/// <https://source.android.com/docs/core/runtime/dex-format#membername>
#[pyo3(get, set)]
pub name: DexString,
/// The type of the field, format described at <>
#[pyo3(get, set)]
pub type_: DexString,
}
#[pymethods]
impl Field {
#[new]
pub fn new(name: DexString, type_: DexString) -> Self {
Self { name, type_ }
}
pub fn __str__(&self) -> String {
/*
let name: String = (&self.name).into();
let file = if let Some(file) = &self.source_file {
let file: String = file.into();
format!(" defined in {file}\n")
} else {
"".into()
};
let superclass = if let Some(spcl) = &self.superclass {
let spcl: String = spcl.into();
format!(" extends: {spcl}\n")
} else {
"".into()
};
let interfaces = if self.interfaces.is_empty() {
"".into()
} else {
let mut interfaces: String = " implements:\n".into();
for it in &self.interfaces {
let it: String = it.into();
interfaces += &format!(" {it}\n");
}
interfaces
};
format!("{name}\n{file}{superclass}{interfaces}")
*/
self.__repr__()
}
pub fn __repr__(&self) -> String {
let name: String = (&self.name).into();
let ty: String = (&self.type_).into();
format!("Field({name}, {ty})")
}
}

View file

@ -4,9 +4,11 @@ use pyo3::prelude::*;
pub mod apk;
pub mod class;
pub mod field;
pub use apk::*;
pub use class::*;
pub use field::*;
#[derive(Debug)]
pub enum Error {

View file

@ -143,6 +143,27 @@ impl<'a> DexFileReader<'a> {
})
}
/// Return a [`&TypeIdItem`] from its idx.
pub fn get_type_id(&self, idx: usize) -> Result<&TypeIdItem> {
self.type_ids
.get(idx)
.ok_or(Error::InconsistantStruct(format!(
"type idx {} out of bound of type_ids (|type_ids| = {})",
idx,
self.type_ids.len()
)))
}
/// Return a [`&FieldIdItem`] from its idx.
pub fn get_field_id(&self, idx: usize) -> Result<&FieldIdItem> {
self.field_ids
.get(idx)
.ok_or(Error::InconsistantStruct(format!(
"field idx {idx} is out of bound (|field_ids|={})",
self.field_ids.len()
)))
}
fn sanity_check(&self) -> Result<()> {
if self.header.magic.version != [0x30, 0x33, 0x39] {
warn!(