start implementing field
This commit is contained in:
parent
0ae6ce5e88
commit
c84e3e36cc
5 changed files with 145 additions and 32 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
63
androscalpel/src/field.rs
Normal 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})")
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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!(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue