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 log::info;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
use crate::{Class, DexString, Error, Result};
|
use crate::{Class, DexString, Field, Result};
|
||||||
use androscalpel_serializer::*;
|
use androscalpel_serializer::*;
|
||||||
|
|
||||||
/// Represent an apk.
|
/// Represent an apk.
|
||||||
|
|
@ -18,38 +18,27 @@ impl Apk {
|
||||||
pub fn add_dex_file(&mut self, data: &[u8]) -> Result<()> {
|
pub fn add_dex_file(&mut self, data: &[u8]) -> Result<()> {
|
||||||
let dex = DexFileReader::new(data)?;
|
let dex = DexFileReader::new(data)?;
|
||||||
for class in dex.get_class_defs() {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a class from a dex file reader.
|
/// Extract a class from a dex file reader.
|
||||||
fn add_class_from_dex_file(
|
fn get_class_from_dex_file(
|
||||||
&mut self,
|
&self,
|
||||||
class_item: &ClassDefItem,
|
class_item: &ClassDefItem,
|
||||||
dex: &DexFileReader,
|
dex: &DexFileReader,
|
||||||
) -> Result<()> {
|
) -> Result<Class> {
|
||||||
let name_idx = dex
|
let name_idx = dex
|
||||||
.get_type_ids()
|
.get_type_id(class_item.class_idx as usize)?
|
||||||
.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()
|
|
||||||
)))?
|
|
||||||
.descriptor_idx;
|
.descriptor_idx;
|
||||||
let name: DexString = dex.get_string(name_idx)?.into();
|
let name: DexString = dex.get_string(name_idx)?.into();
|
||||||
let superclass = if class_item.class_idx == NO_INDEX.0 {
|
let superclass = if class_item.class_idx == NO_INDEX.0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let superclass_idx = dex
|
let superclass_idx = dex
|
||||||
.get_type_ids()
|
.get_type_id(class_item.class_idx as usize)?
|
||||||
.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()
|
|
||||||
)))?
|
|
||||||
.descriptor_idx;
|
.descriptor_idx;
|
||||||
Some(dex.get_string(superclass_idx)?.into())
|
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 type_list = dex.get_struct_at_offset::<TypeList>(class_item.interfaces_off)?;
|
||||||
let mut list = vec![];
|
let mut list = vec![];
|
||||||
for ty in type_list.list {
|
for ty in type_list.list {
|
||||||
let ty = dex.get_type_ids().get(ty.type_idx as usize).ok_or(
|
let ty = dex.get_type_id(ty.type_idx as usize)?;
|
||||||
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_string(ty.descriptor_idx)?.into();
|
let ty = dex.get_string(ty.descriptor_idx)?.into();
|
||||||
list.push(ty);
|
list.push(ty);
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +82,15 @@ impl Apk {
|
||||||
class_item.access_flags
|
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,
|
name,
|
||||||
superclass,
|
superclass,
|
||||||
interfaces,
|
interfaces,
|
||||||
|
|
@ -111,8 +102,29 @@ impl Apk {
|
||||||
is_synthetic,
|
is_synthetic,
|
||||||
is_annotation,
|
is_annotation,
|
||||||
is_enum,
|
is_enum,
|
||||||
});
|
instance_fields,
|
||||||
Ok(())
|
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 pyo3::prelude::*;
|
||||||
|
|
||||||
use crate::DexString;
|
use crate::{DexString, Field};
|
||||||
|
|
||||||
/// Represent an apk
|
/// Represent an apk
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
|
@ -45,6 +45,18 @@ pub struct Class {
|
||||||
/// Name of the source file where this class is defined.
|
/// Name of the source file where this class is defined.
|
||||||
#[pyo3(get, set)]
|
#[pyo3(get, set)]
|
||||||
pub source_file: Option<DexString>,
|
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 annotations: Option<()> // TODO
|
||||||
// pub data: Option<()> // TODO
|
// pub data: Option<()> // TODO
|
||||||
// pub static_values: Option<()> // TODO
|
// pub static_values: Option<()> // TODO
|
||||||
|
|
@ -67,6 +79,8 @@ impl Class {
|
||||||
is_synthetic: false,
|
is_synthetic: false,
|
||||||
is_annotation: false,
|
is_annotation: false,
|
||||||
is_enum: false,
|
is_enum: false,
|
||||||
|
static_fields: vec![],
|
||||||
|
instance_fields: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,6 +113,7 @@ impl Class {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn __repr__(&self) -> String {
|
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 apk;
|
||||||
pub mod class;
|
pub mod class;
|
||||||
|
pub mod field;
|
||||||
|
|
||||||
pub use apk::*;
|
pub use apk::*;
|
||||||
pub use class::*;
|
pub use class::*;
|
||||||
|
pub use field::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
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<()> {
|
fn sanity_check(&self) -> Result<()> {
|
||||||
if self.header.magic.version != [0x30, 0x33, 0x39] {
|
if self.header.magic.version != [0x30, 0x33, 0x39] {
|
||||||
warn!(
|
warn!(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue