fix stuff, add class annotation

This commit is contained in:
Jean-Marie Mineau 2023-11-28 19:06:28 +01:00
parent b8b4e28f2d
commit cf55766653
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
10 changed files with 164 additions and 117 deletions

View file

@ -5,18 +5,14 @@ use std::collections::HashMap;
use crate::{dex_id::IdType, value::DexValue, DexString};
/// An annotation.
/// Annotation with a visibility
#[pyclass]
#[derive(Debug, Clone)]
pub struct Annotation {
// TODO: check the relation between type and encoded_value.
/// The type of the annotation.
#[pyo3(get, set)]
pub type_: IdType,
pub struct DexAnnotationItem {
// TODO: the get/set will probably be wonky on the python side when edditing
/// The annotation elements.
/// The actual annotation
#[pyo3(get, set)]
pub elements: HashMap<DexString, DexValue>, // TODO: check MemberName syntax?
pub annotation: DexAnnotation,
// TODO: enforce exclusivity
/// If the annotation visibility is set to build
#[pyo3(get, set)]
@ -30,12 +26,11 @@ pub struct Annotation {
}
#[pymethods]
impl Annotation {
impl DexAnnotationItem {
#[new]
pub fn new(type_: IdType, elements: HashMap<DexString, DexValue>) -> Self {
pub fn new(annotation: DexAnnotation) -> Self {
Self {
type_,
elements,
annotation,
visibility_build: false,
visibility_runtime: false,
visibility_system: false,
@ -47,7 +42,6 @@ impl Annotation {
}
pub fn __repr__(&self) -> String {
let type_ = self.type_.__str__();
let visibility =
if !(self.visibility_build || self.visibility_runtime || self.visibility_system) {
"none".into()
@ -64,11 +58,43 @@ impl Annotation {
}
visibility
};
let annotation = self.annotation.__repr__();
format!("AnnotationItem(visibility: {visibility}, {annotation})")
}
}
/// An annotation.
#[pyclass]
#[derive(Debug, Clone)]
pub struct DexAnnotation {
// TODO: check the relation between type and encoded_value.
/// The type of the annotation.
#[pyo3(get, set)]
pub type_: IdType,
// TODO: the get/set will probably be wonky on the python side when edditing
/// The annotation elements.
#[pyo3(get, set)]
pub elements: HashMap<DexString, DexValue>, // TODO: check MemberName syntax?
}
#[pymethods]
impl DexAnnotation {
#[new]
pub fn new(type_: IdType, elements: HashMap<DexString, DexValue>) -> Self {
Self { type_, elements }
}
pub fn __str__(&self) -> String {
self.__repr__()
}
pub fn __repr__(&self) -> String {
let type_ = self.type_.__str__();
let mut elts: String = "{".into();
for (key, val) in self.elements.iter() {
elts += &format!("{}: {}, ", key.__str__(), val.__str__());
}
elts += "}";
format!("Annotation({type_}, visibility: {visibility}, {elts})")
format!("Annotation({type_}, {elts})")
}
}

View file

@ -94,7 +94,7 @@ impl Apk {
let mut annotations = vec![];
if let Some(annotations_directory) = annotations_directory {
if annotations_directory.class_annotations_off != 0 {
annotations = Self::get_annotations_from_annotation_set_off(
annotations = Self::get_annotation_items_from_annotation_set_off(
annotations_directory.class_annotations_off,
dex,
)?;
@ -164,10 +164,11 @@ impl Apk {
))
}
pub fn get_annotations_from_annotation_set_off(
/// Return a [`Vec<DexAnnotationItem>`] for the offset of an [`AnnotationSet`].
pub fn get_annotation_items_from_annotation_set_off(
annotations_set_off: u32,
dex: &DexFileReader,
) -> Result<Vec<Annotation>> {
) -> Result<Vec<DexAnnotationItem>> {
let mut annotations = vec![];
for annotation_off in dex
.get_struct_at_offset::<AnnotationSetItem>(annotations_set_off)?
@ -181,16 +182,9 @@ impl Apk {
AnnotationVisibility::Runtime => (false, true, false),
AnnotationVisibility::System => (false, false, true),
};
let type_ = Self::get_id_type_from_idx(item.annotation.type_idx.0 as usize, dex)?;
let mut elements = HashMap::new();
for elt in item.annotation.elements {
let name: DexString = dex.get_string(elt.name_idx.0)?.into();
let value = Self::encoded_value_to_dex_value(&elt.value, dex)?;
elements.insert(name, value);
}
annotations.push(Annotation {
type_,
elements,
let annotation = Self::get_annotations_from_encoded_annotation(&item.annotation, dex)?;
annotations.push(DexAnnotationItem {
annotation,
visibility_build,
visibility_runtime,
visibility_system,
@ -199,6 +193,21 @@ impl Apk {
Ok(annotations)
}
/// Return a [`DexAnnotation`] from [`EncodedAnnotation`]
pub fn get_annotations_from_encoded_annotation(
encoded_annotation: &EncodedAnnotation,
dex: &DexFileReader,
) -> Result<DexAnnotation> {
let type_ = Self::get_id_type_from_idx(encoded_annotation.type_idx.0 as usize, dex)?;
let mut elements = HashMap::new();
for elt in &encoded_annotation.elements {
let name: DexString = dex.get_string(elt.name_idx.0)?.into();
let value = Self::encoded_value_to_dex_value(&elt.value, dex)?;
elements.insert(name, value);
}
Ok(DexAnnotation { type_, elements })
}
/// Return a [`IdMethodType`] from its idx.
pub fn get_id_method_type_from_idx(idx: usize, dex: &DexFileReader) -> Result<IdMethodType> {
let proto = dex.get_proto_id(idx)?;
@ -340,7 +349,9 @@ impl Apk {
.map(|val| Self::encoded_value_to_dex_value(val, dex))
.collect::<Result<_>>()?,
))),
EncodedValue::Annotation(_val) => todo!(), //Ok(DexValue::Annotation(DexAnnotation(*val))),
EncodedValue::Annotation(val) => Ok(DexValue::Annotation(
Self::get_annotations_from_encoded_annotation(val, dex)?,
)),
EncodedValue::Null => Ok(DexValue::Null(DexNull)),
EncodedValue::Boolean(val) => Ok(DexValue::Boolean(DexBoolean(*val))),
}

View file

@ -2,7 +2,7 @@
use pyo3::prelude::*;
use crate::{Annotation, DexString, Field, Method};
use crate::{DexAnnotationItem, DexString, Field, Method};
/// Represent an apk
#[pyclass]
@ -61,7 +61,11 @@ pub struct Class {
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: Vec<Annotation>,
/// The annotation related to this class (note: this does not include the
/// methods/field/parameters annotations, they are stored in the methods and fields
/// structutres)
#[pyo3(get, set)]
pub annotations: Vec<DexAnnotationItem>,
// TODO: mix annotation data to fields / methods / class to make it more practicle
}

View file

@ -418,26 +418,3 @@ impl IdEnum {
format!("DexEnum({})", self.__str__())
}
}
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IdAnnotation;
#[pymethods]
impl IdAnnotation {
#[new]
pub fn _new() -> Self {
Self
}
pub fn get_value(&self) {
todo!()
}
pub fn __str__(&self) -> String {
self.__repr__()
}
pub fn __repr__(&self) -> String {
"DexAnnotation".into()
}
}

View file

@ -1,3 +1,6 @@
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use anyhow::Result;
use pyo3::class::basic::CompareOp;
@ -65,6 +68,13 @@ impl From<String> for DexString {
}
}
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]
@ -99,12 +109,11 @@ impl DexString {
pub fn __repr__(&self) -> String {
self.into()
}
}
impl std::hash::Hash for DexString {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.get_utf16_size().hash(state);
self.get_bytes().hash(state);
fn __hash__(&self) -> u64 {
let mut hasher = DefaultHasher::new();
self.hash(&mut hasher);
hasher.finish()
}
}
@ -139,9 +148,9 @@ fn androscalpel(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<InvokeDirect>()?;
m.add_class::<InvokeInterface>()?;
m.add_class::<Annotation>()?;
m.add_class::<DexAnnotationItem>()?;
m.add_class::<DexAnnotation>()?;
m.add_class::<DexArray>()?;
m.add_class::<IdAnnotation>()?;
m.add_class::<FieldVisibility>()?;
m.add_class::<Method>()?;
m.add_class::<Field>()?;

View file

@ -259,6 +259,6 @@ impl DexArray {
}
pub fn __repr__(&self) -> String {
"DexArray()".into()
"DexArray(...)".into()
}
}

View file

@ -3,7 +3,7 @@
use pyo3::exceptions::PyTypeError;
use pyo3::prelude::*;
use crate::{dex_id::*, scalar::*, DexString, MethodHandle};
use crate::{dex_id::*, scalar::*, DexAnnotation, DexString, MethodHandle};
#[derive(Debug, Clone)]
pub enum DexValue {
@ -22,7 +22,7 @@ pub enum DexValue {
Method(IdMethod),
Enum(IdEnum),
Array(DexArray),
Annotation(IdAnnotation),
Annotation(DexAnnotation),
Null(DexNull),
Boolean(DexBoolean),
}
@ -55,7 +55,7 @@ impl<'source> FromPyObject<'source> for DexValue {
Ok(Self::Enum(val))
} else if let Ok(val) = DexArray::extract(ob) {
Ok(Self::Array(val))
} else if let Ok(val) = IdAnnotation::extract(ob) {
} else if let Ok(val) = DexAnnotation::extract(ob) {
Ok(Self::Annotation(val))
} else if let Ok(val) = DexNull::extract(ob) {
Ok(Self::Null(val))