store structs in hash map (WIP)
This commit is contained in:
parent
224d1efdba
commit
67efc6365d
5 changed files with 254 additions and 211 deletions
|
|
@ -14,7 +14,7 @@ use androscalpel_serializer::*;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Apk {
|
pub struct Apk {
|
||||||
#[pyo3(get, set)]
|
#[pyo3(get, set)]
|
||||||
pub classes: Vec<Class>,
|
pub classes: HashMap<IdType, Class>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Apk {
|
impl Apk {
|
||||||
|
|
@ -22,8 +22,8 @@ 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.classes
|
let class = self.get_class_from_dex_file(class, &dex)?;
|
||||||
.push(self.get_class_from_dex_file(class, &dex)?);
|
self.classes.insert(class.descriptor.clone(), class);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -34,17 +34,14 @@ impl Apk {
|
||||||
class_item: &ClassDefItem,
|
class_item: &ClassDefItem,
|
||||||
dex: &DexFileReader,
|
dex: &DexFileReader,
|
||||||
) -> Result<Class> {
|
) -> Result<Class> {
|
||||||
let name_idx = dex
|
let descriptor = Self::get_id_type_from_idx(class_item.class_idx as usize, dex)?;
|
||||||
.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.superclass_idx == NO_INDEX.0 {
|
let superclass = if class_item.superclass_idx == NO_INDEX.0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let superclass_idx = dex
|
Some(Self::get_id_type_from_idx(
|
||||||
.get_type_id(class_item.superclass_idx as usize)?
|
class_item.superclass_idx as usize,
|
||||||
.descriptor_idx;
|
dex,
|
||||||
Some(dex.get_string(superclass_idx)?.into())
|
)?)
|
||||||
};
|
};
|
||||||
let interfaces = if class_item.interfaces_off == 0 {
|
let interfaces = if class_item.interfaces_off == 0 {
|
||||||
vec![]
|
vec![]
|
||||||
|
|
@ -52,9 +49,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_id(ty.type_idx as usize)?;
|
list.push(Self::get_id_type_from_idx(ty.type_idx as usize, dex)?);
|
||||||
let ty = dex.get_string(ty.descriptor_idx)?.into();
|
|
||||||
list.push(ty);
|
|
||||||
}
|
}
|
||||||
list
|
list
|
||||||
};
|
};
|
||||||
|
|
@ -82,7 +77,7 @@ impl Apk {
|
||||||
{
|
{
|
||||||
info!(
|
info!(
|
||||||
"Unexpected flags found in class_def_item.access_flags for {}: 0x{:x}",
|
"Unexpected flags found in class_def_item.access_flags for {}: 0x{:x}",
|
||||||
<&DexString as Into<String>>::into(&name),
|
String::from(descriptor.get_name()),
|
||||||
class_item.access_flags
|
class_item.access_flags
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -100,43 +95,57 @@ impl Apk {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut static_fields = vec![];
|
let mut static_fields_list = vec![];
|
||||||
let mut instance_fields = vec![];
|
let mut instance_fields_list = vec![];
|
||||||
let mut direct_methods = vec![];
|
let mut direct_methods = HashMap::new();
|
||||||
let mut virtual_methods = vec![];
|
let mut virtual_methods = HashMap::new();
|
||||||
let data_off = class_item.class_data_off;
|
let data_off = class_item.class_data_off;
|
||||||
if data_off != 0 {
|
if data_off != 0 {
|
||||||
let data = dex.get_struct_at_offset::<ClassDataItem>(data_off)?;
|
let data = dex.get_struct_at_offset::<ClassDataItem>(data_off)?;
|
||||||
static_fields = Self::get_field_list_from_encoded_field_list(&data.static_fields, dex)?;
|
static_fields_list =
|
||||||
instance_fields =
|
Self::get_field_list_from_encoded_field_list(&data.static_fields, dex)?;
|
||||||
|
instance_fields_list =
|
||||||
Self::get_field_list_from_encoded_field_list(&data.instance_fields, dex)?;
|
Self::get_field_list_from_encoded_field_list(&data.instance_fields, dex)?;
|
||||||
direct_methods =
|
for method in Self::get_method_list_from_encoded_field_list(&data.direct_methods, dex)?
|
||||||
Self::get_method_list_from_encoded_field_list(&data.direct_methods, dex)?;
|
{
|
||||||
virtual_methods =
|
direct_methods.insert(method.descriptor.clone(), method);
|
||||||
Self::get_method_list_from_encoded_field_list(&data.virtual_methods, dex)?;
|
}
|
||||||
|
for method in Self::get_method_list_from_encoded_field_list(&data.virtual_methods, dex)?
|
||||||
|
{
|
||||||
|
virtual_methods.insert(method.descriptor.clone(), method);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if class_item.static_values_off != 0 {
|
if class_item.static_values_off != 0 {
|
||||||
let values = dex
|
let values = dex
|
||||||
.get_struct_at_offset::<EncodedArray>(class_item.static_values_off)?
|
.get_struct_at_offset::<EncodedArray>(class_item.static_values_off)?
|
||||||
.values;
|
.values;
|
||||||
if values.len() > static_fields.len() {
|
if values.len() > static_fields_list.len() {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"Inconsistant static_values array found in {}: \
|
"Inconsistant static_values array found in {}: \
|
||||||
|static_values| = {}, |static_fields| = {}, \
|
|static_values| = {}, |static_fields| = {}, \
|
||||||
|static_values| should be <= |static_fields|",
|
|static_values| should be <= |static_fields|",
|
||||||
<&DexString as Into<String>>::into(&name),
|
String::from(&descriptor.get_name()),
|
||||||
values.len(),
|
values.len(),
|
||||||
static_fields.len()
|
static_fields_list.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)?);
|
static_fields_list[i].value = Some(Self::encoded_value_to_dex_value(value, dex)?);
|
||||||
}
|
}
|
||||||
for field in static_fields.iter_mut().skip(values.len()) {
|
for field in static_fields_list.iter_mut().skip(values.len()) {
|
||||||
field.value = None;
|
field.value = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut static_fields = HashMap::new();
|
||||||
|
let mut instance_fields = HashMap::new();
|
||||||
|
for field in static_fields_list {
|
||||||
|
static_fields.insert(field.descriptor.clone(), field);
|
||||||
|
}
|
||||||
|
for field in instance_fields_list {
|
||||||
|
instance_fields.insert(field.descriptor.clone(), field);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(annotations_directory) = annotations_directory {
|
if let Some(annotations_directory) = annotations_directory {
|
||||||
for field_annotation in annotations_directory.field_annotations {
|
for field_annotation in annotations_directory.field_annotations {
|
||||||
let field_id =
|
let field_id =
|
||||||
|
|
@ -149,29 +158,22 @@ impl Apk {
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
if field_id.class_.get_name() != name {
|
if field_id.class_ != descriptor {
|
||||||
info!(
|
info!(
|
||||||
"Annotation for field {} found in class {}, dropping it",
|
"Annotation for field {} found in class {}, dropping it",
|
||||||
field_id.__str__(),
|
field_id.__str__(),
|
||||||
name.__str__(),
|
String::from(descriptor.get_name()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let mut found = false;
|
instance_fields
|
||||||
for field in &mut instance_fields {
|
.entry(field_id.clone())
|
||||||
if field.descriptor == field_id {
|
.and_modify(|field| field.annotations = annotations.clone());
|
||||||
field.annotations.append(&mut (annotations.clone())); // the clone is prob
|
static_fields
|
||||||
// unnecessary
|
.entry(field_id.clone())
|
||||||
found = true;
|
.and_modify(|field| field.annotations = annotations.clone());
|
||||||
}
|
if instance_fields.get(&field_id).is_none()
|
||||||
}
|
&& static_fields.get(&field_id).is_none()
|
||||||
for field in &mut static_fields {
|
{
|
||||||
if field.descriptor == field_id {
|
|
||||||
field.annotations.append(&mut (annotations.clone())); // the clone is prob
|
|
||||||
// unnecessary
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
info!(
|
info!(
|
||||||
"Annotation found for field {} but could not find the field definition, dropping it",
|
"Annotation found for field {} but could not find the field definition, dropping it",
|
||||||
field_id.__str__(),
|
field_id.__str__(),
|
||||||
|
|
@ -189,29 +191,22 @@ impl Apk {
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
if method_id.class_.get_name() != name {
|
if method_id.class_ != descriptor {
|
||||||
info!(
|
info!(
|
||||||
"Annotation for method {} found in class {}, dropping it",
|
"Annotation for method {} found in class {}, dropping it",
|
||||||
method_id.__str__(),
|
method_id.__str__(),
|
||||||
name.__str__(),
|
String::from(descriptor.get_name()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let mut found = false;
|
direct_methods
|
||||||
for method in &mut direct_methods {
|
.entry(method_id.clone())
|
||||||
if method.descriptor == method_id {
|
.and_modify(|method| method.annotations = annotations.clone()); // TODO = or append?
|
||||||
method.annotations.append(&mut (annotations.clone())); // the clone is prob
|
virtual_methods
|
||||||
// unnecessary
|
.entry(method_id.clone())
|
||||||
found = true;
|
.and_modify(|method| method.annotations = annotations.clone()); // TODO = or append?
|
||||||
}
|
if direct_methods.get(&method_id).is_none()
|
||||||
}
|
&& virtual_methods.get(&method_id).is_none()
|
||||||
for method in &mut virtual_methods {
|
{
|
||||||
if method.descriptor == method_id {
|
|
||||||
method.annotations.append(&mut (annotations.clone())); // the clone is prob
|
|
||||||
// unnecessary
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
info!(
|
info!(
|
||||||
"Annotation found for method {} but could not find the method definition, dropping it",
|
"Annotation found for method {} but could not find the method definition, dropping it",
|
||||||
method_id.__str__(),
|
method_id.__str__(),
|
||||||
|
|
@ -235,29 +230,22 @@ impl Apk {
|
||||||
annotations_list.push(vec![]);
|
annotations_list.push(vec![]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if method_id.class_.get_name() != name {
|
if method_id.class_ != descriptor {
|
||||||
info!(
|
info!(
|
||||||
"Annotation for parameter of method {} found in class {}, dropping it",
|
"Annotation for parameter of method {} found in class {}, dropping it",
|
||||||
method_id.__str__(),
|
method_id.__str__(),
|
||||||
name.__str__(),
|
String::from(descriptor.get_name()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let mut found = false;
|
direct_methods
|
||||||
for method in &mut direct_methods {
|
.entry(method_id.clone())
|
||||||
if method.descriptor == method_id {
|
.and_modify(|method| method.parameters_annotations = annotations_list.clone());
|
||||||
method.parameters_annotations = annotations_list.clone(); // the clone is prob
|
virtual_methods
|
||||||
// unnecessary
|
.entry(method_id.clone())
|
||||||
found = true;
|
.and_modify(|method| method.parameters_annotations = annotations_list.clone());
|
||||||
}
|
if direct_methods.get(&method_id).is_none()
|
||||||
}
|
&& virtual_methods.get(&method_id).is_none()
|
||||||
for method in &mut virtual_methods {
|
{
|
||||||
if method.descriptor == method_id {
|
|
||||||
method.parameters_annotations = annotations_list.clone(); // the clone is prob
|
|
||||||
// unnecessary
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
info!(
|
info!(
|
||||||
"Annotation found for parameter of method {} but could not find the method definition, dropping it",
|
"Annotation found for parameter of method {} but could not find the method definition, dropping it",
|
||||||
method_id.__str__(),
|
method_id.__str__(),
|
||||||
|
|
@ -266,7 +254,7 @@ impl Apk {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Class {
|
Ok(Class {
|
||||||
name,
|
descriptor,
|
||||||
superclass,
|
superclass,
|
||||||
interfaces,
|
interfaces,
|
||||||
source_file,
|
source_file,
|
||||||
|
|
@ -712,7 +700,9 @@ impl Apk {
|
||||||
impl Apk {
|
impl Apk {
|
||||||
#[new]
|
#[new]
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self { classes: vec![] }
|
Self {
|
||||||
|
classes: HashMap::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(name = "add_dex_file")]
|
#[pyo3(name = "add_dex_file")]
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
//! Representation of a class.
|
//! Representation of a class.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
use crate::{DexAnnotationItem, DexString, Field, Method};
|
use crate::{DexAnnotationItem, DexString, Field, IdField, IdMethod, IdType, Method, Result};
|
||||||
|
|
||||||
/// Represent an apk
|
/// Represent an apk
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Class {
|
pub struct Class {
|
||||||
/// Type name, format described at
|
/// Type, format described at
|
||||||
/// <https://source.android.com/docs/core/runtime/dex-format#typedescriptor>
|
/// <https://source.android.com/docs/core/runtime/dex-format#typedescriptor>
|
||||||
#[pyo3(get, set)]
|
#[pyo3(get, set)]
|
||||||
pub name: DexString,
|
pub descriptor: IdType,
|
||||||
/// If the class is visible everywhere
|
/// If the class is visible everywhere
|
||||||
#[pyo3(get, set)]
|
#[pyo3(get, set)]
|
||||||
pub is_public: bool,
|
pub is_public: bool,
|
||||||
|
|
@ -36,12 +38,12 @@ pub struct Class {
|
||||||
/// Name of the superclass, format described at
|
/// Name of the superclass, format described at
|
||||||
/// <https://source.android.com/docs/core/runtime/dex-format#typedescriptor>
|
/// <https://source.android.com/docs/core/runtime/dex-format#typedescriptor>
|
||||||
#[pyo3(get, set)]
|
#[pyo3(get, set)]
|
||||||
pub superclass: Option<DexString>,
|
pub superclass: Option<IdType>,
|
||||||
/// List of the interfaces that class implement, format of the interfaces
|
/// List of the interfaces that class implement, format of the interfaces
|
||||||
/// name is discribed at
|
/// name is discribed at
|
||||||
/// <https://source.android.com/docs/core/runtime/dex-format#typedescriptor>
|
/// <https://source.android.com/docs/core/runtime/dex-format#typedescriptor>
|
||||||
#[pyo3(get, set)]
|
#[pyo3(get, set)]
|
||||||
pub interfaces: Vec<DexString>,
|
pub interfaces: Vec<IdType>,
|
||||||
/// 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>,
|
||||||
|
|
@ -49,16 +51,16 @@ pub struct Class {
|
||||||
// TODO: hash map?
|
// TODO: hash map?
|
||||||
/// The static fields
|
/// The static fields
|
||||||
#[pyo3(get, set)]
|
#[pyo3(get, set)]
|
||||||
pub static_fields: Vec<Field>,
|
pub static_fields: HashMap<IdField, Field>,
|
||||||
/// The instance fields
|
/// The instance fields
|
||||||
#[pyo3(get, set)]
|
#[pyo3(get, set)]
|
||||||
pub instance_fields: Vec<Field>,
|
pub instance_fields: HashMap<IdField, Field>,
|
||||||
/// The direct (static, private or constructor) methods of the class
|
/// The direct (static, private or constructor) methods of the class
|
||||||
#[pyo3(get, set)]
|
#[pyo3(get, set)]
|
||||||
pub direct_methods: Vec<Method>,
|
pub direct_methods: HashMap<IdMethod, Method>,
|
||||||
/// The virtual (ie non direct) methods of the class
|
/// The virtual (ie non direct) methods of the class
|
||||||
#[pyo3(get, set)]
|
#[pyo3(get, set)]
|
||||||
pub virtual_methods: Vec<Method>,
|
pub virtual_methods: HashMap<IdMethod, Method>,
|
||||||
// Do we need to distinguish direct and virtual (all the other) methods?
|
// Do we need to distinguish direct and virtual (all the other) methods?
|
||||||
// Maybe overlapping descriptor (same name, class and proto?)
|
// Maybe overlapping descriptor (same name, class and proto?)
|
||||||
/// The annotation related to this class (note: this does not include the
|
/// The annotation related to this class (note: this does not include the
|
||||||
|
|
@ -72,10 +74,10 @@ pub struct Class {
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl Class {
|
impl Class {
|
||||||
#[new]
|
#[new]
|
||||||
pub fn new(name: DexString) -> Self {
|
pub fn new(name: DexString) -> Result<Self> {
|
||||||
Self {
|
Ok(Self {
|
||||||
name,
|
descriptor: IdType::new(name)?,
|
||||||
superclass: Some("Ljava/lang/Object;".into()),
|
superclass: Some(IdType::new("Ljava/lang/Object;".into())?),
|
||||||
interfaces: vec![],
|
interfaces: vec![],
|
||||||
source_file: None,
|
source_file: None,
|
||||||
is_public: true,
|
is_public: true,
|
||||||
|
|
@ -85,16 +87,16 @@ impl Class {
|
||||||
is_synthetic: false,
|
is_synthetic: false,
|
||||||
is_annotation: false,
|
is_annotation: false,
|
||||||
is_enum: false,
|
is_enum: false,
|
||||||
static_fields: vec![],
|
static_fields: HashMap::new(),
|
||||||
instance_fields: vec![],
|
instance_fields: HashMap::new(),
|
||||||
direct_methods: vec![],
|
direct_methods: HashMap::new(),
|
||||||
virtual_methods: vec![],
|
virtual_methods: HashMap::new(),
|
||||||
annotations: vec![],
|
annotations: vec![],
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn __str__(&self) -> String {
|
pub fn __str__(&self) -> String {
|
||||||
let name: String = (&self.name).into();
|
let name: String = (&self.descriptor.get_name()).into();
|
||||||
let file = if let Some(file) = &self.source_file {
|
let file = if let Some(file) = &self.source_file {
|
||||||
let file: String = file.into();
|
let file: String = file.into();
|
||||||
format!(" defined in {file}\n")
|
format!(" defined in {file}\n")
|
||||||
|
|
@ -102,7 +104,7 @@ impl Class {
|
||||||
"".into()
|
"".into()
|
||||||
};
|
};
|
||||||
let superclass = if let Some(spcl) = &self.superclass {
|
let superclass = if let Some(spcl) = &self.superclass {
|
||||||
let spcl: String = spcl.into();
|
let spcl: String = spcl.get_name().into();
|
||||||
format!(" extends: {spcl}\n")
|
format!(" extends: {spcl}\n")
|
||||||
} else {
|
} else {
|
||||||
"".into()
|
"".into()
|
||||||
|
|
@ -112,7 +114,7 @@ impl Class {
|
||||||
} else {
|
} else {
|
||||||
let mut interfaces: String = " implements:\n".into();
|
let mut interfaces: String = " implements:\n".into();
|
||||||
for it in &self.interfaces {
|
for it in &self.interfaces {
|
||||||
let it: String = it.into();
|
let it: String = it.get_name().into();
|
||||||
interfaces += &format!(" {it}\n");
|
interfaces += &format!(" {it}\n");
|
||||||
}
|
}
|
||||||
interfaces
|
interfaces
|
||||||
|
|
@ -122,7 +124,7 @@ impl Class {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn __repr__(&self) -> String {
|
pub fn __repr__(&self) -> String {
|
||||||
let name: String = (&self.name).into();
|
let name: String = (&self.descriptor.get_name()).into();
|
||||||
format!("Class({name})")
|
format!("Class({name})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
//! The class identifying dex structure.
|
//! The class identifying dex structure.
|
||||||
|
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
|
@ -7,10 +10,10 @@ use crate::{DexString, Result};
|
||||||
use androscalpel_serializer::{StringDataItem, Uleb128};
|
use androscalpel_serializer::{StringDataItem, Uleb128};
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
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, // Redondant, but same as in the encoding, keep it in cas we ever
|
pub(crate) shorty: DexString, // Redondant, but same as in the encoding, keep it in case we ever
|
||||||
// need it
|
// need it
|
||||||
pub(crate) return_type: IdType,
|
pub(crate) return_type: IdType,
|
||||||
pub(crate) parameters: Vec<IdType>,
|
pub(crate) parameters: Vec<IdType>,
|
||||||
|
|
@ -56,6 +59,12 @@ impl IdMethodType {
|
||||||
pub fn get_parameters(&self) -> Vec<IdType> {
|
pub fn get_parameters(&self) -> Vec<IdType> {
|
||||||
self.parameters.clone()
|
self.parameters.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn __hash__(&self) -> u64 {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
self.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdMethodType {
|
impl IdMethodType {
|
||||||
|
|
@ -77,12 +86,14 @@ impl IdMethodType {
|
||||||
// Not a clean rust enum because we want to be compatible with python, and maybe support strange
|
// Not a clean rust enum because we want to be compatible with python, and maybe support strange
|
||||||
// malware edge case?
|
// malware edge case?
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
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) -> Result<Self> {
|
pub fn new(
|
||||||
|
#[pyo3(from_py_with = "crate::dex_string::as_dex_string")] ty: DexString,
|
||||||
|
) -> Result<Self> {
|
||||||
// TODO: check format
|
// TODO: check format
|
||||||
let ty = Self(ty);
|
let ty = Self(ty);
|
||||||
ty.check_format()?;
|
ty.check_format()?;
|
||||||
|
|
@ -173,7 +184,7 @@ impl IdType {
|
||||||
|
|
||||||
pub fn __repr__(&self) -> String {
|
pub fn __repr__(&self) -> String {
|
||||||
let name: String = (&self.0).into();
|
let name: String = (&self.0).into();
|
||||||
format!("DexType({name})")
|
format!("IdType({name})")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the type is void (return type)
|
/// Check if the type is void (return type)
|
||||||
|
|
@ -312,11 +323,17 @@ impl IdType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn __hash__(&self) -> u64 {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
self.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: TESTS
|
// TODO: TESTS
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct IdField {
|
pub struct IdField {
|
||||||
/// The name of the field, format described at
|
/// The name of the field, format described at
|
||||||
/// <https://source.android.com/docs/core/runtime/dex-format#membername>
|
/// <https://source.android.com/docs/core/runtime/dex-format#membername>
|
||||||
|
|
@ -333,7 +350,12 @@ pub struct IdField {
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl IdField {
|
impl IdField {
|
||||||
#[new]
|
#[new]
|
||||||
pub fn new(name: DexString, type_: IdType, class_: IdType) -> Self {
|
pub fn new(
|
||||||
|
#[pyo3(from_py_with = "crate::dex_string::as_dex_string")] name: DexString,
|
||||||
|
type_: IdType,
|
||||||
|
class_: IdType,
|
||||||
|
) -> Self {
|
||||||
|
// Todo: check that class_ is a class?
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
type_,
|
type_,
|
||||||
|
|
@ -352,10 +374,16 @@ impl IdField {
|
||||||
let name: String = (&self.name).into();
|
let name: String = (&self.name).into();
|
||||||
format!("IdField({class}.{name})")
|
format!("IdField({class}.{name})")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn __hash__(&self) -> u64 {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
self.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct IdMethod {
|
pub struct IdMethod {
|
||||||
#[pyo3(get, set)]
|
#[pyo3(get, set)]
|
||||||
pub class_: IdType,
|
pub class_: IdType,
|
||||||
|
|
@ -394,6 +422,12 @@ impl IdMethod {
|
||||||
pub fn __repr__(&self) -> String {
|
pub fn __repr__(&self) -> String {
|
||||||
format!("DexMethod({})", self.__str__())
|
format!("DexMethod({})", self.__str__())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn __hash__(&self) -> u64 {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
self.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
|
|
||||||
110
androscalpel/src/dex_string.rs
Normal file
110
androscalpel/src/dex_string.rs
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
use pyo3::class::basic::CompareOp;
|
||||||
|
use pyo3::exceptions::PyTypeError;
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub struct DexString(pub androscalpel_serializer::StringDataItem);
|
||||||
|
|
||||||
|
impl From<DexString> for androscalpel_serializer::StringDataItem {
|
||||||
|
fn from(DexString(string): DexString) -> Self {
|
||||||
|
string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<androscalpel_serializer::StringDataItem> for DexString {
|
||||||
|
fn from(string: androscalpel_serializer::StringDataItem) -> Self {
|
||||||
|
Self(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&DexString> for String {
|
||||||
|
fn from(DexString(string): &DexString) -> Self {
|
||||||
|
string
|
||||||
|
.try_into()
|
||||||
|
.unwrap_or(format!("InvalidEncoding:{:x?}", string.data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DexString> for String {
|
||||||
|
fn from(string: DexString) -> Self {
|
||||||
|
(&string).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for DexString {
|
||||||
|
fn from(string: &str) -> Self {
|
||||||
|
Self(string.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for DexString {
|
||||||
|
fn from(string: String) -> Self {
|
||||||
|
Self(string.as_str().into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for DexString {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.get_utf16_size().hash(state);
|
||||||
|
self.get_bytes().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_dex_string(obj: &PyAny) -> PyResult<DexString> {
|
||||||
|
if let Ok(string) = DexString::extract(obj) {
|
||||||
|
Ok(string)
|
||||||
|
} else if let Ok(string) = String::extract(obj) {
|
||||||
|
Ok(string.into())
|
||||||
|
} else {
|
||||||
|
Err(PyErr::new::<PyTypeError, _>(format!(
|
||||||
|
"{} cannot be converted to a DexString",
|
||||||
|
obj.repr()?
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl DexString {
|
||||||
|
#[new]
|
||||||
|
pub fn new(s: &str) -> Self {
|
||||||
|
s.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the binary mutf-8 encoded string (minus the trailling 0)
|
||||||
|
pub fn get_bytes(&self) -> &[u8] {
|
||||||
|
&self.0.data
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the 'utf-16' size of the string (number of unicode code point, ie its lenght in 'java-land')
|
||||||
|
pub fn get_utf16_size(&self) -> u32 {
|
||||||
|
self.0.utf16_size.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn __str__(&self) -> String {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __richcmp__(&self, other: &PyAny, op: CompareOp, py: Python<'_>) -> PyResult<PyObject> {
|
||||||
|
let other: Self = other
|
||||||
|
.extract()
|
||||||
|
.or(<String as FromPyObject>::extract(other).map(|string| string.into()))?;
|
||||||
|
match op {
|
||||||
|
CompareOp::Eq => Ok((self == &other).into_py(py)),
|
||||||
|
_ => Ok(py.NotImplemented()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn __repr__(&self) -> String {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __hash__(&self) -> u64 {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
self.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,12 @@
|
||||||
use std::collections::hash_map::DefaultHasher;
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use pyo3::class::basic::CompareOp;
|
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
pub mod annotation;
|
pub mod annotation;
|
||||||
pub mod apk;
|
pub mod apk;
|
||||||
pub mod class;
|
pub mod class;
|
||||||
pub mod dex_id;
|
pub mod dex_id;
|
||||||
|
pub mod dex_string;
|
||||||
pub mod field;
|
pub mod field;
|
||||||
pub mod method;
|
pub mod method;
|
||||||
pub mod method_handle;
|
pub mod method_handle;
|
||||||
|
|
@ -20,103 +17,13 @@ pub use annotation::*;
|
||||||
pub use apk::*;
|
pub use apk::*;
|
||||||
pub use class::*;
|
pub use class::*;
|
||||||
pub use dex_id::*;
|
pub use dex_id::*;
|
||||||
|
pub use dex_string::*;
|
||||||
pub use field::*;
|
pub use field::*;
|
||||||
pub use method::*;
|
pub use method::*;
|
||||||
pub use method_handle::*;
|
pub use method_handle::*;
|
||||||
pub use scalar::*;
|
pub use scalar::*;
|
||||||
pub use value::*;
|
pub use value::*;
|
||||||
|
|
||||||
#[pyclass]
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
||||||
pub struct DexString(androscalpel_serializer::StringDataItem);
|
|
||||||
|
|
||||||
impl From<DexString> for androscalpel_serializer::StringDataItem {
|
|
||||||
fn from(DexString(string): DexString) -> Self {
|
|
||||||
string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<androscalpel_serializer::StringDataItem> for DexString {
|
|
||||||
fn from(string: androscalpel_serializer::StringDataItem) -> Self {
|
|
||||||
Self(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&DexString> for String {
|
|
||||||
fn from(DexString(string): &DexString) -> Self {
|
|
||||||
string
|
|
||||||
.try_into()
|
|
||||||
.unwrap_or(format!("InvalidEncoding:{:x?}", string.data))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DexString> for String {
|
|
||||||
fn from(string: DexString) -> Self {
|
|
||||||
(&string).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for DexString {
|
|
||||||
fn from(string: &str) -> Self {
|
|
||||||
Self(string.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for DexString {
|
|
||||||
fn from(string: String) -> Self {
|
|
||||||
Self(string.as_str().into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for DexString {
|
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
self.get_utf16_size().hash(state);
|
|
||||||
self.get_bytes().hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl DexString {
|
|
||||||
#[new]
|
|
||||||
pub fn new(s: &str) -> Self {
|
|
||||||
s.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the binary mutf-8 encoded string (minus the trailling 0)
|
|
||||||
pub fn get_bytes(&self) -> &[u8] {
|
|
||||||
&self.0.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the 'utf-16' size of the string (number of unicode code point, ie its lenght in 'java-land')
|
|
||||||
pub fn get_utf16_size(&self) -> u32 {
|
|
||||||
self.0.utf16_size.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn __str__(&self) -> String {
|
|
||||||
self.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn __richcmp__(&self, other: &PyAny, op: CompareOp, py: Python<'_>) -> PyResult<PyObject> {
|
|
||||||
let other: Self = other
|
|
||||||
.extract()
|
|
||||||
.or(<String as FromPyObject>::extract(other).map(|string| string.into()))?;
|
|
||||||
match op {
|
|
||||||
CompareOp::Eq => Ok((self == &other).into_py(py)),
|
|
||||||
_ => Ok(py.NotImplemented()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn __repr__(&self) -> String {
|
|
||||||
self.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn __hash__(&self) -> u64 {
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
self.hash(&mut hasher);
|
|
||||||
hasher.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Androscalpel.
|
/// Androscalpel.
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
fn androscalpel(_py: Python, m: &PyModule) -> PyResult<()> {
|
fn androscalpel(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue