From bcc93545268325b7903f54c2e275ccacbf149eb0 Mon Sep 17 00:00:00 2001 From: Jean-Marie 'Histausse' Mineau Date: Tue, 4 Mar 2025 09:57:21 +0100 Subject: [PATCH] compute ins_size ans outs_size on the go --- androscalpel/src/apk.rs | 2 - androscalpel/src/code.rs | 48 ++++++++++++------- .../src/code_analysis/register_type.rs | 2 +- androscalpel/src/dex_writer.rs | 17 +++---- androscalpel/src/method.rs | 19 ++++++++ 5 files changed, 58 insertions(+), 30 deletions(-) diff --git a/androscalpel/src/apk.rs b/androscalpel/src/apk.rs index 1de2a9d..065bce2 100644 --- a/androscalpel/src/apk.rs +++ b/androscalpel/src/apk.rs @@ -2769,8 +2769,6 @@ impl Apk { } Ok(Code { registers_size: code_item.registers_size, - ins_size: code_item.ins_size, - outs_size: code_item.outs_size, parameter_names, insns, }) diff --git a/androscalpel/src/code.rs b/androscalpel/src/code.rs index a05c78d..5e04185 100644 --- a/androscalpel/src/code.rs +++ b/androscalpel/src/code.rs @@ -9,8 +9,8 @@ use std::collections::{HashMap, HashSet}; use pyo3::prelude::*; use crate::{ - ins::Instruction, DexString, IdField, IdMethod, IdMethodType, IdType, MethodHandle, Result, - Visitable, VisitableMut, Visitor, VisitorMut, + ins::Instruction, DexString, IdField, IdMethod, IdMethodType, IdType, Method, MethodHandle, + Result, Visitable, VisitableMut, Visitor, VisitorMut, }; // TODO: make this easy to edit/manipulate, maybe move to Method @@ -25,14 +25,6 @@ pub struct Code { /// The number of registers used by the code #[cfg_attr(feature = "python", pyo3(get))] pub registers_size: u16, - // TODO: what does it means? is it computable? - /// The number of words of incoming arguments to the method - #[cfg_attr(feature = "python", pyo3(get))] - pub ins_size: u16, - // TODO: what does it means? is it computable? - /// The number of words of outgoing argument space - #[cfg_attr(feature = "python", pyo3(get))] - pub outs_size: u16, /// The names of the parameters if given #[cfg_attr(feature = "python", pyo3(get))] pub parameter_names: Option>>, @@ -47,8 +39,6 @@ impl PartialEq for Code { let comparable_self = self.semantic_comparable().unwrap(); let comparable_other = other.semantic_comparable().unwrap(); (comparable_self.registers_size == comparable_other.registers_size) - && (comparable_self.ins_size == comparable_other.ins_size) - && (comparable_self.outs_size == comparable_other.outs_size) && (comparable_self.insns == comparable_other.insns) } } @@ -68,20 +58,46 @@ impl Code { #[cfg_attr(feature = "python", pyo3(signature = (registers_size, ins_size, outs_size, insns, parameter_names=None)))] pub fn new( registers_size: u16, - ins_size: u16, - outs_size: u16, insns: Vec, parameter_names: Option>>, ) -> Self { Self { registers_size, - ins_size, - outs_size, insns, parameter_names, } } + /// Compute the `ins_size` field. This is the number of register parameters, including the + /// `this` parameter for non-static methods. + /// This information is stored in the code item in dex files, but is not computable from the code + /// (as opposed to `outs_size`). The [`Method`] struct is needed to compute it. + pub fn ins_size(&self, method: &Method) -> u16 { + method.ins_size() + } + /// Compute the `outs_size` field. This is the number of registers needed to call other + /// function. + pub fn outs_size(&self) -> u16 { + let mut outs = 0; + for ins in &self.insns { + match ins { + Instruction::InvokeVirtual { args, .. } + | Instruction::InvokeSuper { args, .. } + | Instruction::InvokeDirect { args, .. } + | Instruction::InvokeStatic { args, .. } + | Instruction::InvokeInterface { args, .. } + | Instruction::InvokePolymorphic { args, .. } + | Instruction::InvokeCustom { args, .. } => { + if args.len() > outs { + outs = args.len(); + } + } + _ => (), + } + } + outs as u16 + } + pub fn __str__(&self) -> String { self.__repr__() } diff --git a/androscalpel/src/code_analysis/register_type.rs b/androscalpel/src/code_analysis/register_type.rs index c294517..e20e965 100644 --- a/androscalpel/src/code_analysis/register_type.rs +++ b/androscalpel/src/code_analysis/register_type.rs @@ -54,7 +54,7 @@ impl MethodCFG<'_> { return HashMap::new(); } // Initialize the entry block from function signature: - let mut i = (code.registers_size - code.ins_size) as usize; + let mut i = (code.registers_size - code.ins_size(&self.method)) as usize; if !self.method.is_static { end_block_reg_tys[0][i] = RegType::Object; // 'this' i += 1; diff --git a/androscalpel/src/dex_writer.rs b/androscalpel/src/dex_writer.rs index ddc6549..8c504b5 100644 --- a/androscalpel/src/dex_writer.rs +++ b/androscalpel/src/dex_writer.rs @@ -406,7 +406,7 @@ impl DexWriter { /// /// This is currently a stub that probably serialize invalid references to data. fn insert_code_item(&mut self, method_id: IdMethod, direct_methods: bool) -> Result<()> { - let code = if direct_methods { + let method = if direct_methods { self.class_defs .get(&method_id.class_) .unwrap() @@ -414,10 +414,6 @@ impl DexWriter { .direct_methods .get(&method_id) .unwrap() - .code - .as_ref() - .unwrap() - .clone() } else { self.class_defs .get(&method_id.class_) @@ -426,11 +422,10 @@ impl DexWriter { .virtual_methods .get(&method_id) .unwrap() - .code - .as_ref() - .unwrap() - .clone() }; + let code = method.code.as_ref().unwrap().clone(); + let ins_size = code.ins_size(&method); + let outs_size = code.outs_size(); // Estimate instructions addresses let mut min_addr = 0; let mut max_addr = 0; @@ -864,9 +859,9 @@ impl DexWriter { }; let item = CodeItem { registers_size: code.registers_size, - ins_size: code.ins_size, - outs_size: code.outs_size, debug_info_off, // linked in link_debug_info() + ins_size, + outs_size, insns, tries, handlers, diff --git a/androscalpel/src/method.rs b/androscalpel/src/method.rs index e586849..2275d7c 100644 --- a/androscalpel/src/method.rs +++ b/androscalpel/src/method.rs @@ -294,6 +294,25 @@ impl Method { .iter() .any(|list| !list.is_empty()) } + + /// Compute the `ins_size` field. This is the number of register parameters, including the + /// `this` parameter for non-static methods. + /// This information is stored in the code item in dex files, but is not computable from the code + /// (as opposed to `outs_size`). The [`Method`] struct is needed to compute it. + pub fn ins_size(&self) -> u16 { + let mut ins = 0; + if !self.is_static { + ins += 1; // this + } + for param in &self.descriptor.proto.parameters { + if param.is_long() || param.is_double() { + ins += 2; + } else { + ins += 1; + } + } + ins + } } impl Visitable for Method {