add methods

This commit is contained in:
Jean-Marie Mineau 2023-10-06 19:25:23 +02:00
parent 0a112802cd
commit b704e7cbad
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
4 changed files with 300 additions and 73 deletions

View file

@ -88,11 +88,18 @@ impl Apk {
}
let mut static_fields = vec![];
let mut instance_fields = vec![];
let mut direct_methods = vec![];
let mut virtual_methods = 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)?;
static_fields = Self::get_field_list_from_encoded_field_list(&data.static_fields, dex)?;
instance_fields =
Self::get_field_list_from_encoded_field_list(&data.instance_fields, dex)?;
direct_methods =
Self::get_method_list_from_encoded_field_list(&data.direct_methods, dex)?;
virtual_methods =
Self::get_method_list_from_encoded_field_list(&data.virtual_methods, dex)?;
}
if class_item.static_values_off != 0 {
@ -130,6 +137,8 @@ impl Apk {
is_enum,
instance_fields,
static_fields,
direct_methods,
virtual_methods,
})
}
@ -284,51 +293,67 @@ impl Apk {
}
}
pub fn get_field_list_from_dex(
encoded_fields: &[EncodedField],
/// Return a [`Field`] from it's idx (index in `field_ids`) in the dex file and its access flags
/// ([`EncodedField.access_flags`])
pub fn get_field_from_idx(
idx: usize,
Uleb128(access_flags): Uleb128,
dex: &DexFileReader,
) -> Result<Vec<Field>> {
let mut idx = 0;
let mut fields = vec![];
for field in encoded_fields {
idx += field.field_idx_diff.0;
) -> Result<Field> {
let descriptor = Self::get_id_field_from_idx(idx, dex)?;
let id_item = dex.get_field_id(idx as usize)?;
let class_ty = dex.get_type_id(id_item.class_idx as usize)?;
let class: DexString = dex.get_string(class_ty.descriptor_idx)?.into();
let ty = dex.get_type_id(id_item.type_idx as usize)?;
let ty = dex.get_string(ty.descriptor_idx)?.into();
let name: DexString = dex.get_string(id_item.name_idx)?.into();
let is_public = (field.access_flags.0 & ACC_PUBLIC) != 0;
let is_private = (field.access_flags.0 & ACC_PRIVATE) != 0;
let is_protected = (field.access_flags.0 & ACC_PROTECTED) != 0;
let is_static = (field.access_flags.0 & ACC_STATIC) != 0;
let is_final = (field.access_flags.0 & ACC_FINAL) != 0;
let is_volatile = (field.access_flags.0 & ACC_VOLATILE) != 0;
let is_transient = (field.access_flags.0 & ACC_TRANSIENT) != 0;
let is_synthetic = (field.access_flags.0 & ACC_SYNTHETIC) != 0;
let is_enum = (field.access_flags.0 & ACC_ENUM) != 0;
let is_public = (access_flags & ACC_PUBLIC) != 0;
let is_private = (access_flags & ACC_PRIVATE) != 0;
let is_protected = (access_flags & ACC_PROTECTED) != 0;
let is_static = (access_flags & ACC_STATIC) != 0;
let is_final = (access_flags & ACC_FINAL) != 0;
let is_volatile = (access_flags & ACC_VOLATILE) != 0;
let is_transient = (access_flags & ACC_TRANSIENT) != 0;
let is_synthetic = (access_flags & ACC_SYNTHETIC) != 0;
let is_enum = (access_flags & ACC_ENUM) != 0;
let visibility = match (is_public, is_private, is_protected) {
(true, false, false) => FieldVisibility::Public,
(false, true, false) => FieldVisibility::Private,
(false, false, true) => FieldVisibility::Protected,
(false, false, false) => FieldVisibility::None_,
(pbl, prv, prt) => {
let class: String = class.into();
let name: String = name.into();
let class: String = descriptor.class_.0.into();
let name: String = descriptor.name.into();
return Err(Error::InconsistantStruct(format!(
"Inconsistant visiblity found in {class}.{name}: \
(public: {pbl}, private: {prv}, protected: {prt})"
)));
}
};
let descriptor = IdField {
name,
type_: IdType(ty),
class_: IdType(class),
};
fields.push(Field {
if access_flags
& !(ACC_PUBLIC
| ACC_PRIVATE
| ACC_PROTECTED
| ACC_STATIC
| ACC_FINAL
| ACC_VOLATILE
| ACC_TRANSIENT
| ACC_SYNTHETIC
| ACC_ENUM)
!= 0
{
info!(
"Unexpected flags found in {} for : 0x{:x} (unknown flags: 0x{:x})",
descriptor.__repr__(),
access_flags,
access_flags
& !(ACC_PUBLIC
| ACC_PRIVATE
| ACC_PROTECTED
| ACC_STATIC
| ACC_FINAL
| ACC_VOLATILE
| ACC_TRANSIENT
| ACC_SYNTHETIC
| ACC_ENUM)
);
}
Ok(Field {
descriptor,
visibility,
is_static,
@ -340,8 +365,152 @@ impl Apk {
value: None,
})
}
/// Return a list of field from a list of [`EncodedField`].
///
/// # Warning
///
/// The index of the fields is computed by summing the [`EncodedField.field_idx_diff`] of the
/// previous element of the list the diff of the current field, so the list must be preserved
/// as in the dex file.
pub fn get_field_list_from_encoded_field_list(
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;
fields.push(Self::get_field_from_idx(
idx as usize,
field.access_flags,
dex,
)?);
}
Ok(fields)
}
/// Return a [`Method`] from it's idx (index in `method_ids`) in the dex file and its access flags
/// ([`EncodedMethod.access_flags`]) and code offset ([`EncodedMethod.code_off`]).
pub fn get_method_from_idx(
idx: usize,
Uleb128(access_flags): Uleb128,
Uleb128(_code_off): Uleb128,
dex: &DexFileReader,
) -> Result<Method> {
let descriptor = Self::get_id_method_from_idx(idx, dex)?;
let is_public = (access_flags & ACC_PUBLIC) != 0;
let is_private = (access_flags & ACC_PRIVATE) != 0;
let is_protected = (access_flags & ACC_PROTECTED) != 0;
let is_static = (access_flags & ACC_STATIC) != 0;
let is_final = (access_flags & ACC_FINAL) != 0;
let is_synchronized = (access_flags & ACC_SYNCHRONIZED) != 0;
let is_bridge = (access_flags & ACC_BRIDGE) != 0;
let is_varargs = (access_flags & ACC_VARARGS) != 0;
let is_native = (access_flags & ACC_NATIVE) != 0;
let is_abstract = (access_flags & ACC_ABSTRACT) != 0;
let is_strictfp = (access_flags & ACC_STRICT) != 0;
let is_synthetic = (access_flags & ACC_SYNTHETIC) != 0;
let is_constructor = (access_flags & ACC_CONSTRUCTOR) != 0;
let is_declared_syncrhonized = (access_flags & ACC_DECLARED_SYNCHRONIZED) != 0;
let visibility = match (is_public, is_private, is_protected) {
(true, false, false) => MethodVisibility::Public,
(false, true, false) => MethodVisibility::Private,
(false, false, true) => MethodVisibility::Protected,
(false, false, false) => MethodVisibility::None_,
(pbl, prv, prt) => {
return Err(Error::InconsistantStruct(format!(
"Inconsistant visiblity found in {}: \
(public: {pbl}, private: {prv}, protected: {prt})",
descriptor.__repr__()
)));
// TODO: replace by public?
}
};
if access_flags
& !(ACC_PUBLIC
| ACC_PRIVATE
| ACC_PROTECTED
| ACC_STATIC
| ACC_FINAL
| ACC_SYNCHRONIZED
| ACC_BRIDGE
| ACC_VARARGS
| ACC_NATIVE
| ACC_ABSTRACT
| ACC_STRICT
| ACC_SYNTHETIC
| ACC_CONSTRUCTOR
| ACC_DECLARED_SYNCHRONIZED)
!= 0
{
info!(
"Unexpected flags found in {} for : 0x{:x} (unknown flags: 0x{:x})",
descriptor.__repr__(),
access_flags,
access_flags
& !(ACC_PUBLIC
| ACC_PRIVATE
| ACC_PROTECTED
| ACC_STATIC
| ACC_FINAL
| ACC_SYNCHRONIZED
| ACC_BRIDGE
| ACC_VARARGS
| ACC_NATIVE
| ACC_ABSTRACT
| ACC_STRICT
| ACC_SYNTHETIC
| ACC_CONSTRUCTOR
| ACC_DECLARED_SYNCHRONIZED)
);
}
Ok(Method {
descriptor,
visibility,
is_static,
is_final,
is_synchronized,
is_bridge,
is_varargs,
is_native,
is_abstract,
is_strictfp,
is_synthetic,
is_constructor,
is_declared_syncrhonized,
code: (),
})
}
/// Return a list of field from a list of [`EncodedMethod`].
///
/// # Warning
///
/// The index of the fields is computed by summing the [`EncodedMethod.field_idx_diff`] of the
/// previous element of the list the diff of the current field, so the list must be preserved
/// as in the dex file.
pub fn get_method_list_from_encoded_field_list(
encoded_methods: &[EncodedMethod],
dex: &DexFileReader,
) -> Result<Vec<Method>> {
let mut idx = 0;
let mut methods = vec![];
for method in encoded_methods {
idx += method.method_idx_diff.0;
methods.push(Self::get_method_from_idx(
idx as usize,
method.access_flags,
method.code_off,
dex,
)?);
}
Ok(methods)
}
}
#[pymethods]

View file

@ -2,7 +2,7 @@
use pyo3::prelude::*;
use crate::{DexString, Field};
use crate::{DexString, Field, Method};
/// Represent an apk
#[pyclass]
@ -46,21 +46,24 @@ pub struct Class {
#[pyo3(get, set)]
pub source_file: Option<DexString>,
// TODO: hash map?
/// 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
//
/// The direct (static, private or constructor) methods of the class
#[pyo3(get, set)]
pub direct_methods: Vec<Method>,
/// The virtual (ie non direct) methods of the class
#[pyo3(get, set)]
pub virtual_methods: Vec<Method>,
// Do we need to distinguish direct and virtual (all the other) methods?
// Maybe overlapping descriptor (same name, class and proto?)
// pub annotations: Option<()> // TODO
// pub data: Option<()> // TODO
// pub static_values: Option<()> // TODO
// TODO: mix annotation data and static values to make it more practical
// TODO: mix annotation data to fields / methods / class to make it more practicle
}
#[pymethods]
@ -81,6 +84,8 @@ impl Class {
is_enum: false,
static_fields: vec![],
instance_fields: vec![],
direct_methods: vec![],
virtual_methods: vec![],
}
}

View file

@ -22,7 +22,7 @@ impl IdMethodType {
#[new]
pub fn new(return_type: IdType, parameters: Vec<IdType>) -> Self {
Self {
shorty: Self::get_shorty(&return_type, &parameters),
shorty: Self::compute_shorty(&return_type, &parameters),
return_type,
parameters,
}
@ -43,12 +43,24 @@ impl IdMethodType {
pub fn __repr__(&self) -> String {
format!("DexMethodType({})", self.__str__())
}
pub fn get_shorty(&self) -> DexString {
self.shorty.clone()
}
pub fn get_return_type(&self) -> IdType {
self.return_type.clone()
}
pub fn get_parameters(&self) -> Vec<IdType> {
self.parameters.clone()
}
}
impl IdMethodType {
/// Compute the format for the shorty as described in
/// <https://source.android.com/docs/core/runtime/dex-format#shortydescriptor>
pub fn get_shorty(return_type: &IdType, parameters: &[IdType]) -> DexString {
pub fn compute_shorty(return_type: &IdType, parameters: &[IdType]) -> DexString {
let mut shorty: String = return_type.get_shorty().into();
for ty in parameters {
let ty: String = ty.get_shorty().into();
@ -346,8 +358,11 @@ impl IdField {
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IdMethod {
#[pyo3(get, set)]
pub class_: IdType,
#[pyo3(get, set)]
pub proto: IdMethodType,
#[pyo3(get, set)]
pub name: DexString,
}

View file

@ -11,39 +11,77 @@ pub struct Method {
/// The structure used to reference this method.
#[pyo3(get, set)]
pub descriptor: IdMethod,
/* TODO: ACCESS FLAG
/// The field visibility
/// The field visibility.
#[pyo3(get, set)]
pub visibility: FieldVisibility,
/// If the field is defined for the class globally
pub visibility: MethodVisibility,
/// Static methods do not take this in argument.
#[pyo3(get, set)]
pub is_static: bool,
/// If the field is immutable after construction
/// Final methods are not averridable.
#[pyo3(get, set)]
pub is_final: bool,
/// For thread safety
/// Synchronized method automatically acquire their associated lock around call.
/// Can only be set in native method, (`[Self::is_native] = true`).
#[pyo3(get, set)]
pub is_volatile: bool,
/// If the field should **not** be saved by default serialization
pub is_synchronized: bool,
/// Bridge are automatically added by the compiler as a type-safe bridge.
#[pyo3(get, set)]
pub is_transient: bool,
/// If the field is not defined in the source code
pub is_bridge: bool,
/// If the last argument should be treated as a "rest" argument by compiler
/// (for method of variable number of argument).
#[pyo3(get, set)]
pub is_varargs: bool,
/// If the method is a native method.
#[pyo3(get, set)]
pub is_native: bool,
/// Abstract methods are not implemented by the class.
#[pyo3(get, set)]
pub is_abstract: bool,
/// If the method must use strict rules for floating point arithmetic.
#[pyo3(get, set)]
pub is_strictfp: bool,
/// Synthetic method are not directly defined in the source code.
#[pyo3(get, set)]
pub is_synthetic: bool,
/// If the field is an enumerated value
/// If the method is a constructor.
#[pyo3(get, set)]
pub is_enum: bool,
*/
pub is_constructor: bool,
/// If the method is declared as synchronize (just indicatif)
#[pyo3(get, set)]
pub is_declared_syncrhonized: bool,
/// The code of the method
pub code: (),
}
/// Represent the visibility of a field
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MethodVisibility {
Public,
Private,
Protected,
None_, // Actually quite common
}
#[pymethods]
impl Method {
#[new]
pub fn new(descriptor: IdMethod) -> Self {
Self {
descriptor,
visibility: MethodVisibility::Public,
is_static: false,
is_final: false,
is_synchronized: false,
is_bridge: false,
is_varargs: false,
is_native: false,
is_abstract: false,
is_strictfp: false,
is_synthetic: false,
is_constructor: false,
is_declared_syncrhonized: false,
code: (),
}
}