From feff847310949acd70d17f0ec3a6e2fe406c4ddb Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Mon, 2 Oct 2023 17:55:17 +0200 Subject: [PATCH] add method handle This really needs more checks --- androscalpel/src/apk.rs | 43 +++- androscalpel/src/dex_id.rs | 25 -- androscalpel/src/lib.rs | 22 +- androscalpel/src/method_handle.rs | 278 +++++++++++++++++++++ androscalpel/src/value.rs | 9 +- androscalpel_serializer/src/file_reader.rs | 10 + 6 files changed, 351 insertions(+), 36 deletions(-) create mode 100644 androscalpel/src/method_handle.rs diff --git a/androscalpel/src/apk.rs b/androscalpel/src/apk.rs index fde40ba..a5635a0 100644 --- a/androscalpel/src/apk.rs +++ b/androscalpel/src/apk.rs @@ -199,6 +199,44 @@ impl Apk { }) } + /// Return a [`MethodHandle`] from its idx. + pub fn get_method_handle_from_idx(idx: usize, dex: &DexFileReader) -> Result { + let handle = dex.get_method_handle(idx)?; + match handle.method_handle_type { + MethodHandleType::StaticPut => Ok(MethodHandle::StaticPut(StaticPut( + Self::get_id_field_from_idx(handle.field_or_method_id as usize, dex)?, + ))), + MethodHandleType::StaticGet => Ok(MethodHandle::StaticGet(StaticGet( + Self::get_id_field_from_idx(handle.field_or_method_id as usize, dex)?, + ))), + MethodHandleType::InstancePut => Ok(MethodHandle::InstancePut(InstancePut( + Self::get_id_field_from_idx(handle.field_or_method_id as usize, dex)?, + ))), + MethodHandleType::InstanceGet => Ok(MethodHandle::InstanceGet(InstanceGet( + Self::get_id_field_from_idx(handle.field_or_method_id as usize, dex)?, + ))), + MethodHandleType::InvokeStatic => Ok(MethodHandle::InvokeStatic(InvokeStatic( + Self::get_id_method_from_idx(handle.field_or_method_id as usize, dex)?, + ))), + MethodHandleType::InvokeInstance => Ok(MethodHandle::InvokeInstance(InvokeInstance( + Self::get_id_method_from_idx(handle.field_or_method_id as usize, dex)?, + ))), + MethodHandleType::InvokeConstructor => { + Ok(MethodHandle::InvokeConstructor(InvokeConstructor( + Self::get_id_method_from_idx(handle.field_or_method_id as usize, dex)?, + ))) + } + MethodHandleType::InvokeDirect => Ok(MethodHandle::InvokeDirect(InvokeDirect( + Self::get_id_method_from_idx(handle.field_or_method_id as usize, dex)?, + ))), + MethodHandleType::InvokeInterface => { + Ok(MethodHandle::InvokeInterface(InvokeInterface( + Self::get_id_method_from_idx(handle.field_or_method_id as usize, dex)?, + ))) + } + } + } + pub fn encoded_value_to_dex_value( encoded_value: &EncodedValue, dex: &DexFileReader, @@ -214,8 +252,9 @@ impl Apk { EncodedValue::MethodType(val) => Ok(DexValue::MethodType( Self::get_id_method_type_from_idx(*val as usize, dex)?, )), - // TODO: need method to be implemented first - EncodedValue::MethodHandle(_val) => todo!(), //Ok(DexValue::MethodHandle(DexMethodHandle(*val))), + EncodedValue::MethodHandle(val) => Ok(DexValue::MethodHandle( + Self::get_method_handle_from_idx(*val as usize, dex)?, + )), EncodedValue::String(val) => Ok(DexValue::String(dex.get_string(*val)?.into())), EncodedValue::Type(val) => Ok(DexValue::Type(Self::get_id_type_from_idx( *val as usize, diff --git a/androscalpel/src/dex_id.rs b/androscalpel/src/dex_id.rs index 7a964e6..c4bbba8 100644 --- a/androscalpel/src/dex_id.rs +++ b/androscalpel/src/dex_id.rs @@ -58,31 +58,6 @@ impl IdMethodType { } } -/* -#[pyclass] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct IdMethodHandle(pub u32); -#[pymethods] -impl DexMethodHandle { - #[new] - pub fn new(val: u32) -> Self { - Self(val) - } - - pub fn get_value(&self) -> u32 { - self.0 - } - - pub fn __str__(&self) -> String { - self.__repr__() - } - - pub fn __repr__(&self) -> String { - format!("DexMethodHandle({})", self.0) - } -} -*/ - /// A type. /// Type represented by [`DexString`] that follow the TypeDescriptor format /// as described here diff --git a/androscalpel/src/lib.rs b/androscalpel/src/lib.rs index c7db25f..40f8f74 100644 --- a/androscalpel/src/lib.rs +++ b/androscalpel/src/lib.rs @@ -6,6 +6,7 @@ pub mod apk; pub mod class; pub mod dex_id; pub mod field; +pub mod method_handle; pub mod scalar; pub mod value; @@ -13,6 +14,7 @@ pub use apk::*; pub use class::*; pub use dex_id::*; pub use field::*; +pub use method_handle::*; pub use scalar::*; pub use value::*; @@ -154,6 +156,8 @@ impl DexString { #[pymodule] fn androscalpel(_py: Python, m: &PyModule) -> PyResult<()> { pyo3_log::init(); + m.add_class::()?; + m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; @@ -161,18 +165,26 @@ fn androscalpel(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; - // m.add_class::()?; - // m.add_class::()?; m.add_class::()?; + + m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; m.add_class::()?; - m.add_class::()?; - m.add_class::()?; - m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; diff --git a/androscalpel/src/method_handle.rs b/androscalpel/src/method_handle.rs new file mode 100644 index 0000000..e1aaa9f --- /dev/null +++ b/androscalpel/src/method_handle.rs @@ -0,0 +1,278 @@ +//! The structure use to reference a method invocation. + +use pyo3::exceptions::PyTypeError; +use pyo3::prelude::*; + +use crate::dex_id::*; + +/// The structure use to reference a method invocation. +#[derive(Debug, Clone)] +pub enum MethodHandle { + StaticPut(StaticPut), + StaticGet(StaticGet), + InstancePut(InstancePut), + InstanceGet(InstanceGet), + InvokeStatic(InvokeStatic), + InvokeInstance(InvokeInstance), + InvokeConstructor(InvokeConstructor), + InvokeDirect(InvokeDirect), + InvokeInterface(InvokeInterface), +} + +#[pyclass] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StaticPut(pub IdField); + +#[pymethods] +impl StaticPut { + #[new] + pub fn new(val: IdField) -> Self { + Self(val) + } + + pub fn get_field(&self) -> IdField { + self.0.clone() + } + + pub fn __str__(&self) -> String { + self.__repr__() + } + + pub fn __repr__(&self) -> String { + format!("StaticPut({})", self.0.__str__()) + } +} +#[pyclass] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StaticGet(pub IdField); + +#[pymethods] +impl StaticGet { + #[new] + pub fn new(val: IdField) -> Self { + Self(val) + } + + pub fn get_field(&self) -> IdField { + self.0.clone() + } + + pub fn __str__(&self) -> String { + self.__repr__() + } + + pub fn __repr__(&self) -> String { + format!("StaticGet({})", self.0.__str__()) + } +} +#[pyclass] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InstancePut(pub IdField); + +#[pymethods] +impl InstancePut { + #[new] + pub fn new(val: IdField) -> Self { + Self(val) + } + + pub fn get_field(&self) -> IdField { + self.0.clone() + } + + pub fn __str__(&self) -> String { + self.__repr__() + } + + pub fn __repr__(&self) -> String { + format!("InstancePut({})", self.0.__str__()) + } +} +#[pyclass] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InstanceGet(pub IdField); + +#[pymethods] +impl InstanceGet { + #[new] + pub fn new(val: IdField) -> Self { + Self(val) + } + + pub fn get_field(&self) -> IdField { + self.0.clone() + } + + pub fn __str__(&self) -> String { + self.__repr__() + } + + pub fn __repr__(&self) -> String { + format!("InstanceGet({})", self.0.__str__()) + } +} + +#[pyclass] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InvokeStatic(pub IdMethod); + +#[pymethods] +impl InvokeStatic { + #[new] + pub fn new(val: IdMethod) -> Self { + Self(val) + } + + pub fn get_method(&self) -> IdMethod { + self.0.clone() + } + + pub fn __str__(&self) -> String { + self.__repr__() + } + + pub fn __repr__(&self) -> String { + format!("InvokeStatic({})", self.0.__str__()) + } +} + +#[pyclass] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InvokeInstance(pub IdMethod); + +#[pymethods] +impl InvokeInstance { + #[new] + pub fn new(val: IdMethod) -> Self { + Self(val) + } + + pub fn get_method(&self) -> IdMethod { + self.0.clone() + } + + pub fn __str__(&self) -> String { + self.__repr__() + } + + pub fn __repr__(&self) -> String { + format!("InvokeInstance({})", self.0.__str__()) + } +} + +#[pyclass] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InvokeConstructor(pub IdMethod); + +#[pymethods] +impl InvokeConstructor { + #[new] + pub fn new(val: IdMethod) -> Self { + Self(val) + } + + pub fn get_method(&self) -> IdMethod { + self.0.clone() + } + + pub fn __str__(&self) -> String { + self.__repr__() + } + + pub fn __repr__(&self) -> String { + format!("InvokeConstructor({})", self.0.__str__()) + } +} + +#[pyclass] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InvokeDirect(pub IdMethod); + +#[pymethods] +impl InvokeDirect { + #[new] + pub fn new(val: IdMethod) -> Self { + Self(val) + } + + pub fn get_method(&self) -> IdMethod { + self.0.clone() + } + + pub fn __str__(&self) -> String { + self.__repr__() + } + + pub fn __repr__(&self) -> String { + format!("InvokeDirect({})", self.0.__str__()) + } +} + +#[pyclass] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InvokeInterface(pub IdMethod); + +#[pymethods] +impl InvokeInterface { + #[new] + pub fn new(val: IdMethod) -> Self { + Self(val) + } + + pub fn get_method(&self) -> IdMethod { + self.0.clone() + } + + pub fn __str__(&self) -> String { + self.__repr__() + } + + pub fn __repr__(&self) -> String { + format!("InvokeInterface({})", self.0.__str__()) + } +} + +impl<'source> FromPyObject<'source> for MethodHandle { + fn extract(ob: &'source PyAny) -> PyResult { + if let Ok(val) = StaticPut::extract(ob) { + Ok(Self::StaticPut(val)) + } else if let Ok(val) = StaticGet::extract(ob) { + Ok(Self::StaticGet(val)) + } else if let Ok(val) = InstancePut::extract(ob) { + Ok(Self::InstancePut(val)) + } else if let Ok(val) = InstanceGet::extract(ob) { + Ok(Self::InstanceGet(val)) + } else if let Ok(val) = InvokeStatic::extract(ob) { + Ok(Self::InvokeStatic(val)) + } else if let Ok(val) = InvokeInstance::extract(ob) { + Ok(Self::InvokeInstance(val)) + } else if let Ok(val) = InvokeConstructor::extract(ob) { + Ok(Self::InvokeConstructor(val)) + } else if let Ok(val) = InvokeDirect::extract(ob) { + Ok(Self::InvokeDirect(val)) + } else if let Ok(val) = InvokeInterface::extract(ob) { + Ok(Self::InvokeInterface(val)) + } else { + Err(PyErr::new::(format!( + "{} is not a castable as a MethodHandle", + ob.repr()? + ))) + } + } +} + +impl IntoPy for MethodHandle { + fn into_py(self, py: Python<'_>) -> PyObject { + match self { + Self::StaticPut(val) => val.into_py(py), + Self::StaticGet(val) => val.into_py(py), + Self::InstancePut(val) => val.into_py(py), + Self::InstanceGet(val) => val.into_py(py), + Self::InvokeStatic(val) => val.into_py(py), + Self::InvokeInstance(val) => val.into_py(py), + Self::InvokeConstructor(val) => val.into_py(py), + Self::InvokeDirect(val) => val.into_py(py), + Self::InvokeInterface(val) => val.into_py(py), + } + } +} diff --git a/androscalpel/src/value.rs b/androscalpel/src/value.rs index adfeec9..a29ea04 100644 --- a/androscalpel/src/value.rs +++ b/androscalpel/src/value.rs @@ -3,7 +3,7 @@ use pyo3::exceptions::PyTypeError; use pyo3::prelude::*; -use crate::{dex_id::*, scalar::*, DexString}; +use crate::{dex_id::*, scalar::*, DexString, MethodHandle}; #[derive(Debug, Clone)] pub enum DexValue { @@ -15,7 +15,7 @@ pub enum DexValue { Float(DexFloat), Double(DexDouble), MethodType(IdMethodType), - //MethodHandle(DexMethodHandle), + MethodHandle(MethodHandle), String(DexString), Type(IdType), Field(IdField), @@ -63,7 +63,8 @@ impl<'source> FromPyObject<'source> for DexValue { Ok(Self::Boolean(val)) } else if let Ok(val) = IdMethodType::extract(ob) { Ok(Self::MethodType(val)) - // } else if let Ok(val) = DexMethodHandle::extract(ob) { Ok(Self::MethodHandle(val)) + } else if let Ok(val) = MethodHandle::extract(ob) { + Ok(Self::MethodHandle(val)) } else { Err(PyErr::new::(format!( "{} is not a castable as a DexValue", @@ -84,7 +85,7 @@ impl IntoPy for DexValue { DexValue::Float(val) => val.into_py(py), DexValue::Double(val) => val.into_py(py), DexValue::MethodType(val) => val.into_py(py), - //DexValue::MethodHandle(val) => val.into_py(py), + DexValue::MethodHandle(val) => val.into_py(py), DexValue::String(val) => val.into_py(py), DexValue::Type(val) => val.into_py(py), DexValue::Field(val) => val.into_py(py), diff --git a/androscalpel_serializer/src/file_reader.rs b/androscalpel_serializer/src/file_reader.rs index 96c1c5c..a1e1933 100644 --- a/androscalpel_serializer/src/file_reader.rs +++ b/androscalpel_serializer/src/file_reader.rs @@ -184,6 +184,16 @@ impl<'a> DexFileReader<'a> { ))) } + /// Return a [`MethodHandleItem`] reference from its idx. + pub fn get_method_handle(&self, idx: usize) -> Result<&MethodHandleItem> { + self.method_handles + .get(idx) + .ok_or(Error::InconsistantStruct(format!( + "method handle {idx} is out of bound (|method_handles|={})", + self.method_handles.len() + ))) + } + fn sanity_check(&self) -> Result<()> { if self.header.magic.version != [0x30, 0x33, 0x39] { warn!(