From 625420c5f6ae7125d40b24dc28a501ed721bc749 Mon Sep 17 00:00:00 2001 From: Jean-Marie 'Histausse' Mineau Date: Fri, 1 Sep 2023 12:22:24 +0200 Subject: [PATCH] add flags and DexString cmp --- androscalpel/src/apk.rs | 36 +++++++++++++++++++++++++++++++++--- androscalpel/src/class.rs | 37 ++++++++++++++++++++++++++++++++++++- androscalpel/src/lib.rs | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 4 deletions(-) diff --git a/androscalpel/src/apk.rs b/androscalpel/src/apk.rs index 615f15d..e45840d 100644 --- a/androscalpel/src/apk.rs +++ b/androscalpel/src/apk.rs @@ -1,8 +1,8 @@ //! Representation of an apk. use pyo3::prelude::*; -use crate::{Class, Error, Result}; -use androscalpel_serializer::{ClassDefItem, DexFileReader, TypeList, NO_INDEX}; +use crate::{Class, DexString, Error, Result}; +use androscalpel_serializer::*; /// Represent an apk. #[pyclass] @@ -37,7 +37,7 @@ impl Apk { dex.get_type_ids().len() )))? .descriptor_idx; - let name = 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 { None } else { @@ -75,11 +75,41 @@ impl Apk { } else { Some(dex.get_string(class_item.source_file_idx)?.into()) }; + let is_public = (class_item.access_flags & ACC_PUBLIC) != 0; + let is_final = (class_item.access_flags & ACC_FINAL) != 0; + let is_interface = (class_item.access_flags & ACC_INTERFACE) != 0; + let is_abstract = (class_item.access_flags & ACC_ABSTRACT) != 0; + let is_synthetic = (class_item.access_flags & ACC_SYNTHETIC) != 0; + let is_annotation = (class_item.access_flags & ACC_ANNOTATION) != 0; + let is_enum = (class_item.access_flags & ACC_ENUM) != 0; + if (class_item.access_flags + & !(ACC_PUBLIC + | ACC_FINAL + | ACC_INTERFACE + | ACC_ABSTRACT + | ACC_SYNTHETIC + | ACC_ANNOTATION + | ACC_ENUM)) + != 0 + { + println!( + "Unexpected flags found in class_def_item.access_flags for {}: 0x{:x}", + <&DexString as Into>::into(&name), + class_item.access_flags + ); // TODO: better logging + } self.classes.push(Class { name, superclass, interfaces, source_file, + is_public, + is_final, + is_interface, + is_abstract, + is_synthetic, + is_annotation, + is_enum, }); Ok(()) } diff --git a/androscalpel/src/class.rs b/androscalpel/src/class.rs index da48304..f5a6410 100644 --- a/androscalpel/src/class.rs +++ b/androscalpel/src/class.rs @@ -8,13 +8,41 @@ use crate::DexString; #[pyclass] #[derive(Debug, Clone)] pub struct Class { + /// Type name, format described at + /// #[pyo3(get, set)] pub name: DexString, - // pub access_flags: // TODO + /// If the class is visible everywhere + #[pyo3(get, set)] + pub is_public: bool, // TODO: is it possible to not be public non a non inner class? + /// If the class is subclassable + #[pyo3(get, set)] + pub is_final: bool, + /// If the class is a 'multipy-implementable abstract class' AKA an interface + #[pyo3(get, set)] + pub is_interface: bool, + /// If the class is instanciable + #[pyo3(get, set)] + pub is_abstract: bool, + /// If the class is not directly defined in the source code + #[pyo3(get, set)] + pub is_synthetic: bool, + /// If the class is an annotation + #[pyo3(get, set)] + pub is_annotation: bool, + /// If the class is an enum + #[pyo3(get, set)] + pub is_enum: bool, + /// Name of the superclass, format described at + /// #[pyo3(get, set)] pub superclass: Option, + /// List of the interfaces that class implement, format of the interfaces + /// name is discribed at + /// #[pyo3(get, set)] pub interfaces: Vec, + /// Name of the source file where this class is defined. #[pyo3(get, set)] pub source_file: Option, // pub annotations: Option<()> // TODO @@ -32,6 +60,13 @@ impl Class { superclass: None, interfaces: vec![], source_file: None, + is_public: true, + is_final: false, + is_interface: false, + is_abstract: false, + is_synthetic: false, + is_annotation: false, + is_enum: false, } } diff --git a/androscalpel/src/lib.rs b/androscalpel/src/lib.rs index 2fb22d8..5cd8cb7 100644 --- a/androscalpel/src/lib.rs +++ b/androscalpel/src/lib.rs @@ -1,3 +1,4 @@ +use pyo3::class::basic::CompareOp; use pyo3::exceptions::PyValueError; use pyo3::prelude::*; @@ -93,11 +94,49 @@ impl From for String { } } +impl From<&str> for DexString { + fn from(string: &str) -> Self { + Self(string.into()) + } +} + +impl From for DexString { + fn from(string: String) -> Self { + Self(string.as_str().into()) + } +} + #[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 { + let other: Self = other + .extract() + .or(::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() }