From 71fc0d2398ff7c55fa97848efabb8a4a707eef6b Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Mon, 11 Mar 2024 16:31:03 +0100 Subject: [PATCH 01/14] wip: start building fragment --- androscalpel/src/dex_fragment.rs | 2912 ++++++++++++++++++++++++++++++ androscalpel/src/lib.rs | 1 + 2 files changed, 2913 insertions(+) create mode 100644 androscalpel/src/dex_fragment.rs diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs new file mode 100644 index 0000000..ed45a38 --- /dev/null +++ b/androscalpel/src/dex_fragment.rs @@ -0,0 +1,2912 @@ +//! The structure that generate a .dex from classes. + +use std::collections::{HashMap, HashSet, VecDeque}; +use std::io; +use std::io::{Cursor, Seek, SeekFrom, Write}; + +use anyhow::{anyhow, bail, Context}; +use log::debug; + +use crate::Result; +use crate::*; +use androscalpel_serializer::*; + +use crate::ins::{CallSite, Instruction}; +use crate::instructions::*; +use androscalpel_serializer::Instruction as InsFormat; + +/// A [`DexFragment`] is a 'precompile' class. Its is not compiled per-say, +/// but most structures are converted to a [`Serializable`] structs close to +/// the dalvik format. The structs still need to be linked before serialization. +/// +/// Ids structs ([`IdType`], [`IdMethodType`], [`IdField`], ect...) are almost entirely +/// recomputed at the link stage and need to be sorted, so they are kept in the hight +/// level format. +#[derive(Debug, Clone)] +pub struct DexFragment { + /// The id of the class represented by this fragment. + class_id: IdType, + /// The strings in the dex file, sorted. + strings: Vec, + /// The types in the dex file, sorted. + type_ids: Vec, + /// The prototypes in the dex file, sorted. + proto_ids: Vec, + /// The field ids in the dex file, sorted. + field_ids: Vec, + /// The methods ids in the dex file, sorted. + method_ids: Vec, + /// The call sites refered to in the bytecode. + call_site_ids: Vec, + /// A struct that keep track of sections size, nb elements and offsets. + section_manager: FragSectionManager, + /// The class_def. The `annotations_off`, `class_data_off` and `static_values_off` field take + /// the value of the offset from the start of their respective section +1 (or zero if the class + /// does not have a value associated to the field). + class_def: ClassDefItem, + /// The class_data. + class_data: Option, + // TODO: type list should be handle like other ids + // TODO: type list inside of proto ids are not handled here + /// The type lists found in the classes associated to their index in the type_lists section. + type_lists_index: HashMap, + /// The type_lists section and the offset of the lists inside the section. + type_lists_with_offset: Vec<(TypeList, u32)>, + /// The encoded_array_items section. + encoded_array_items: Vec, + /// The annotations_directory_item section. + annotations_directory_items: Vec, + /// The annotation_set_item section. + annotation_set_items: Vec, + /// The annotation item section. + annotation_items: Vec, + /// The annotation_set_ref_list section. + annotation_set_lists: Vec, + /// The method_handles section. + method_handles: Vec, + /// The code_items sections. + code_items: Vec, + /// The debug info items section. + debug_info_items: Vec, +} + +impl DexFragment { + pub fn new(class: &Class) -> Result { + debug!( + "Building dex fragment for class {}", + class.descriptor.__str__() + ); + let mut frag = Self { + class_id: class.descriptor.clone(), + strings: vec![], + type_ids: vec![], + proto_ids: vec![], + field_ids: vec![], + method_ids: vec![], + class_def: ClassDefItem { + class_idx: 0, + access_flags: 0, + superclass_idx: 0, + interfaces_off: 0, + source_file_idx: 0, + annotations_off: 0, + class_data_off: 0, + static_values_off: 0, + }, + call_site_ids: vec![], + section_manager: FragSectionManager::default(), + class_data: None, + type_lists_index: HashMap::new(), + type_lists_with_offset: vec![], + encoded_array_items: vec![], + method_handles: vec![], + code_items: vec![], + annotations_directory_items: vec![], + annotation_set_items: vec![], + annotation_items: vec![], + annotation_set_lists: vec![], + debug_info_items: vec![], + }; + frag.strings = class.get_all_strings().into_iter().collect(); + frag.strings.sort(); + let strings_index = frag + .strings + .iter() + .enumerate() + .map(|(idx, string)| (string.clone(), idx)) + .collect::>(); + frag.type_ids = class.get_all_types().into_iter().collect(); + frag.type_ids.sort(); + let types_index = frag + .type_ids + .iter() + .enumerate() + .map(|(idx, ty)| (ty.clone(), idx)) + .collect::>(); + frag.proto_ids = class.get_all_protos().into_iter().collect(); + frag.proto_ids.sort(); + let protos_index = frag + .proto_ids + .iter() + .enumerate() + .map(|(idx, proto)| (proto.clone(), idx)) + .collect::>(); + frag.field_ids = class.get_all_field_ids().into_iter().collect(); + frag.field_ids.sort(); + let fields_index = frag + .field_ids + .iter() + .enumerate() + .map(|(idx, field)| (field.clone(), idx)) + .collect::>(); + frag.method_ids = class.get_all_method_ids().into_iter().collect(); + frag.method_ids.sort(); + let methods_index = frag + .method_ids + .iter() + .enumerate() + .map(|(idx, method)| (method.clone(), idx)) + .collect::>(); + + frag.section_manager + .add_elt(FragSection::ClassDefItem, None); + Ok(frag) + } + + /// Insert a code_item. + /// + /// # Warning + /// + /// 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 { + self.class_defs + .get(&method_id.class_) + .unwrap() + .0 + .direct_methods + .get(&method_id) + .unwrap() + .code + .as_ref() + .unwrap() + .clone() + } else { + self.class_defs + .get(&method_id.class_) + .unwrap() + .0 + .virtual_methods + .get(&method_id) + .unwrap() + .code + .as_ref() + .unwrap() + .clone() + }; + // Estimate instructions addresses + let mut min_addr = 0; + let mut max_addr = 0; + let mut label_min_max_addrs: HashMap = HashMap::new(); + + for ins in &code.insns { + match ins { + Instruction::Label(Label { name }) => { + label_min_max_addrs.insert(name.clone(), (min_addr, max_addr)); + min_addr += ins.min_ins_size() / 2; + max_addr += ins.max_ins_size() / 2; + } + Instruction::ConstString(ins) => { + let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( + "String {} (found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let size = ins.get_raw_ins(*string_idx).size() / 2; + min_addr += size; + max_addr += size; + } + _ => { + min_addr += ins.min_ins_size() / 2; + max_addr += ins.max_ins_size() / 2; + } + } + } + // Compute instruction size and precise addresses + let mut addr = 0; + let mut label_addrs = HashMap::new(); + let mut goto_sizes = vec![]; + for ins in &code.insns { + match ins { + Instruction::Label(Label { name }) => { + label_addrs.insert(name.clone(), addr); + addr += ins.max_ins_size() / 2; + } + Instruction::ConstString(ins) => { + let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( + "String {} (found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + addr += ins.get_raw_ins(*string_idx).size() / 2; + } + Instruction::Goto(Goto { label }) => { + let (min_addr, max_addr) = label_min_max_addrs + .get(label) + .ok_or(anyhow!("Label {label} not found in label estimation map"))?; + let size = Goto::size_from_branch_offset_interval(addr, *min_addr, *max_addr)?; + goto_sizes.push(size); + addr += size / 2; + } + _ => addr += ins.ins_size()? / 2, + } + } + // Serialize instructions + let mut tries = vec![]; + let mut handlers = EncodedCatchHandlerList { list: vec![] }; + let mut handler_off = 0; + let mut insns = vec![]; + let mut payloads = vec![]; + let mut goto_idx = 0; + let mut payload_addr = addr; + if payload_addr % 2 != 0 { + payload_addr += 1; // For switch and array table alignment + } + addr = 0; + for ins in &code.insns { + match ins { + Instruction::ConstString(ins) => { + let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( + "String {} (found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*string_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::ConstClass(ins) => { + let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( + "Class {} (type of class found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(class_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::CheckCast(ins) => { + let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( + "Class {} (type of class found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(class_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InstanceOf(ins) => { + let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( + "Class {} (type of class found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(class_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::NewInstance(ins) => { + let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( + "Class {} (type of class found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(class_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::NewArray(ins) => { + let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( + "Type {} (type found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(class_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::FilledNewArray(ins) => { + let class_idx = *self.type_ids.get(&ins.type_).ok_or(anyhow!( + "Type {} (type found in code of {}) not found in dex builder", + ins.type_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(class_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::FillArrayData(ins) => { + let payload = InsFormat::FormatFillArrayDataPayload { + elt_width: ins.elt_width, + data: ins.data.clone(), + }; + if payload_addr % 2 != 0 { + // https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/method_verifier.cc;drc=e8c3e7be783937a340cd4f3280b69962d6f1ea0c;l=1347 + // The ART check if the array data table is 4 bytes aligned (= 2 ins alligned) + // TODO: check how it is donne in android and other dex generation code. + let nop = Instruction::Nop(Nop).get_raw_ins()?; + payload_addr += nop.size() / 2; + payloads.push(nop); + } + let data_offset = payload_addr as i32 - addr as i32; + payload_addr += payload.size() / 2; + payloads.push(payload); + let ins = ins.get_raw_ins(data_offset); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::Goto(ins) => { + let goto_size = goto_sizes[goto_idx]; + goto_idx += 1; + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found goto with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + let ins = ins.get_raw_ins(branch_offset, goto_size); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::Switch(ins) => { + let mut key_targets = vec![]; + for (key, label) in &ins.branches { + let label_addr = label_addrs.get(label).ok_or(anyhow!( + "Label {} not found in code of {}, but found goto with this label", + label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + key_targets.push((*key, branch_offset)); + } + key_targets.sort_by_key(|(key, _)| *key); + let payload = if ins.is_packed() { + let (first_key, _) = *key_targets.first().ok_or(anyhow!( + "Found empty swith in code of {}", + method_id.__repr__() + ))?; + let targets: Vec<_> = + key_targets.into_iter().map(|(_, target)| target).collect(); + InsFormat::FormatPackedSwitchPayload { first_key, targets } + } else { + InsFormat::FormatSparseSwitchPayload { key_targets } + }; + if payload_addr % 2 != 0 { + // https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/method_verifier.cc;drc=e8c3e7be783937a340cd4f3280b69962d6f1ea0c;l=1464 + // The ART check if the switch table is 4 bytes aligned (= 2 ins alligned) + // TODO: check how it is donne in android and other dex generation code. + let nop = Instruction::Nop(Nop).get_raw_ins()?; + payload_addr += nop.size() / 2; + payloads.push(nop); + } + let data_offset = payload_addr as i32 - addr as i32; + payload_addr += payload.size() / 2; + payloads.push(payload); + let ins = ins.get_raw_ins(data_offset); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfEq(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfNe(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfLt(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfGe(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfGt(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfLe(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfEqZ(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfNeZ(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfLtZ(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfGeZ(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfGtZ(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfLeZ(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGet(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGetWide(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGetObject(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGetBoolean(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGetByte(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGetChar(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGetShort(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPut(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPutWide(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPutObject(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPutBoolean(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPutByte(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPutChar(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPutShort(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGet(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGetWide(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGetObject(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGetBoolean(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGetByte(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGetChar(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGetShort(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPut(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPutWide(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPutObject(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPutBoolean(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPutByte(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPutChar(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPutShort(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokeVirtual(ins) => { + let meth = &ins.method; + let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( + "Method {} (method of class {}, found in code of {}) not found in dex builder", + meth.__repr__(), + meth.class_.__repr__(), + method_id.__repr__() + ))?; + debug_assert!( + *meth_idx <= u16::MAX as usize, + "methode id too big for invoke instruction" + ); + let meth_idx = *meth_idx as u16; + let ins = ins.get_raw_ins(meth_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokeSuper(ins) => { + let meth = &ins.method; + let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( + "Method {} (method of class {}, found in code of {}) not found in dex builder", + meth.__repr__(), + meth.class_.__repr__(), + method_id.__repr__() + ))?; + debug_assert!( + *meth_idx <= u16::MAX as usize, + "methode id too big for invoke instruction" + ); + let meth_idx = *meth_idx as u16; + let ins = ins.get_raw_ins(meth_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokeDirect(ins) => { + let meth = &ins.method; + let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( + "Method {} (method of class {}, found in code of {}) not found in dex builder", + meth.__repr__(), + meth.class_.__repr__(), + method_id.__repr__() + ))?; + debug_assert!( + *meth_idx <= u16::MAX as usize, + "methode id too big for invoke instruction" + ); + let meth_idx = *meth_idx as u16; + let ins = ins.get_raw_ins(meth_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokeStatic(ins) => { + let meth = &ins.method; + let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( + "Method {} (method of class {}, found in code of {}) not found in dex builder", + meth.__repr__(), + meth.class_.__repr__(), + method_id.__repr__() + ))?; + debug_assert!( + *meth_idx <= u16::MAX as usize, + "methode id too big for invoke instruction" + ); + let meth_idx = *meth_idx as u16; + let ins = ins.get_raw_ins(meth_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokeInterface(ins) => { + let meth = &ins.method; + let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( + "Method {} (method of class {}, found in code of {}) not found in dex builder", + meth.__repr__(), + meth.class_.__repr__(), + method_id.__repr__() + ))?; + debug_assert!( + *meth_idx <= u16::MAX as usize, + "methode id too big for invoke instruction" + ); + let meth_idx = *meth_idx as u16; + let ins = ins.get_raw_ins(meth_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokePolymorphic(ins) => { + let meth = &ins.method; + let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( + "Method {} (method of class {}, found in code of {}) not found in dex builder", + meth.__repr__(), + meth.class_.__repr__(), + method_id.__repr__() + ))?; + let proto_idx = self.proto_ids.get(&ins.proto).ok_or(anyhow!( + "Prototype {} (found in code of {}) not found in dex builder", + ins.proto.__repr__(), + method_id.__repr__() + ))?; + debug_assert!( + *meth_idx <= u16::MAX as usize, + "methode id too big for invoke instruction" + ); + debug_assert!( + *proto_idx <= u16::MAX as usize, + "proto id too big for invoke instruction" + ); + let meth_idx = *meth_idx as u16; + let proto_idx = *proto_idx as u16; + let ins = ins.get_raw_ins(meth_idx, proto_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokeCustom(ins) => { + let call_site_idx = self.call_site_ids.len(); + self.insert_call_site_item(&ins.call_site)?; + let ins = ins.get_raw_ins(call_site_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::ConstMethodHandle(ins) => { + let method_handle_idx = self.method_handles.len(); + self.insert_method_handle(&ins.handle)?; + let ins = ins.get_raw_ins(method_handle_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::ConstMethodType(ins) => { + let proto_idx = self.proto_ids.get(&ins.proto).ok_or(anyhow!( + "Prototype {} (found in code of {}) not found in dex builder", + ins.proto.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*proto_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::Try(try_) => { + let end_block_addr = *label_addrs.get(&try_.end_label).ok_or(anyhow!( + "Label {} not found in code of {}, but found try with this label", + &try_.end_label, + method_id.__repr__() + ))?; + if end_block_addr < addr { + bail!( + "Found end label of a try block before the try instruction in code of {}", + method_id.__repr__() + ) + } + let try_item = TryItem { + start_addr: addr as u32, + insn_count: (end_block_addr - addr) as u16, + handler_off: handler_off as u16, // will be ajusted once the size of the + // handler list object is known + }; + tries.push(try_item); + let mut catches = EncodedCatchHandler { + handlers: vec![], + catch_all_addr: None, + }; + for (ty, label) in &try_.handlers { + let type_idx = Uleb128(*self.type_ids.get(ty).ok_or(anyhow!( + "Could not found type {} captured by a try block in {}\ + in the dex builder", + ty.__repr__(), + method_id.__repr__() + ))? as u32); + let addr = Uleb128(*label_addrs.get(label).ok_or(anyhow!( + "Label {} not found in code of {}, but found try \ + with this label as catch for type {}", + &try_.end_label, + method_id.__repr__(), + ty.__repr__(), + ))? as u32); + catches + .handlers + .push(EncodedTypeAddrPair { type_idx, addr }); + } + if let Some(ref label) = try_.default_handler { + let catch_all_addr = *label_addrs.get(label).ok_or(anyhow!( + "Label {} not found in code of {}, but found try \ + with this label as catch all", + &try_.end_label, + method_id.__repr__() + ))?; + catches.catch_all_addr = Some(Uleb128(catch_all_addr as u32)); + } + handler_off += catches.size(); + handlers.list.push(catches); + } + Instruction::Label(_) => (), + _ => { + let ins = ins.get_raw_ins().with_context(|| { + format!( + "Failed to convert instruction {} (found in code of {}) to raw instruction", + ins.__repr__(), + method_id.__repr__() + ) + })?; + addr += ins.size() / 2; + insns.push(ins); + } + } + } + if addr % 2 != 0 { + // make sure the payload section is 4 bytes aligned + let nop = Instruction::Nop(Nop).get_raw_ins()?; + //addr += nop.size() / 2; + insns.push(nop); + } + insns.extend(payloads); + + for try_ in &mut tries { + try_.handler_off += handlers.size_field().size() as u16; + } + + let debug_info_off = if code.debug_info.1.is_empty() && code.parameter_names.is_none() { + 0 + } else { + let debug_info_off = self + .section_manager + .get_aligned_size(Section::DebugInfoItem); + let mut cursor = Cursor::new(code.debug_info.1); + let mut item = DebugInfoItem { + line_start: Uleb128(code.debug_info.0), + parameter_names: vec![], + bytecode: Vec::::deserialize(&mut cursor, DbgBytecode::EndSequence)?, + }; + if let Some(parameter_names) = code.parameter_names { + for name in ¶meter_names { + if let Some(name) = name { + item.parameter_names + .push(Uleb128p1(*self.strings.get(name).ok_or(anyhow!( + "String {} (name of param of {}) not found", + name.__str__(), + method_id.__repr__() + ))? as u32)); + } else { + item.parameter_names.push(NO_INDEX); + } + } + } + self.section_manager + .add_elt(Section::DebugInfoItem, Some(item.size())); + self.debug_info_items.push(item); + debug_info_off + 1 + }; + let handlers = if handlers.list.is_empty() { + None + } else { + Some(handlers) + }; + 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() + insns, + tries, + handlers, + }; + self.section_manager + .add_elt(Section::CodeItem, Some(item.size())); + self.code_items.push(item); + Ok(()) + } + + /// Insert annotation associated to a class. + /// + + /// Insert a class_data_item in the class_data section (in data). + /// + /// # Note + /// + /// code_item objects are 4 bytes aligns, so their offset cannot be odd. + /// + /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset + /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow + /// to distinguish the offset of the first item (equal to zero before linking) and the value + /// 0 used to indicate an abscence of item. + fn insert_class_data_item(&mut self, class_id: &IdType) -> Result<()> { + let mut data = ClassDataItem::default(); + let (class, _) = self.class_defs.get(class_id).unwrap(); + + let mut static_fields: Vec = class.static_fields.keys().cloned().collect(); + static_fields.sort(); + let mut last_field_id = 0; + for id in &static_fields { + let idx = self.field_ids.get(id).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex builder", + id.__repr__(), + class.__repr__() + ))?; + let field_idx_diff = Uleb128((idx - last_field_id) as u32); + last_field_id = *idx; + let access_flags = Uleb128(class.static_fields.get(id).unwrap().get_raw_access_flags()); + data.static_fields.push(EncodedField { + field_idx_diff, + access_flags, + }); + } + + let mut instance_fields: Vec = class.instance_fields.keys().cloned().collect(); + instance_fields.sort(); + let mut last_field_id = 0; + for id in &instance_fields { + let idx = self.field_ids.get(id).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex builder", + id.__repr__(), + class.__repr__() + ))?; + let field_idx_diff = Uleb128((idx - last_field_id) as u32); + last_field_id = *idx; + let access_flags = Uleb128( + class + .instance_fields + .get(id) + .unwrap() + .get_raw_access_flags(), + ); + data.instance_fields.push(EncodedField { + field_idx_diff, + access_flags, + }); + } + + let mut direct_methods: Vec = class.direct_methods.keys().cloned().collect(); + direct_methods.sort(); + let mut last_method_id = 0; + for id in &direct_methods { + // &mut vs & of self and class make things difficult... + let (class, _) = self.class_defs.get(class_id).unwrap(); + let idx = self.method_ids.get(id).ok_or(anyhow!( + "Method {} (method of class {}) not found in dex builder", + id.__repr__(), + class.__repr__() + ))?; + let method_idx_diff = Uleb128((idx - last_method_id) as u32); + last_method_id = *idx; + let access_flags = + Uleb128(class.direct_methods.get(id).unwrap().get_raw_access_flags()); + // No if let because ownership gunfooterie + let code_off = if class.direct_methods.get(id).unwrap().code.is_some() { + let code_off = self.section_manager.get_aligned_size(Section::CodeItem); + self.insert_code_item(id.clone(), true)?; + Uleb128(code_off + 1) + } else { + Uleb128(0) + }; + data.direct_methods.push(EncodedMethod { + method_idx_diff, + access_flags, + code_off, // Will be relinked once the offset of the code item section is known + }); + } + + let (class, _) = self.class_defs.get(class_id).unwrap(); + let mut virtual_methods: Vec = class.virtual_methods.keys().cloned().collect(); + virtual_methods.sort(); + let mut last_method_id = 0; + for id in &virtual_methods { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let idx = self.method_ids.get(id).ok_or(anyhow!( + "Method {} (method of class {}) not found in dex builder", + id.__repr__(), + class.__repr__() + ))?; + let method_idx_diff = Uleb128((idx - last_method_id) as u32); + last_method_id = *idx; + let access_flags = Uleb128( + class + .virtual_methods + .get(id) + .unwrap() + .get_raw_access_flags(), + ); + // No if let because ownership gunfooterie + let code_off = if class.virtual_methods.get(id).unwrap().code.is_some() { + let code_off = self.section_manager.get_aligned_size(Section::CodeItem); + self.insert_code_item(id.clone(), false)?; + Uleb128(code_off + 1) + } else { + Uleb128(0) + }; + data.virtual_methods.push(EncodedMethod { + method_idx_diff, + access_flags, + code_off, // Will be relinked once the offset of the code item section is known + }); + } + self.section_manager + .add_elt(Section::ClassDataItem, Some(data.size())); + //assert_eq!(data.size(), data.serialize_to_vec().unwrap().len()); + self.class_data_list.push(data); + Ok(()) + } + + /// Insert a [`MethodHandle`]. + pub fn insert_method_handle(&mut self, handle: &MethodHandle) -> Result<()> { + let (field_or_method_id, method_handle_type) = match handle { + MethodHandle::StaticPut(StaticPut(field)) => ( + *self.field_ids.get(field).ok_or(anyhow!( + "Field {} not found in dex writer", + field.__repr__() + ))? as u16, + MethodHandleType::StaticPut, + ), + MethodHandle::StaticGet(StaticGet(field)) => ( + *self.field_ids.get(field).ok_or(anyhow!( + "Field {} not found in dex writer", + field.__repr__() + ))? as u16, + MethodHandleType::StaticGet, + ), + MethodHandle::InstancePut(InstancePut(field)) => ( + *self.field_ids.get(field).ok_or(anyhow!( + "Field {} not found in dex writer", + field.__repr__() + ))? as u16, + MethodHandleType::InstancePut, + ), + MethodHandle::InstanceGet(InstanceGet(field)) => ( + *self.field_ids.get(field).ok_or(anyhow!( + "Field {} not found in dex writer", + field.__repr__() + ))? as u16, + MethodHandleType::InstanceGet, + ), + MethodHandle::InvokeStatic(InvokeStatic(meth)) => ( + *self.method_ids.get(meth).ok_or(anyhow!( + "Method {} not found in dex writer", + meth.__repr__() + ))? as u16, + MethodHandleType::InvokeStatic, + ), + MethodHandle::InvokeInstance(InvokeInstance(meth)) => ( + *self.method_ids.get(meth).ok_or(anyhow!( + "Method {} not found in dex writer", + meth.__repr__() + ))? as u16, + MethodHandleType::InvokeInstance, + ), + MethodHandle::InvokeConstructor(InvokeConstructor(meth)) => ( + *self.method_ids.get(meth).ok_or(anyhow!( + "Method {} not found in dex writer", + meth.__repr__() + ))? as u16, + MethodHandleType::InvokeConstructor, + ), + MethodHandle::InvokeDirect(InvokeDirect(meth)) => ( + *self.method_ids.get(meth).ok_or(anyhow!( + "Method {} not found in dex writer", + meth.__repr__() + ))? as u16, + MethodHandleType::InvokeDirect, + ), + MethodHandle::InvokeInterface(InvokeInterface(meth)) => ( + *self.method_ids.get(meth).ok_or(anyhow!( + "Method {} not found in dex writer", + meth.__repr__() + ))? as u16, + MethodHandleType::InvokeInterface, + ), + }; + self.method_handles.push(MethodHandleItem { + method_handle_type, + field_or_method_id, + unused1: 0, + unused2: 0, + }); + Ok(()) + } + + /// Convert a [`DexValue`] to an [`EncodedValue`]. + /// + /// # Warning + /// + /// This method can insert element in the dex file like method_handles. + pub fn dex_value_to_encoded_value(&mut self, value: &DexValue) -> Result { + match value { + DexValue::Byte(DexByte(val)) => Ok(EncodedValue::Byte(*val)), + DexValue::Short(DexShort(val)) => Ok(EncodedValue::Short(*val)), + DexValue::Char(DexChar(val)) => Ok(EncodedValue::Char(*val)), + DexValue::Int(DexInt(val)) => Ok(EncodedValue::Int(*val)), + DexValue::Long(DexLong(val)) => Ok(EncodedValue::Long(*val)), + DexValue::Float(DexFloat(val)) => Ok(EncodedValue::Float(*val)), + DexValue::Double(DexDouble(val)) => Ok(EncodedValue::Double(*val)), + DexValue::MethodType(val) => Ok(EncodedValue::MethodType( + *self.proto_ids.get(val).ok_or(anyhow!( + "Prototype {} not found in dex writer", + val.__repr__() + ))? as u32, + )), + DexValue::MethodHandle(val) => { + // TODO: move to a method + let idx = self.method_handles.len() as u32; + self.insert_method_handle(val)?; + Ok(EncodedValue::MethodHandle(idx)) + } + DexValue::String(val) => Ok(EncodedValue::String( + *self + .strings + .get(val) + .ok_or(anyhow!("String {} not found in dex writer", val.__repr__()))? + as u32, + )), + DexValue::Type(val) => Ok(EncodedValue::Type( + *self + .type_ids + .get(val) + .ok_or(anyhow!("Type {} not found in dex writer", val.__repr__()))? + as u32, + )), + DexValue::Field(val) => Ok(EncodedValue::Field( + *self + .field_ids + .get(val) + .ok_or(anyhow!("Field {} not found in dex writer", val.__repr__()))? + as u32, + )), + DexValue::Method(val) => Ok(EncodedValue::Method( + *self + .method_ids + .get(val) + .ok_or(anyhow!("Method {} not found in dex writer", val.__repr__()))? + as u32, + )), + DexValue::Enum(IdEnum(val)) => Ok(EncodedValue::Enum( + *self + .field_ids + .get(val) + .ok_or(anyhow!("Field {} not found in dex writer", val.__repr__()))? + as u32, + )), + DexValue::Array(DexArray(arr)) => { + let mut values = vec![]; + for val in arr { + values.push( + self.dex_value_to_encoded_value(val) + .context("Error while serializing a array")?, + ); + } + Ok(EncodedValue::Array(EncodedArray { values })) + } + DexValue::Annotation(val) => Ok(EncodedValue::Annotation( + self.dex_annotation_to_encoded_annotation(val.clone())?, + )), + DexValue::Null(DexNull) => Ok(EncodedValue::Null), + DexValue::Boolean(DexBoolean(val)) => Ok(EncodedValue::Boolean(*val)), + } + } + + /// Insert an encoded_array in the encoded_array_item section. + fn insert_encoded_array_item(&mut self, DexArray(array): DexArray) -> Result<()> { + let mut values = vec![]; + for value in array { + values.push(self.dex_value_to_encoded_value(&value)?); + } + let item = EncodedArrayItem { + value: EncodedArray { values }, + }; + self.section_manager + .add_elt(Section::EncodedArrayItem, Some(item.size())); + self.encoded_array_items.push(item); + Ok(()) + } + + /// Insert a [`CallSite`] to the encoded array items + /// + /// # Warning + /// + /// This method can insert element in the dex file like method_handles. + pub fn insert_call_site_item(&mut self, call_site: &CallSite) -> Result<()> { + let mut values = vec![]; + values.push(DexValue::MethodHandle(call_site.method_handle.clone())); + values.push(DexValue::String(call_site.name.clone())); + values.push(DexValue::MethodType(call_site.type_.clone())); + values.extend(call_site.args.iter().cloned()); + self.call_site_ids.push(CallSiteIdItem { + call_site_off: self + .section_manager + .get_aligned_size(Section::EncodedArrayItem), + }); // linked in link_call_site_ids() + self.section_manager.add_elt(Section::CallSiteIdItem, None); + self.insert_encoded_array_item(DexArray(values)) + } + + /// Insert the encoded_array_item encoding the static_values of a class. + fn insert_class_static_values(&mut self, class_id: &IdType) -> Result<()> { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let mut static_fields: Vec = class.static_fields.keys().cloned().collect(); + static_fields.sort(); + let mut array = vec![]; + let mut last_defined_field_index = 0; + for (idx, f) in static_fields.iter().enumerate() { + if class.static_fields.get(f).unwrap().value.is_some() { + last_defined_field_index = idx; + } + } + for f in &static_fields[..=last_defined_field_index] { + let field = class.static_fields.get(f).unwrap(); + if let Some(val) = field.value.as_ref() { + array.push(val.clone()); + } else { + array.push(field.descriptor.type_.get_default_value().ok_or(anyhow!( + "The type {} (for field {} in class {}) does not have a default value", + field.descriptor.type_.__repr__(), + field.descriptor.__repr__(), + class_id.__repr__() + ))?); + } + } + self.insert_encoded_array_item(DexArray(array)) + .with_context(|| { + format!( + "Failed to serialize static values of class {}", + class_id.__repr__() + ) + }) + } + + fn dex_annotation_to_encoded_annotation( + &mut self, + DexAnnotation { type_, elements }: DexAnnotation, + ) -> Result { + let mut encoded_elements = vec![]; + + let mut elements_names: Vec<_> = elements.keys().collect(); + elements_names.sort(); + + for name in elements_names { + let elt = elements.get(name).unwrap(); + encoded_elements.push(AnnotationElement { + name_idx: Uleb128(*self.strings.get(name).ok_or(anyhow!( + "{} (annotation element name) not found in dex builder", + name.__str__() + ))? as u32), + value: self.dex_value_to_encoded_value(elt)?, + }); + } + Ok(EncodedAnnotation { + type_idx: Uleb128(*self.type_ids.get(&type_).ok_or(anyhow!( + "Annotation type {} not found in dex builder", + type_.__repr__(), + ))? as u32), + elements: encoded_elements, + }) + } + + /// Insert the annnotations set for a class. + fn insert_class_annotation_set(&mut self, class_id: &IdType) -> Result<()> { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let mut annotations = class.annotations.clone(); + + let mut annotation_set = AnnotationSetItem { entries: vec![] }; + annotations.sort_by_key(|annot| annot.annotation.type_.clone()); + for annot in annotations { + annotation_set.entries.push(AnnotationOffItem { + annotation_off: self + .section_manager + .get_aligned_size(Section::AnnotationItem), + }); // linked in link_annotations() + + let item = AnnotationItem { + visibility: match ( + annot.visibility_build, + annot.visibility_runtime, + annot.visibility_system, + ) { + (true, false, false) => AnnotationVisibility::Build, + (false, true, false) => AnnotationVisibility::Runtime, + (false, false, true) => AnnotationVisibility::System, + _ => bail!( + "Annotation need visibility set to one and only one of build, \ + runtime or system" + ), // TODO: check if this is true + }, + annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, + }; + self.section_manager + .add_elt(Section::AnnotationItem, Some(item.size())); + self.annotation_items.push(item); + } + self.section_manager + .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); + self.annotation_set_items.push(annotation_set); + Ok(()) + } + + /// Insert the annnotations set for a class. + fn insert_field_annotation_set( + &mut self, + field_id: &IdField, + is_static_field: bool, + ) -> Result<()> { + let (class, _) = self.class_defs.get(&field_id.class_).unwrap(); + let field = if is_static_field { + class.static_fields.get(field_id).unwrap() + } else { + class.instance_fields.get(field_id).unwrap() + }; + let mut annotations = field.annotations.clone(); + + let mut annotation_set = AnnotationSetItem { entries: vec![] }; + annotations.sort_by_key(|annot| annot.annotation.type_.clone()); + for annot in annotations { + annotation_set.entries.push(AnnotationOffItem { + annotation_off: self + .section_manager + .get_aligned_size(Section::AnnotationItem), + }); // linked in link_annotations() + + let item = AnnotationItem { + visibility: match ( + annot.visibility_build, + annot.visibility_runtime, + annot.visibility_system, + ) { + (true, false, false) => AnnotationVisibility::Build, + (false, true, false) => AnnotationVisibility::Runtime, + (false, false, true) => AnnotationVisibility::System, + _ => bail!( + "Annotation need visibility set to one and only one of build, \ + runtime or system" + ), // TODO: check if this is true + }, + annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, + }; + self.section_manager + .add_elt(Section::AnnotationItem, Some(item.size())); + self.annotation_items.push(item); + } + self.section_manager + .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); + self.annotation_set_items.push(annotation_set); + Ok(()) + } + + /// Insert the annnotations set for a method (but not the parameters annotations). + fn insert_method_annotation_set( + &mut self, + method_id: &IdMethod, + is_direct_method: bool, + ) -> Result<()> { + let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); + let method = if is_direct_method { + class.direct_methods.get(method_id).unwrap() + } else { + class.virtual_methods.get(method_id).unwrap() + }; + let mut annotations = method.annotations.clone(); + + let mut annotation_set = AnnotationSetItem { entries: vec![] }; + annotations.sort_by_key(|annot| annot.annotation.type_.clone()); + for annot in annotations { + annotation_set.entries.push(AnnotationOffItem { + annotation_off: self + .section_manager + .get_aligned_size(Section::AnnotationItem), + }); // linked in link_annotations() + + let item = AnnotationItem { + visibility: match ( + annot.visibility_build, + annot.visibility_runtime, + annot.visibility_system, + ) { + (true, false, false) => AnnotationVisibility::Build, + (false, true, false) => AnnotationVisibility::Runtime, + (false, false, true) => AnnotationVisibility::System, + _ => bail!( + "Annotation need visibility set to one and only one of build, \ + runtime or system" + ), // TODO: check if this is true + }, + annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, + }; + self.section_manager + .add_elt(Section::AnnotationItem, Some(item.size())); + self.annotation_items.push(item); + } + self.section_manager + .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); + self.annotation_set_items.push(annotation_set); + Ok(()) + } + + /// Insert the annotations set for a method parameter. + fn insert_parameters_annotation_set( + &mut self, + method_id: &IdMethod, + is_direct_method: bool, + parameter_idx: usize, + ) -> Result<()> { + let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); + let method = if is_direct_method { + class.direct_methods.get(method_id).unwrap() + } else { + class.virtual_methods.get(method_id).unwrap() + }; + let mut annotations = method.parameters_annotations[parameter_idx].clone(); + + let mut annotation_set = AnnotationSetItem { entries: vec![] }; + annotations.sort_by_key(|annot| annot.annotation.type_.clone()); + for annot in annotations { + annotation_set.entries.push(AnnotationOffItem { + annotation_off: self + .section_manager + .get_aligned_size(Section::AnnotationItem), + }); // linked in link_annotations() + + let item = AnnotationItem { + visibility: match ( + annot.visibility_build, + annot.visibility_runtime, + annot.visibility_system, + ) { + (true, false, false) => AnnotationVisibility::Build, + (false, true, false) => AnnotationVisibility::Runtime, + (false, false, true) => AnnotationVisibility::System, + _ => bail!( + "Annotation need visibility set to one and only one of build, \ + runtime or system" + ), // TODO: check if this is true + }, + annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, + }; + self.section_manager + .add_elt(Section::AnnotationItem, Some(item.size())); + self.annotation_items.push(item); + } + self.section_manager + .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); + self.annotation_set_items.push(annotation_set); + Ok(()) + } + + /// Insert the annotations set list for a method parameters. + fn insert_parameters_annotation_set_list( + &mut self, + method_id: &IdMethod, + is_direct_method: bool, + ) -> Result<()> { + let mut list = AnnotationSetRefList { list: vec![] }; + let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); + let method = if is_direct_method { + class.direct_methods.get(method_id).unwrap() + } else { + class.virtual_methods.get(method_id).unwrap() + }; + let param_has_annotation: Vec<_> = method + .parameters_annotations + .iter() + .map(|annots| !annots.is_empty()) + .collect(); + for (param_idx, has_annotation) in param_has_annotation.into_iter().enumerate() { + list.list.push(AnnotationSetRefItem { + annotations_off: if has_annotation { + let annotation_off = self + .section_manager + .get_aligned_size(Section::AnnotationSetItem); + self.insert_parameters_annotation_set(method_id, is_direct_method, param_idx)?; + annotation_off + 1 + } else { + 0 + }, // linked in link_annotations() + }); + } + + self.section_manager + .add_elt(Section::AnnotationSetRefList, Some(list.size())); + self.annotation_set_lists.push(list); + Ok(()) + } + + /// Insert a class annotations (including field, methods and parameters annotations). + /// + /// # Note + /// + /// annotation_set_item objects are 4 bytes aligns, so their offset cannot be odd. + /// + /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset + /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow + /// to distinguish the offset of the first item (equal to zero before linking) and the value + /// 0 used to indicate an abscence of item. + fn insert_annotations(&mut self, class_id: &IdType) -> Result<()> { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let class_annotations_off = if !class.annotations.is_empty() { + let class_annotations_off = self + .section_manager + .get_aligned_size(Section::AnnotationSetItem); + self.insert_class_annotation_set(class_id) + .with_context(|| { + format!( + "Failed to insert class annotation for class {}", + class_id.__repr__() + ) + })?; + class_annotations_off + 1 + } else { + 0 + }; + + let mut field_ids = vec![]; + let (class, _) = self.class_defs.get(class_id).unwrap(); + field_ids.extend(class.static_fields.keys().cloned()); + field_ids.extend(class.instance_fields.keys().cloned()); + field_ids.sort(); + let mut field_annotations = vec![]; + for field_id in field_ids { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let static_field = class.static_fields.get(&field_id); + let instance_field = class.instance_fields.get(&field_id); + let (is_static, field) = match (static_field, instance_field) { + (Some(field), None) => (true, field), + (None, Some(field)) => (false, field), + _ => bail!( + "Unexpected configuration: field {} is both a static and a instance field in {}", + field_id.__repr__(), class_id.__repr__()), + }; + if !field.annotations.is_empty() { + let annotations_off = self + .section_manager + .get_aligned_size(Section::AnnotationSetItem) + + 1; + self.insert_field_annotation_set(&field_id, is_static)?; + field_annotations.push(FieldAnnotation { + field_idx: *self.field_ids.get(&field_id).ok_or(anyhow!( + "Field {} in {} not found in dex builder", + field_id.__repr__(), + class_id.__repr__(), + ))? as u32, + annotations_off, // linked in link_annotations() + }); + } + } + + let mut method_ids = vec![]; + let (class, _) = self.class_defs.get(class_id).unwrap(); + method_ids.extend(class.direct_methods.keys().cloned()); + method_ids.extend(class.virtual_methods.keys().cloned()); + method_ids.sort(); + let mut method_annotations = vec![]; + for method_id in &method_ids { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let direct_method = class.direct_methods.get(method_id); + let virtual_method = class.virtual_methods.get(method_id); + let (is_direct, method) = match (direct_method, virtual_method) { + (Some(method), None) => (true, method), + (None, Some(method)) => (false, method), + _ => bail!( + "Unexpected configuration: method {} is both a direct and a virtual method in {}", + method_id.__repr__(), class_id.__repr__()), + }; + if !method.annotations.is_empty() { + let annotations_off = self + .section_manager + .get_aligned_size(Section::AnnotationSetItem) + + 1; + self.insert_method_annotation_set(method_id, is_direct)?; + method_annotations.push(MethodAnnotation { + method_idx: *self.method_ids.get(method_id).ok_or(anyhow!( + "Method {} in {} not found in dex builder", + method_id.__repr__(), + class_id.__repr__(), + ))? as u32, + annotations_off, // linked in link_annotations() + }); + } + } + + let mut parameter_annotations = vec![]; + for method_id in method_ids { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let direct_method = class.direct_methods.get(&method_id); + let virtual_method = class.virtual_methods.get(&method_id); + let (is_direct, method) = match (direct_method, virtual_method) { + (Some(method), None) => (true, method), + (None, Some(method)) => (false, method), + _ => bail!( + "Unexpected configuration: method {} is both a direct and a virtual method in {}", + method_id.__repr__(), class_id.__repr__()), + }; + if !method.parameters_annotations.is_empty() { + let annotations_off = self + .section_manager + .get_aligned_size(Section::AnnotationSetRefList) + + 1; + self.insert_parameters_annotation_set_list(&method_id, is_direct)?; + parameter_annotations.push(ParameterAnnotation { + method_idx: *self.method_ids.get(&method_id).ok_or(anyhow!( + "Method {} in {} not found in dex builder", + method_id.__repr__(), + class_id.__repr__(), + ))? as u32, + annotations_off, // linked in link_annotations() + }); + } + } + + let item = AnnotationsDirectoryItem { + class_annotations_off, // linked in link_annotations() + field_annotations, + method_annotations, + parameter_annotations, + }; + self.section_manager + .add_elt(Section::AnnotationsDirectoryItem, Some(item.size())); + self.annotations_directory_items.push(item); + Ok(()) + } + + /// Insert a class_def_item in the class_defs section **and** the other struct that needs to be + /// generated on the fly. + /// + /// # Warning + /// + /// The class_defs section **MUST** be sorted by inheritance dependencies (parents classes and + /// interfaces must appear **before** child classes). Accordingly, this method must be invoked + /// in the right order. + /// + /// # Note + /// + /// annotations_directory_item, encoded_array_item and class_data_item objects are 4 bytes + /// aligns, so their offset cannot be odd. + /// + /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset + /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow + /// to distinguish the offset of the first item (equal to zero before linking) and the value + /// 0 used to indicate an abscence of item. + fn insert_class_def_item(&mut self, class_id: &IdType) -> Result<()> { + let idx = self.class_defs_list.len(); + self.class_defs + .entry(class_id.clone()) + .and_modify(|(_, i)| *i = idx); + let (class, _) = self.class_defs.get(class_id).unwrap(); + let class_data_off = if class.has_data_item() { + let class_data_off = self + .section_manager + .get_aligned_size(Section::ClassDataItem); + self.insert_class_data_item(class_id)?; + class_data_off + 1 + } else { + 0 + }; + // & vs &mut cluster-f, this make rust drop the ref so self hold by `class` before + // mutating self with `insert_class_data_item`, and get a new ref afterward + let (class, _) = self.class_defs.get(class_id).unwrap(); + let static_values_off = if class.has_static_values_array() { + let static_values_off = self + .section_manager + .get_aligned_size(Section::EncodedArrayItem); + self.insert_class_static_values(class_id)?; + static_values_off + 1 + } else { + 0 + }; + let (class, _) = self.class_defs.get(class_id).unwrap(); + let annotations_off = if class.has_annotations() { + let annotations_off = self + .section_manager + .get_aligned_size(Section::AnnotationsDirectoryItem); + self.insert_annotations(class_id)?; + annotations_off + 1 + } else { + 0 + }; + let (class, _) = self.class_defs.get(class_id).unwrap(); + self.class_defs_list.push(ClassDefItem { + class_idx: *self.type_ids.get(class_id).ok_or(anyhow!( + "Type {} (type of class {}) not found in dex builder", + class_id.__repr__(), + class.__repr__() + ))? as u32, + access_flags: class.get_raw_access_flags(), + superclass_idx: if let Some(sup) = &class.superclass { + *self.type_ids.get(sup).ok_or(anyhow!( + "Type {} (superclass of class {}) not found in dex builder", + sup.__repr__(), + class.__repr__() + ))? as u32 + } else { + NO_INDEX.0 + }, + interfaces_off: 0, + source_file_idx: if let Some(file) = &class.source_file { + *self.strings.get(file).ok_or(anyhow!( + "String {} (source file of class {}) not found in dex builder", + file.__repr__(), + class.__repr__() + ))? as u32 + } else { + NO_INDEX.0 + }, + + annotations_off, // need relinking once offset of class_def_item section is known + class_data_off, // need relinking once offset of class_data section is known + static_values_off, // need relinking once offset of encoded_array section is known + }); + self.section_manager.add_elt(Section::ClassDefItem, None); + Ok(()) + } + + fn gen_type_list_section(&mut self) -> Result<()> { + debug!("Generate the type_list section"); + // Collect all type lists + for proto in self.proto_ids.keys() { + if !proto.parameters.is_empty() { + let type_list = self.gen_type_list(&proto.parameters).with_context(|| { + format!("Failed to generate param list for {}", proto.__repr__()) + })?; + self.type_lists_index.insert(type_list, 0); + } + } + for (class, _) in self.class_defs.values() { + if !class.interfaces.is_empty() { + let type_list = self.gen_type_list(&class.interfaces).with_context(|| { + format!("Failed to generate interface list for {}", class.__repr__()) + })?; + self.type_lists_index.insert(type_list, 0); + } + } + + // safe type lists with their offset in the section + let mut offset = 0; + for (i, (list, idx)) in self.type_lists_index.iter_mut().enumerate() { + while offset % 4 != 0 { + // Alignment + self.section_manager.incr_section_size(Section::TypeList, 1); + offset += 1; + } + *idx = i; + self.type_lists_with_offset.push((list.clone(), offset)); + self.section_manager + .add_elt(Section::TypeList, Some(list.size())); + offset += list.size() as u32; + } + // The next section requires alignment to 4 + while offset % 4 != 0 { + // Alignment + self.section_manager.incr_section_size(Section::TypeList, 1); + offset += 1; + } + Ok(()) + } + + /// Generate the map list. + /// + /// # Warning + /// + /// All sections must be generated (but not linked) before generating the map list. + /// + /// This method switch the section manager from edit mode to read only. + fn gen_map_list(&mut self) -> Result<()> { + debug!("Generate the map_list"); + // Get the size of a map item + let map_item_size = 12; /* = MapItem { + type_: MapItemType::HeaderItem, + unused: 0, + size: 0, + offset: 0, + } + .size(); */ + // Empty map has a size 4, then we add the size of a MapItem for each element + // The size of the map_list must be computed before generating the map list, + // as it affect the offset of some sections. + self.section_manager.add_elt(Section::MapList, Some(4)); + for section in Section::VARIANT_LIST { + if !section.is_data() && self.section_manager.get_nb_elt(*section) != 0 { + self.section_manager + .incr_section_size(Section::MapList, map_item_size); + } + } + // All sections are knowns and should not be eddited anymore + self.section_manager.finalize_sections(); + for section in Section::VARIANT_LIST { + if !section.is_data() && self.section_manager.get_nb_elt(*section) != 0 { + /* + match section { + // Alignment + // Until Section::MapList included, the section are naturally alligned to 4 + _ => (), + } + */ + self.map_list.list.push(MapItem { + type_: section.get_map_item_type(), + unused: 0, + size: self.section_manager.get_nb_elt(*section) as u32, + offset: self.section_manager.get_offset(*section), + }); + } + } + Ok(()) + } + + /// Link the offsets in the header. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. + fn link_header(&mut self) { + debug!("Link the header section"); + self.header.map_off = self.section_manager.get_offset(Section::MapList); + self.header.string_ids_size = self.section_manager.get_nb_elt(Section::StringIdItem) as u32; + self.header.string_ids_off = self.section_manager.get_offset(Section::StringIdItem); + self.header.type_ids_size = self.section_manager.get_nb_elt(Section::TypeIdItem) as u32; + self.header.type_ids_off = self.section_manager.get_offset(Section::TypeIdItem); + self.header.proto_ids_size = self.section_manager.get_nb_elt(Section::ProtoIdItem) as u32; + self.header.proto_ids_off = self.section_manager.get_offset(Section::ProtoIdItem); + self.header.field_ids_size = self.section_manager.get_nb_elt(Section::FieldIdItem) as u32; + self.header.field_ids_off = self.section_manager.get_offset(Section::FieldIdItem); + self.header.method_ids_size = self.section_manager.get_nb_elt(Section::MethodIdItem) as u32; + self.header.method_ids_off = self.section_manager.get_offset(Section::MethodIdItem); + self.header.class_defs_size = self.section_manager.get_nb_elt(Section::ClassDefItem) as u32; + self.header.class_defs_off = self.section_manager.get_offset(Section::ClassDefItem); + self.header.data_size = self.section_manager.get_unaligned_size(Section::Data); + self.header.data_off = self.section_manager.get_offset(Section::Data); + } + + /// Link the offsets in the call site id items. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. + fn link_call_site_ids(&mut self) { + debug!("Link call site id items"); + for id in &mut self.call_site_ids { + id.call_site_off += self.section_manager.get_offset(Section::EncodedArrayItem); + } + } + + /// Link the offsets in class_def_items. + /// + /// # Warning + /// + /// This is the only link method called before generating the map list and finilizing the + /// section: + /// + /// Linking can only occur once all sections are entirelly generated, however, + /// `class_data_item.direct|virtual_methods[.].code_off` are Uleb128 encoded, meaning + /// that linking class_data_item modify the size of the class_data_items, hence the position + /// of the class_data_item and all element located after, as well as the size of the data + /// section. This is pretty bothersome and means that the sections **are** modified. + fn link_class_data(&mut self) -> Result<()> { + debug!("Link class data items"); + let mut unlinked_local_offset = 0; + let mut linked_local_offset = 0; + let code_section_off = self.section_manager.get_code_item_offset_prefinalized(); + for data in self.class_data_list.iter_mut() { + let unlinked_size = data.size() as u32; + for method in &mut data.direct_methods { + if method.code_off.0 != 0 { + method.code_off.0 += code_section_off - 1; + } + } + for method in &mut data.virtual_methods { + if method.code_off.0 != 0 { + method.code_off.0 += code_section_off - 1; + } + } + self.corrected_class_data_offset + .insert(unlinked_local_offset, linked_local_offset); + linked_local_offset += data.size() as u32; + unlinked_local_offset += unlinked_size; + } + self.section_manager.incr_section_size( + Section::ClassDataItem, + linked_local_offset as usize - unlinked_local_offset as usize, + ); + Ok(()) + } + + /// Link the offsets in proto_id_items. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. + fn link_proto_id(&mut self) -> Result<()> { + debug!("Link proto id items"); + for (proto, idx) in &self.proto_ids { + if !proto.parameters.is_empty() { + let type_list = self.gen_type_list(&proto.parameters).with_context(|| { + format!("Failed to generate param list for {}", proto.__repr__()) + })?; + let offset = self.section_manager.get_offset(Section::TypeList) + + self.type_lists_with_offset[*self.type_lists_index.get(&type_list).unwrap()] + .1; + self.proto_ids_list[*idx].parameters_off = offset; + } + } + Ok(()) + } + + /// Link the offsets of class_data_items in class_def_items. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. + fn link_class_def(&mut self) -> Result<()> { + debug!("Link class_def_items"); + for class_def in self.class_defs_list.iter_mut() { + // Link the class_data_item entries + // prelink value is set to offset in the section + 1 (to distinguish with 0) + if class_def.class_data_off != 0 { + let unlinked_local_offset = class_def.class_data_off - 1; + let linked_local_offset = *self + .corrected_class_data_offset + .get(&unlinked_local_offset) + .expect( + "Unlinked class_data_item offset not found in corrected_class_data_offset", + ); + class_def.class_data_off = + self.section_manager.get_offset(Section::ClassDataItem) + linked_local_offset; + } + // Link the annotations_directory_item entrie + // prelink value is set to offset in the section + 1 (to distinguish with 0) + if class_def.annotations_off != 0 { + class_def.annotations_off += self + .section_manager + .get_offset(Section::AnnotationsDirectoryItem) + - 1; + } + + // Link the static_values entries + if class_def.static_values_off != 0 { + class_def.static_values_off += + self.section_manager.get_offset(Section::EncodedArrayItem) - 1; + } + } + for (cls, idx) in self.class_defs.values() { + if !cls.interfaces.is_empty() { + let type_list = self.gen_type_list(&cls.interfaces).with_context(|| { + format!("Failed to generate interface list for {}", cls.__repr__()) + })?; + let offset = self.section_manager.get_offset(Section::TypeList) + + self.type_lists_with_offset[*self.type_lists_index.get(&type_list).unwrap()] + .1; + self.class_defs_list[*idx].interfaces_off = offset; + } + } + Ok(()) + } + + /// Link the offset of debug info item in code items. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. + fn link_code(&mut self) { + debug!("Link the debug_info_off entries in code_items"); + for code in self.code_items.iter_mut() { + if code.debug_info_off != 0 { + code.debug_info_off += self.section_manager.get_offset(Section::DebugInfoItem) - 1; + } + } + } + + /// Link all annotations objects. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. + fn link_annotations(&mut self) { + for annotation in self.annotations_directory_items.iter_mut() { + if annotation.class_annotations_off != 0 { + annotation.class_annotations_off += + self.section_manager.get_offset(Section::AnnotationSetItem) - 1; + } + for field_annotation in annotation.field_annotations.iter_mut() { + if field_annotation.annotations_off != 0 { + field_annotation.annotations_off += + self.section_manager.get_offset(Section::AnnotationSetItem) - 1; + } + } + for method_annotation in annotation.method_annotations.iter_mut() { + if method_annotation.annotations_off != 0 { + method_annotation.annotations_off += + self.section_manager.get_offset(Section::AnnotationSetItem) - 1; + } + } + for parameter_annotation in annotation.parameter_annotations.iter_mut() { + if parameter_annotation.annotations_off != 0 { + parameter_annotation.annotations_off += self + .section_manager + .get_offset(Section::AnnotationSetRefList) + - 1; + } + } + } + for annotation_set in self.annotation_set_items.iter_mut() { + for entry in annotation_set.entries.iter_mut() { + entry.annotation_off += self.section_manager.get_offset(Section::AnnotationItem); + } + } + for list in self.annotation_set_lists.iter_mut() { + for annotation in list.list.iter_mut() { + if annotation.annotations_off != 0 { + annotation.annotations_off += + self.section_manager.get_offset(Section::AnnotationSetItem) - 1; + } + } + } + } + + fn write_dex_file(&mut self, writer: &mut dyn Write) -> Result<()> { + self.section_manager.reset(); + self.section_manager.add_elt(Section::HeaderItem, None); + + self.gen_string_data_section()?; + self.gen_type_ids_section()?; + self.gen_proto_ids_section()?; + self.gen_field_ids_section()?; + self.gen_method_ids_section()?; + + debug!("Sort classes and generate the class_defs and class_data section"); + for class_id in self.get_sorted_class_def()? { + self.insert_class_def_item(&class_id)?; + } + self.gen_type_list_section()?; + + // start by linking class_data_items to populate self.corrected_class_data_offset + // and update the class_data_item sections size. + // Why before gen_map_list? Because the offsets in class_data_items are F***ing Uleb128 + // encoded, so there size change when linking (see doc of self.corrected_class_data_offset). + let code_offset = self.section_manager.get_code_item_offset_prefinalized(); + self.link_class_data()?; + self.gen_map_list()?; + assert_eq!( + code_offset, + self.section_manager.get_offset(Section::CodeItem), + "Prelinking computed value and post linking value for \ + the offset of the code_item section don't match" + ); + + // From now on, all section are generated and the value in section_manager do not change, + + self.link_header(); + self.link_call_site_ids(); + self.link_proto_id()?; + self.link_class_def()?; + self.link_code(); + self.link_annotations(); + + debug!("Serialize the dex file"); + let mut buffer = Cursor::new(Vec::::new()); + + self.check_section_offset(&buffer, Section::HeaderItem); + Self::fix_section_alignement(&mut buffer, Section::HeaderItem)?; + self.header.serialize(&mut buffer)?; + // StringIdItem section + let mut string_off = self.section_manager.get_offset(Section::StringDataItem); + self.check_section_offset(&buffer, Section::StringIdItem); + for string in self.string_data_list.iter() { + let str_id = StringIdItem { + string_data_off: string_off, + }; + Self::fix_section_alignement(&mut buffer, Section::StringIdItem)?; + str_id.serialize(&mut buffer)?; + string_off += string.size() as u32; + } + // TypeId section + self.check_section_offset(&buffer, Section::TypeIdItem); + for ty in &self.type_ids_list { + Self::fix_section_alignement(&mut buffer, Section::TypeIdItem)?; + ty.serialize(&mut buffer)?; + } + // ProtoId section + self.check_section_offset(&buffer, Section::ProtoIdItem); + for proto in &self.proto_ids_list { + Self::fix_section_alignement(&mut buffer, Section::ProtoIdItem)?; + proto.serialize(&mut buffer)?; + } + // FieldIdItem section + self.check_section_offset(&buffer, Section::FieldIdItem); + for field_id in &self.field_ids_list { + Self::fix_section_alignement(&mut buffer, Section::FieldIdItem)?; + field_id.serialize(&mut buffer)?; + } + // MethodIdItem section + self.check_section_offset(&buffer, Section::MethodIdItem); + for method_id in &self.method_ids_list { + Self::fix_section_alignement(&mut buffer, Section::MethodIdItem)?; + method_id.serialize(&mut buffer)?; + } + // ClassDefItem section + self.check_section_offset(&buffer, Section::ClassDefItem); + for class_def in &self.class_defs_list { + Self::fix_section_alignement(&mut buffer, Section::ClassDefItem)?; + class_def.serialize(&mut buffer)?; + } + // CallSiteIdItem, data are inserted as encoded array item later + self.check_section_offset(&buffer, Section::CallSiteIdItem); + for call_site_id in &self.call_site_ids { + Self::fix_section_alignement(&mut buffer, Section::CallSiteIdItem)?; + call_site_id.serialize(&mut buffer)?; + } + + // MethodHandleItem section + self.check_section_offset(&buffer, Section::MethodHandleItem); + for handle in &self.method_handles { + Self::fix_section_alignement(&mut buffer, Section::MethodHandleItem)?; + handle.serialize(&mut buffer)?; + } + // MapList + self.check_section_offset(&buffer, Section::Data); + self.check_section_offset(&buffer, Section::MapList); + Self::fix_section_alignement(&mut buffer, Section::MapList)?; + self.map_list.serialize(&mut buffer)?; + // TypeList, + self.check_section_offset(&buffer, Section::TypeList); + for (list, _) in &self.type_lists_with_offset { + Self::fix_section_alignement(&mut buffer, Section::TypeList)?; + list.serialize(&mut buffer)?; + } + // AnnotationSetRefList section + self.check_section_offset(&buffer, Section::AnnotationSetRefList); + for list in &self.annotation_set_lists { + Self::fix_section_alignement(&mut buffer, Section::AnnotationSetRefList)?; + list.serialize(&mut buffer)?; + } + // AnnotationSetItem section + self.check_section_offset(&buffer, Section::AnnotationSetItem); + for set in &self.annotation_set_items { + Self::fix_section_alignement(&mut buffer, Section::AnnotationSetItem)?; + set.serialize(&mut buffer)?; + } + // CodeItem section + self.check_section_offset(&buffer, Section::CodeItem); + for code_item in &self.code_items { + Self::fix_section_alignement(&mut buffer, Section::CodeItem)?; + code_item.serialize(&mut buffer)? + } + // StringDataItem section + self.check_section_offset(&buffer, Section::StringDataItem); + for string in &self.string_data_list { + Self::fix_section_alignement(&mut buffer, Section::StringDataItem)?; + string.serialize(&mut buffer)?; + } + // DebugInfoItem section + self.check_section_offset(&buffer, Section::DebugInfoItem); + for debug_info in &self.debug_info_items { + Self::fix_section_alignement(&mut buffer, Section::DebugInfoItem)?; + debug_info.serialize(&mut buffer)?; + } + // AnnotationItem section + self.check_section_offset(&buffer, Section::AnnotationItem); + for annot in &self.annotation_items { + Self::fix_section_alignement(&mut buffer, Section::AnnotationItem)?; + annot.serialize(&mut buffer)?; + } + // EncodedArrayItem section + self.check_section_offset(&buffer, Section::EncodedArrayItem); + for array in &self.encoded_array_items { + Self::fix_section_alignement(&mut buffer, Section::EncodedArrayItem)?; + array.serialize(&mut buffer)?; + } + // AnnotationsDirectoryItem section + self.check_section_offset(&buffer, Section::AnnotationsDirectoryItem); + for dir in &self.annotations_directory_items { + Self::fix_section_alignement(&mut buffer, Section::AnnotationsDirectoryItem)?; + dir.serialize(&mut buffer)?; + } + // ClassDataItem section + self.check_section_offset(&buffer, Section::ClassDataItem); + for data in &self.class_data_list { + Self::fix_section_alignement(&mut buffer, Section::ClassDataItem)?; + data.serialize(&mut buffer)?; + } + // TODO: HiddenapiClassDataItem, + /* + self.check_section_offset(&buffer, Section::HiddenapiClassDataItem); + Self::fix_section_alignement(&mut buffer, Section::HiddenapiClassDataItem)?; + */ + + let end_data = buffer.position(); + assert_eq!( + end_data as u32, + self.header.data_off + self.header.data_size + ); + + // compute signature + buffer.seek(SeekFrom::Start(8 + 4 + 20))?; + let mut hasher = Sha1::new(); + io::copy(&mut buffer, &mut hasher)?; + self.header.signature = hasher.finalize().into(); + let size = buffer.seek(SeekFrom::End(0))? as u32; + self.header.file_size = size; + buffer.rewind()?; + self.header.serialize(&mut buffer)?; + + // Compute checksum + //buffer.seek(SeekFrom::Start(8 + 4))?; + let mut adler = Adler32::new(); + adler.write_slice(&buffer.get_ref()[8 + 4..]); + self.header.checksum = adler.checksum(); + buffer.rewind()?; + self.header.serialize(&mut buffer)?; + + // copy buffer to output + buffer.rewind()?; + io::copy(&mut buffer, writer)?; + + Ok(()) + } + + /// Insert 0 to a buffer until the right alignment is reached for an element of the + /// given section. + fn fix_section_alignement(buffer: &mut Cursor>, section: Section) -> Result<()> { + while buffer.position() % section.get_item_alignment() as u64 != 0 { + Serializable::serialize(&0u8, buffer)?; + } + Ok(()) + } + + /// Check if a section + fn check_section_offset(&self, buffer: &Cursor, section: Section) { + let mut pos = buffer.position(); + while pos % section.get_item_alignment() as u64 != 0 { + pos += 1; + } + let expected = self.section_manager.get_offset(section) as u64; + assert_eq!( + pos, expected, + "Computed section offset and actual section offset do not match for section \ + {section:?}, expected 0x{expected:x}, found 0x{pos:x}" + ); + } + + /// Compute the order of the classes in the section `class_defs`. + /// Class definitions must be sorted so that a class's superclass and interfaces + /// are before the class. + fn get_sorted_class_def(&self) -> Result> { + // Use Kahn's algorithm + let mut graph: HashMap<&IdType, (HashSet<&IdType>, HashSet<&IdType>)> = HashMap::new(); + for (ty, (def, _)) in &self.class_defs { + let mut edges_to = HashSet::new(); + if let Some(sup) = def.superclass.as_ref() { + if self.class_defs.get(sup).is_some() { + edges_to.insert(sup); + } + } + for sup in &def.interfaces { + if self.class_defs.get(sup).is_some() { + edges_to.insert(sup); + } + } + for n_to in &edges_to { + let (from, _) = graph + .entry(n_to) + .or_insert((HashSet::new(), HashSet::new())); + from.insert(ty); + } + let (_, to) = graph.entry(ty).or_insert((HashSet::new(), HashSet::new())); + to.extend(edges_to); + } + + let mut sorted = vec![]; + let mut no_outgoing: VecDeque<&IdType> = VecDeque::new(); + no_outgoing.extend( + graph + .iter() + .filter(|(_, (_, to))| to.is_empty()) + .map(|(ty, _)| ty), + ); + if no_outgoing.is_empty() { + bail!("The class inheritance topoloy is either empty or cyclic"); + } + + while let Some(n) = no_outgoing.pop_front() { + sorted.push(n.clone()); + let (from, _) = graph.get(n).cloned().unwrap(); + for n_from in from { + graph.entry(n_from).and_modify(|(_, to)| _ = to.remove(n)); + let (_, to) = graph.get(n_from).unwrap(); + if to.is_empty() { + no_outgoing.push_back(n_from); + } + } + graph + .entry(n) + .and_modify(|(from, _)| *from = HashSet::new()); + } + for (_, (from, to)) in graph { + if !from.is_empty() || !to.is_empty() { + bail!("The class inheritance topology is cyclic"); + } + } + Ok(sorted) + } + + fn gen_type_list(&self, list: &[IdType]) -> Result { + let mut type_list = TypeList { list: vec![] }; + for ty in list { + type_list.list.push(TypeItem { + type_idx: *self.type_ids.get(ty).ok_or(anyhow!( + "Could not found type {} in dex builder", + ty.__repr__() + ))? as u16, + }); + } + Ok(type_list) + } + + pub fn gen_dex_file_to_vec(&mut self) -> Result> { + let mut output = Cursor::new(Vec::::new()); + self.write_dex_file(&mut output)?; + Ok(output.into_inner()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum FragSection { + ClassDefItem, + CallSiteIdItem, + MethodHandleItem, + TypeList, + AnnotationSetRefList, + AnnotationSetItem, + CodeItem, + StringDataItem, + DebugInfoItem, + AnnotationItem, + EncodedArrayItem, + AnnotationsDirectoryItem, + ClassDataItem, + // HiddenapiClassDataItem, +} + +impl FragSection { + const VARIANT_LIST: &'static [Self] = &[ + Self::ClassDefItem, + Self::CallSiteIdItem, + Self::MethodHandleItem, + Self::TypeList, + Self::AnnotationSetRefList, + Self::AnnotationSetItem, + Self::CodeItem, + Self::StringDataItem, + Self::DebugInfoItem, + Self::AnnotationItem, + Self::EncodedArrayItem, + Self::AnnotationsDirectoryItem, + // must be last because contains offsets in Uleb, + // so size change when linking ! + Self::ClassDataItem, + // Self::HiddenapiClassDataItem, + ]; + + fn get_index(&self) -> usize { + match self { + Self::ClassDefItem => 0, + Self::CallSiteIdItem => 1, + Self::MethodHandleItem => 2, + Self::TypeList => 3, + Self::AnnotationSetRefList => 4, + Self::AnnotationSetItem => 5, + Self::CodeItem => 6, + Self::StringDataItem => 7, + Self::DebugInfoItem => 8, + Self::AnnotationItem => 9, + Self::EncodedArrayItem => 10, + Self::AnnotationsDirectoryItem => 11, + Self::ClassDataItem => 12, + // Self::HiddenapiClassDataItem => 13, + } + } + + fn get_elt_size(&self, default_size: Option) -> usize { + let fixed_size = match self { + Self::ClassDefItem => Some(0x20), + Self::CallSiteIdItem => Some(4), + Self::MethodHandleItem => Some(8), + Self::TypeList => None, + Self::AnnotationSetRefList => None, + Self::AnnotationSetItem => None, + Self::CodeItem => None, + Self::StringDataItem => None, + Self::DebugInfoItem => None, + Self::AnnotationItem => None, + Self::EncodedArrayItem => None, + Self::AnnotationsDirectoryItem => None, + Self::ClassDataItem => None, + //Self::HiddenapiClassDataItem => None, + }; + if let (Some(fixed_size), Some(default_size)) = (fixed_size, default_size) { + if fixed_size == default_size { + default_size + } else { + panic!( + "Element in {:?} have a size of {}, not {}", + self, fixed_size, default_size + ) + } + } else { + fixed_size.or(default_size).unwrap_or_else(|| { + panic!( + "Element of {:?} don't have a fixed size, you need to provide one", + self + ) + }) + } + } + + /// Return the alignment of the item in byte. + fn get_item_alignment(&self) -> u32 { + match self { + Self::ClassDefItem => 4, + Self::CallSiteIdItem => 1, + Self::MethodHandleItem => 4, + Self::TypeList => 4, + Self::AnnotationSetRefList => 4, + Self::AnnotationSetItem => 4, + Self::CodeItem => 4, + Self::StringDataItem => 1, + Self::DebugInfoItem => 1, + Self::AnnotationItem => 1, + Self::EncodedArrayItem => 1, + Self::AnnotationsDirectoryItem => 4, + Self::ClassDataItem => 1, + //Self::HiddenapiClassDataItem => 1, + } + } +} + +#[derive(Debug, Default, Clone)] +struct FragSectionManager { + sizes: [u32; Self::NB_SECTION], + nb_elt: [usize; Self::NB_SECTION], + offsets: [u32; Self::NB_SECTION], + editable: bool, +} + +impl FragSectionManager { + const NB_SECTION: usize = 12; + + fn reset(&mut self) { + self.sizes = [0; Self::NB_SECTION]; + self.nb_elt = [0; Self::NB_SECTION]; + self.offsets = [0; Self::NB_SECTION]; + self.editable = true; + } + + fn add_elt(&mut self, section: FragSection, size: Option) { + if !self.editable { + panic!("Try to modify a section when the sections are set to read only"); + } + if (section == FragSection::ClassDefItem || section == FragSection::ClassDataItem) + && (self.nb_elt[section.get_index()] >= 1) + { + panic!("{section:#?} cannot contain more than one element in a dex fragment"); + } + while self.sizes[section.get_index()] % section.get_item_alignment() != 0 { + self.sizes[section.get_index()] += 1; + } + self.sizes[section.get_index()] += section.get_elt_size(size) as u32; + self.nb_elt[section.get_index()] += 1; + } + /* + fn incr_section_size(&mut self, section: FragSection, size: usize) { + if !self.editable { + panic!("Try to modify a section when the sections are set to read only"); + } + self.sizes[section.get_index()] += size as u32; + } + fn get_offset(&self, section: FragSection) -> u32 { + if self.editable { + panic!("Try to get section offset before sections are finilized"); + } + let size = self.offsets[section.get_index()]; + let alignment = section.get_item_alignment(); + if size % alignment != 0 { + panic!( + "section {section:?} should be aligned on {alignment} bytes, \ + found section offset 0x{size:x}" + ); // avoid by finilized + } + size + } + */ + + fn get_unaligned_size(&self, section: FragSection) -> u32 { + self.sizes[section.get_index()] + } + + /// The position of a potential new item in the section considering alignment. + fn get_aligned_size(&self, section: FragSection) -> u32 { + let mut size = self.get_unaligned_size(section); + while size % section.get_item_alignment() != 0 { + size += 1; + } + size + } + + fn get_nb_elt(&self, section: FragSection) -> usize { + self.nb_elt[section.get_index()] + } + + /* + /// Finialize the sections: switch to read only and fix the section alignment. + fn finalize_sections(&mut self) { + for section in Section::VARIANT_LIST { + while self.sizes[..section.get_index()].iter().sum::() + % section.get_item_alignment() + != 0 + { + self.incr_section_size( + section.prev().expect( + "First section (Header) should alway be aligned but \ + found unaligned section without predecessor", + ), + 1, + ); + } + } + let mut offset = 0; + for section in Section::VARIANT_LIST { + self.offsets[section.get_index()] = offset; + offset += self.sizes[section.get_index()]; + } + + self.editable = false; + } + + /// This method exist for the only purpose of linking the method code offset inside + /// the class data items. This linking needs to be done before finilizing because it change the + /// size of the class data item section. + /// + /// Seriously, avoid using this. + fn get_code_item_offset_prefinalized(&mut self) -> u32 { + if !self.editable || self.get_nb_elt(Section::MapList) != 0 { + panic!("Don't use this method for other purpose than linking class_data_items"); + } + let mut map_list_size = 4; + let map_item_size = 12; /* = MapItem { + type_: MapItemType::HeaderItem, + unused: 0, + size: 0, + offset: 0, + } + .size(); */ + for section in Section::VARIANT_LIST { + if !section.is_data() + && (self.get_nb_elt(*section) != 0 || section == &Section::MapList) + { + map_list_size += map_item_size; + } + } + let mut offset = map_list_size; // This is aligned so it wont affect alignment + for section in &Section::VARIANT_LIST[..Section::CodeItem.get_index()] { + // size Section::Data and size Section::MapList are 0 + while offset % section.get_item_alignment() != 0 { + offset += 1; + } + offset += self.sizes[section.get_index()]; + } + + offset + } + */ + + /// Display the sections informations. + #[allow(dead_code)] + fn show(&self) { + let mut offset = 0; + for section in FragSection::VARIANT_LIST { + let size = self.get_unaligned_size(*section); + let new_offset = offset + size; + let nb_elt = self.get_nb_elt(*section); + println!( + "{section:?}: 0x{offset:x} -> 0x{new_offset:x} (size: 0x{size:x}, \ + nb elt: {nb_elt})" + ); + } + } +} diff --git a/androscalpel/src/lib.rs b/androscalpel/src/lib.rs index 3ece94a..e45a1c2 100644 --- a/androscalpel/src/lib.rs +++ b/androscalpel/src/lib.rs @@ -6,6 +6,7 @@ pub mod annotation; pub mod apk; pub mod class; pub mod code; +pub mod dex_fragment; pub mod dex_id; pub mod dex_string; pub mod dex_writer; From 7e63a523d7ccaaa4d110c4fd67750975f31feffc Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Mon, 11 Mar 2024 17:12:59 +0100 Subject: [PATCH 02/14] WIP: gen class def in frag --- androscalpel/src/dex_fragment.rs | 152 ++++++++++++------------------- 1 file changed, 57 insertions(+), 95 deletions(-) diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs index ed45a38..a506221 100644 --- a/androscalpel/src/dex_fragment.rs +++ b/androscalpel/src/dex_fragment.rs @@ -54,8 +54,8 @@ pub struct DexFragment { type_lists_with_offset: Vec<(TypeList, u32)>, /// The encoded_array_items section. encoded_array_items: Vec, - /// The annotations_directory_item section. - annotations_directory_items: Vec, + /// The annotations_directory_item. + annotations_directory_items: Option, /// The annotation_set_item section. annotation_set_items: Vec, /// The annotation item section. @@ -68,6 +68,8 @@ pub struct DexFragment { code_items: Vec, /// The debug info items section. debug_info_items: Vec, + /// The list of interfaces of the class. + interfaces: Vec, } impl DexFragment { @@ -101,11 +103,12 @@ impl DexFragment { encoded_array_items: vec![], method_handles: vec![], code_items: vec![], - annotations_directory_items: vec![], + annotations_directory_items: None, annotation_set_items: vec![], annotation_items: vec![], annotation_set_lists: vec![], debug_info_items: vec![], + interfaces: class.interfaces.clone(), }; frag.strings = class.get_all_strings().into_iter().collect(); frag.strings.sort(); @@ -150,6 +153,54 @@ impl DexFragment { frag.section_manager .add_elt(FragSection::ClassDefItem, None); + + frag.class_def.class_idx = *types_index.get(&class.descriptor).ok_or(anyhow!( + "Type {} (type of class {}) not found in type list", + class.descriptor.__repr__(), + class.__repr__() + ))? as u32; + frag.class_def.access_flags = class.get_raw_access_flags(); + frag.class_def.superclass_idx = if let Some(sup) = &class.superclass { + *types_index.get(sup).ok_or(anyhow!( + "Type {} (superclass of class {}) not found in dex builder", + sup.__repr__(), + class.__repr__() + ))? as u32 + } else { + NO_INDEX.0 + }; + frag.class_def.interfaces_off = 0; // set when linking + frag.class_def.source_file_idx = if let Some(file) = &class.source_file { + *strings_index.get(file).ok_or(anyhow!( + "String {} (source file of class {}) not found in dex builder", + file.__repr__(), + class.__repr__() + ))? as u32 + } else { + NO_INDEX.0 + }; + frag.class_def.annotations_off = if class.has_annotations() { + // TODO: frag.insert_annotations(class)?; + 1 + } else { + 0 + }; + frag.class_def.class_data_off = if class.has_data_item() { + //TODO: frag.insert_class_data_item(class_id)?; + 1 + } else { + 0 + }; + frag.class_def.static_values_off = if class.has_static_values_array() { + let static_values_off = frag + .section_manager + .get_aligned_size(FragSection::EncodedArrayItem); + // TODO: frag.insert_class_static_values(class)?; + static_values_off + 1 + } else { + 0 + }; + Ok(frag) } @@ -1947,97 +1998,6 @@ impl DexFragment { Ok(()) } - /// Insert a class_def_item in the class_defs section **and** the other struct that needs to be - /// generated on the fly. - /// - /// # Warning - /// - /// The class_defs section **MUST** be sorted by inheritance dependencies (parents classes and - /// interfaces must appear **before** child classes). Accordingly, this method must be invoked - /// in the right order. - /// - /// # Note - /// - /// annotations_directory_item, encoded_array_item and class_data_item objects are 4 bytes - /// aligns, so their offset cannot be odd. - /// - /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset - /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow - /// to distinguish the offset of the first item (equal to zero before linking) and the value - /// 0 used to indicate an abscence of item. - fn insert_class_def_item(&mut self, class_id: &IdType) -> Result<()> { - let idx = self.class_defs_list.len(); - self.class_defs - .entry(class_id.clone()) - .and_modify(|(_, i)| *i = idx); - let (class, _) = self.class_defs.get(class_id).unwrap(); - let class_data_off = if class.has_data_item() { - let class_data_off = self - .section_manager - .get_aligned_size(Section::ClassDataItem); - self.insert_class_data_item(class_id)?; - class_data_off + 1 - } else { - 0 - }; - // & vs &mut cluster-f, this make rust drop the ref so self hold by `class` before - // mutating self with `insert_class_data_item`, and get a new ref afterward - let (class, _) = self.class_defs.get(class_id).unwrap(); - let static_values_off = if class.has_static_values_array() { - let static_values_off = self - .section_manager - .get_aligned_size(Section::EncodedArrayItem); - self.insert_class_static_values(class_id)?; - static_values_off + 1 - } else { - 0 - }; - let (class, _) = self.class_defs.get(class_id).unwrap(); - let annotations_off = if class.has_annotations() { - let annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationsDirectoryItem); - self.insert_annotations(class_id)?; - annotations_off + 1 - } else { - 0 - }; - let (class, _) = self.class_defs.get(class_id).unwrap(); - self.class_defs_list.push(ClassDefItem { - class_idx: *self.type_ids.get(class_id).ok_or(anyhow!( - "Type {} (type of class {}) not found in dex builder", - class_id.__repr__(), - class.__repr__() - ))? as u32, - access_flags: class.get_raw_access_flags(), - superclass_idx: if let Some(sup) = &class.superclass { - *self.type_ids.get(sup).ok_or(anyhow!( - "Type {} (superclass of class {}) not found in dex builder", - sup.__repr__(), - class.__repr__() - ))? as u32 - } else { - NO_INDEX.0 - }, - interfaces_off: 0, - source_file_idx: if let Some(file) = &class.source_file { - *self.strings.get(file).ok_or(anyhow!( - "String {} (source file of class {}) not found in dex builder", - file.__repr__(), - class.__repr__() - ))? as u32 - } else { - NO_INDEX.0 - }, - - annotations_off, // need relinking once offset of class_def_item section is known - class_data_off, // need relinking once offset of class_data section is known - static_values_off, // need relinking once offset of encoded_array section is known - }); - self.section_manager.add_elt(Section::ClassDefItem, None); - Ok(()) - } - fn gen_type_list_section(&mut self) -> Result<()> { debug!("Generate the type_list section"); // Collect all type lists @@ -2781,7 +2741,9 @@ impl FragSectionManager { if !self.editable { panic!("Try to modify a section when the sections are set to read only"); } - if (section == FragSection::ClassDefItem || section == FragSection::ClassDataItem) + if (section == FragSection::ClassDefItem + || section == FragSection::ClassDataItem + || section == FragSection::AnnotationsDirectoryItem) && (self.nb_elt[section.get_index()] >= 1) { panic!("{section:#?} cannot contain more than one element in a dex fragment"); From bfa957ad457a9cc32c7b316385a7146eba22c143 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Tue, 12 Mar 2024 15:10:54 +0100 Subject: [PATCH 03/14] add insert_class_annotation_set to frag --- androscalpel/src/dex_fragment.rs | 817 ++++++++++++++++--------------- androscalpel/src/dex_writer.rs | 1 + 2 files changed, 413 insertions(+), 405 deletions(-) diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs index a506221..e5ddb9f 100644 --- a/androscalpel/src/dex_fragment.rs +++ b/androscalpel/src/dex_fragment.rs @@ -156,15 +156,15 @@ impl DexFragment { frag.class_def.class_idx = *types_index.get(&class.descriptor).ok_or(anyhow!( "Type {} (type of class {}) not found in type list", - class.descriptor.__repr__(), - class.__repr__() + class.descriptor.__str__(), + class.__str__() ))? as u32; frag.class_def.access_flags = class.get_raw_access_flags(); frag.class_def.superclass_idx = if let Some(sup) = &class.superclass { *types_index.get(sup).ok_or(anyhow!( "Type {} (superclass of class {}) not found in dex builder", - sup.__repr__(), - class.__repr__() + sup.__str__(), + class.__str__() ))? as u32 } else { NO_INDEX.0 @@ -173,14 +173,14 @@ impl DexFragment { frag.class_def.source_file_idx = if let Some(file) = &class.source_file { *strings_index.get(file).ok_or(anyhow!( "String {} (source file of class {}) not found in dex builder", - file.__repr__(), - class.__repr__() + file.__str__(), + class.__str__() ))? as u32 } else { NO_INDEX.0 }; frag.class_def.annotations_off = if class.has_annotations() { - // TODO: frag.insert_annotations(class)?; + frag.insert_annotations(class)?; 1 } else { 0 @@ -204,6 +204,181 @@ impl DexFragment { Ok(frag) } + /// Insert a class annotations (including field, methods and parameters annotations). + /// + /// # Note + /// + /// annotation_set_item objects are 4 bytes aligns, so their offset cannot be odd. + /// + /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset + /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow + /// to distinguish the offset of the first item (equal to zero before linking) and the value + /// 0 used to indicate an abscence of item. + fn insert_annotations(&mut self, class: &Class) -> Result<()> { + let class_annotations_off = if !class.annotations.is_empty() { + let class_annotations_off = self + .section_manager + .get_aligned_size(FragSection::AnnotationSetItem); + self.insert_class_annotation_set(class).with_context(|| { + format!( + "Failed to insert class annotation for class {}", + class.__str__() + ) + })?; + class_annotations_off + 1 + } else { + 0 + }; + + let mut field_ids = vec![]; + let (class, _) = self.class_defs.get(class_id).unwrap(); + field_ids.extend(class.static_fields.keys().cloned()); + field_ids.extend(class.instance_fields.keys().cloned()); + field_ids.sort(); + let mut field_annotations = vec![]; + for field_id in field_ids { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let static_field = class.static_fields.get(&field_id); + let instance_field = class.instance_fields.get(&field_id); + let (is_static, field) = match (static_field, instance_field) { + (Some(field), None) => (true, field), + (None, Some(field)) => (false, field), + _ => bail!( + "Unexpected configuration: field {} is both a static and a instance field in {}", + field_id.__str__(), class_id.__str__()), + }; + if !field.annotations.is_empty() { + let annotations_off = self + .section_manager + .get_aligned_size(Section::AnnotationSetItem) + + 1; + self.insert_field_annotation_set(&field_id, is_static)?; + field_annotations.push(FieldAnnotation { + field_idx: *self.field_ids.get(&field_id).ok_or(anyhow!( + "Field {} in {} not found in dex builder", + field_id.__str__(), + class_id.__str__(), + ))? as u32, + annotations_off, // linked in link_annotations() + }); + } + } + + let mut method_ids = vec![]; + let (class, _) = self.class_defs.get(class_id).unwrap(); + method_ids.extend(class.direct_methods.keys().cloned()); + method_ids.extend(class.virtual_methods.keys().cloned()); + method_ids.sort(); + let mut method_annotations = vec![]; + for method_id in &method_ids { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let direct_method = class.direct_methods.get(method_id); + let virtual_method = class.virtual_methods.get(method_id); + let (is_direct, method) = match (direct_method, virtual_method) { + (Some(method), None) => (true, method), + (None, Some(method)) => (false, method), + _ => bail!( + "Unexpected configuration: method {} is both a direct and a virtual method in {}", + method_id.__str__(), class_id.__str__()), + }; + if !method.annotations.is_empty() { + let annotations_off = self + .section_manager + .get_aligned_size(Section::AnnotationSetItem) + + 1; + self.insert_method_annotation_set(method_id, is_direct)?; + method_annotations.push(MethodAnnotation { + method_idx: *self.method_ids.get(method_id).ok_or(anyhow!( + "Method {} in {} not found in dex builder", + method_id.__str__(), + class_id.__str__(), + ))? as u32, + annotations_off, // linked in link_annotations() + }); + } + } + + let mut parameter_annotations = vec![]; + for method_id in method_ids { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let direct_method = class.direct_methods.get(&method_id); + let virtual_method = class.virtual_methods.get(&method_id); + let (is_direct, method) = match (direct_method, virtual_method) { + (Some(method), None) => (true, method), + (None, Some(method)) => (false, method), + _ => bail!( + "Unexpected configuration: method {} is both a direct and a virtual method in {}", + method_id.__str__(), class_id.__str__()), + }; + if !method.parameters_annotations.is_empty() { + let annotations_off = self + .section_manager + .get_aligned_size(Section::AnnotationSetRefList) + + 1; + self.insert_parameters_annotation_set_list(&method_id, is_direct)?; + parameter_annotations.push(ParameterAnnotation { + method_idx: *self.method_ids.get(&method_id).ok_or(anyhow!( + "Method {} in {} not found in dex builder", + method_id.__str__(), + class_id.__str__(), + ))? as u32, + annotations_off, // linked in link_annotations() + }); + } + } + + let item = AnnotationsDirectoryItem { + class_annotations_off, // linked in link_annotations() + field_annotations, + method_annotations, + parameter_annotations, + }; + self.section_manager + .add_elt(FragSection::AnnotationsDirectoryItem, Some(item.size())); + self.annotations_directory_items.push(item); + Ok(()) + } + + /// Insert the annnotations set for a class. + fn insert_class_annotation_set(&mut self, class: &Class) -> Result<()> { + let mut annotations = class.annotations.clone(); + + let mut annotation_set = AnnotationSetItem { entries: vec![] }; + annotations.sort_by_key(|annot| annot.annotation.type_.clone()); + for annot in annotations { + annotation_set.entries.push(AnnotationOffItem { + annotation_off: self + .section_manager + .get_aligned_size(FragSection::AnnotationItem) + + 1, + }); + + let item = AnnotationItem { + visibility: match ( + annot.visibility_build, + annot.visibility_runtime, + annot.visibility_system, + ) { + (true, false, false) => AnnotationVisibility::Build, + (false, true, false) => AnnotationVisibility::Runtime, + (false, false, true) => AnnotationVisibility::System, + _ => bail!( + "Annotation need visibility set to one and only one of build, \ + runtime or system" + ), // TODO: check if this is true + }, + annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, + }; + self.section_manager + .add_elt(FragSection::AnnotationItem, Some(item.size())); + self.annotation_items.push(item); + } + self.section_manager + .add_elt(FragSection::AnnotationSetItem, Some(annotation_set.size())); + self.annotation_set_items.push(annotation_set); + Ok(()) + } + /// Insert a code_item. /// /// # Warning @@ -250,8 +425,8 @@ impl DexFragment { Instruction::ConstString(ins) => { let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( "String {} (found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() + ins.lit.__str__(), + method_id.__str__() ))?; let size = ins.get_raw_ins(*string_idx).size() / 2; min_addr += size; @@ -276,8 +451,8 @@ impl DexFragment { Instruction::ConstString(ins) => { let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( "String {} (found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() + ins.lit.__str__(), + method_id.__str__() ))?; addr += ins.get_raw_ins(*string_idx).size() / 2; } @@ -309,8 +484,8 @@ impl DexFragment { Instruction::ConstString(ins) => { let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( "String {} (found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() + ins.lit.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*string_idx); addr += ins.size() / 2; @@ -319,8 +494,8 @@ impl DexFragment { Instruction::ConstClass(ins) => { let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( "Class {} (type of class found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() + ins.lit.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(class_idx); addr += ins.size() / 2; @@ -329,8 +504,8 @@ impl DexFragment { Instruction::CheckCast(ins) => { let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( "Class {} (type of class found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() + ins.lit.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(class_idx); addr += ins.size() / 2; @@ -339,8 +514,8 @@ impl DexFragment { Instruction::InstanceOf(ins) => { let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( "Class {} (type of class found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() + ins.lit.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(class_idx); addr += ins.size() / 2; @@ -349,8 +524,8 @@ impl DexFragment { Instruction::NewInstance(ins) => { let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( "Class {} (type of class found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() + ins.lit.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(class_idx); addr += ins.size() / 2; @@ -359,8 +534,8 @@ impl DexFragment { Instruction::NewArray(ins) => { let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( "Type {} (type found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() + ins.lit.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(class_idx); addr += ins.size() / 2; @@ -369,8 +544,8 @@ impl DexFragment { Instruction::FilledNewArray(ins) => { let class_idx = *self.type_ids.get(&ins.type_).ok_or(anyhow!( "Type {} (type found in code of {}) not found in dex builder", - ins.type_.__repr__(), - method_id.__repr__() + ins.type_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(class_idx); addr += ins.size() / 2; @@ -402,7 +577,7 @@ impl DexFragment { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( "Label {} not found in code of {}, but found goto with this label", ins.label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; let ins = ins.get_raw_ins(branch_offset, goto_size); @@ -415,7 +590,7 @@ impl DexFragment { let label_addr = label_addrs.get(label).ok_or(anyhow!( "Label {} not found in code of {}, but found goto with this label", label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; key_targets.push((*key, branch_offset)); @@ -424,7 +599,7 @@ impl DexFragment { let payload = if ins.is_packed() { let (first_key, _) = *key_targets.first().ok_or(anyhow!( "Found empty swith in code of {}", - method_id.__repr__() + method_id.__str__() ))?; let targets: Vec<_> = key_targets.into_iter().map(|(_, target)| target).collect(); @@ -451,13 +626,13 @@ impl DexFragment { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( "Label {} not found in code of {}, but found if with this label", ins.label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { bail!( "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() + method_id.__str__() ); } let ins = ins.get_raw_ins(branch_offset as i16); @@ -468,13 +643,13 @@ impl DexFragment { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( "Label {} not found in code of {}, but found if with this label", ins.label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { bail!( "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() + method_id.__str__() ); } let ins = ins.get_raw_ins(branch_offset as i16); @@ -485,13 +660,13 @@ impl DexFragment { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( "Label {} not found in code of {}, but found if with this label", ins.label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { bail!( "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() + method_id.__str__() ); } let ins = ins.get_raw_ins(branch_offset as i16); @@ -502,13 +677,13 @@ impl DexFragment { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( "Label {} not found in code of {}, but found if with this label", ins.label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { bail!( "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() + method_id.__str__() ); } let ins = ins.get_raw_ins(branch_offset as i16); @@ -519,13 +694,13 @@ impl DexFragment { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( "Label {} not found in code of {}, but found if with this label", ins.label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { bail!( "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() + method_id.__str__() ); } let ins = ins.get_raw_ins(branch_offset as i16); @@ -536,13 +711,13 @@ impl DexFragment { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( "Label {} not found in code of {}, but found if with this label", ins.label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { bail!( "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() + method_id.__str__() ); } let ins = ins.get_raw_ins(branch_offset as i16); @@ -553,13 +728,13 @@ impl DexFragment { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( "Label {} not found in code of {}, but found if with this label", ins.label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { bail!( "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() + method_id.__str__() ); } let ins = ins.get_raw_ins(branch_offset as i16); @@ -570,13 +745,13 @@ impl DexFragment { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( "Label {} not found in code of {}, but found if with this label", ins.label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { bail!( "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() + method_id.__str__() ); } let ins = ins.get_raw_ins(branch_offset as i16); @@ -587,13 +762,13 @@ impl DexFragment { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( "Label {} not found in code of {}, but found if with this label", ins.label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { bail!( "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() + method_id.__str__() ); } let ins = ins.get_raw_ins(branch_offset as i16); @@ -604,13 +779,13 @@ impl DexFragment { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( "Label {} not found in code of {}, but found if with this label", ins.label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { bail!( "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() + method_id.__str__() ); } let ins = ins.get_raw_ins(branch_offset as i16); @@ -621,13 +796,13 @@ impl DexFragment { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( "Label {} not found in code of {}, but found if with this label", ins.label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { bail!( "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() + method_id.__str__() ); } let ins = ins.get_raw_ins(branch_offset as i16); @@ -638,13 +813,13 @@ impl DexFragment { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( "Label {} not found in code of {}, but found if with this label", ins.label, - method_id.__repr__() + method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { bail!( "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() + method_id.__str__() ); } let ins = ins.get_raw_ins(branch_offset as i16); @@ -655,9 +830,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -667,9 +842,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -679,9 +854,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -691,9 +866,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -703,9 +878,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -715,9 +890,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -727,9 +902,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -739,9 +914,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -751,9 +926,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -763,9 +938,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -775,9 +950,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -787,9 +962,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -799,9 +974,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -811,9 +986,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -823,9 +998,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -835,9 +1010,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -847,9 +1022,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -859,9 +1034,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -871,9 +1046,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -883,9 +1058,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -895,9 +1070,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -907,9 +1082,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -919,9 +1094,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -931,9 +1106,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -943,9 +1118,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -955,9 +1130,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -967,9 +1142,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -979,9 +1154,9 @@ impl DexFragment { let field = &ins.field; let field_idx = self.field_ids.get(field).ok_or(anyhow!( "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() + field.__str__(), + field.class_.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -991,9 +1166,9 @@ impl DexFragment { let meth = &ins.method; let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() + meth.__str__(), + meth.class_.__str__(), + method_id.__str__() ))?; debug_assert!( *meth_idx <= u16::MAX as usize, @@ -1008,9 +1183,9 @@ impl DexFragment { let meth = &ins.method; let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() + meth.__str__(), + meth.class_.__str__(), + method_id.__str__() ))?; debug_assert!( *meth_idx <= u16::MAX as usize, @@ -1025,9 +1200,9 @@ impl DexFragment { let meth = &ins.method; let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() + meth.__str__(), + meth.class_.__str__(), + method_id.__str__() ))?; debug_assert!( *meth_idx <= u16::MAX as usize, @@ -1042,9 +1217,9 @@ impl DexFragment { let meth = &ins.method; let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() + meth.__str__(), + meth.class_.__str__(), + method_id.__str__() ))?; debug_assert!( *meth_idx <= u16::MAX as usize, @@ -1059,9 +1234,9 @@ impl DexFragment { let meth = &ins.method; let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() + meth.__str__(), + meth.class_.__str__(), + method_id.__str__() ))?; debug_assert!( *meth_idx <= u16::MAX as usize, @@ -1076,14 +1251,14 @@ impl DexFragment { let meth = &ins.method; let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() + meth.__str__(), + meth.class_.__str__(), + method_id.__str__() ))?; let proto_idx = self.proto_ids.get(&ins.proto).ok_or(anyhow!( "Prototype {} (found in code of {}) not found in dex builder", - ins.proto.__repr__(), - method_id.__repr__() + ins.proto.__str__(), + method_id.__str__() ))?; debug_assert!( *meth_idx <= u16::MAX as usize, @@ -1116,8 +1291,8 @@ impl DexFragment { Instruction::ConstMethodType(ins) => { let proto_idx = self.proto_ids.get(&ins.proto).ok_or(anyhow!( "Prototype {} (found in code of {}) not found in dex builder", - ins.proto.__repr__(), - method_id.__repr__() + ins.proto.__str__(), + method_id.__str__() ))?; let ins = ins.get_raw_ins(*proto_idx); addr += ins.size() / 2; @@ -1127,12 +1302,12 @@ impl DexFragment { let end_block_addr = *label_addrs.get(&try_.end_label).ok_or(anyhow!( "Label {} not found in code of {}, but found try with this label", &try_.end_label, - method_id.__repr__() + method_id.__str__() ))?; if end_block_addr < addr { bail!( "Found end label of a try block before the try instruction in code of {}", - method_id.__repr__() + method_id.__str__() ) } let try_item = TryItem { @@ -1150,15 +1325,15 @@ impl DexFragment { let type_idx = Uleb128(*self.type_ids.get(ty).ok_or(anyhow!( "Could not found type {} captured by a try block in {}\ in the dex builder", - ty.__repr__(), - method_id.__repr__() + ty.__str__(), + method_id.__str__() ))? as u32); let addr = Uleb128(*label_addrs.get(label).ok_or(anyhow!( "Label {} not found in code of {}, but found try \ with this label as catch for type {}", &try_.end_label, - method_id.__repr__(), - ty.__repr__(), + method_id.__str__(), + ty.__str__(), ))? as u32); catches .handlers @@ -1169,7 +1344,7 @@ impl DexFragment { "Label {} not found in code of {}, but found try \ with this label as catch all", &try_.end_label, - method_id.__repr__() + method_id.__str__() ))?; catches.catch_all_addr = Some(Uleb128(catch_all_addr as u32)); } @@ -1181,8 +1356,8 @@ impl DexFragment { let ins = ins.get_raw_ins().with_context(|| { format!( "Failed to convert instruction {} (found in code of {}) to raw instruction", - ins.__repr__(), - method_id.__repr__() + ins.__str__(), + method_id.__str__() ) })?; addr += ins.size() / 2; @@ -1221,7 +1396,7 @@ impl DexFragment { .push(Uleb128p1(*self.strings.get(name).ok_or(anyhow!( "String {} (name of param of {}) not found", name.__str__(), - method_id.__repr__() + method_id.__str__() ))? as u32)); } else { item.parameter_names.push(NO_INDEX); @@ -1276,8 +1451,8 @@ impl DexFragment { for id in &static_fields { let idx = self.field_ids.get(id).ok_or(anyhow!( "Field {} (field of class {}) not found in dex builder", - id.__repr__(), - class.__repr__() + id.__str__(), + class.__str__() ))?; let field_idx_diff = Uleb128((idx - last_field_id) as u32); last_field_id = *idx; @@ -1294,8 +1469,8 @@ impl DexFragment { for id in &instance_fields { let idx = self.field_ids.get(id).ok_or(anyhow!( "Field {} (field of class {}) not found in dex builder", - id.__repr__(), - class.__repr__() + id.__str__(), + class.__str__() ))?; let field_idx_diff = Uleb128((idx - last_field_id) as u32); last_field_id = *idx; @@ -1320,8 +1495,8 @@ impl DexFragment { let (class, _) = self.class_defs.get(class_id).unwrap(); let idx = self.method_ids.get(id).ok_or(anyhow!( "Method {} (method of class {}) not found in dex builder", - id.__repr__(), - class.__repr__() + id.__str__(), + class.__str__() ))?; let method_idx_diff = Uleb128((idx - last_method_id) as u32); last_method_id = *idx; @@ -1350,8 +1525,8 @@ impl DexFragment { let (class, _) = self.class_defs.get(class_id).unwrap(); let idx = self.method_ids.get(id).ok_or(anyhow!( "Method {} (method of class {}) not found in dex builder", - id.__repr__(), - class.__repr__() + id.__str__(), + class.__str__() ))?; let method_idx_diff = Uleb128((idx - last_method_id) as u32); last_method_id = *idx; @@ -1387,66 +1562,75 @@ impl DexFragment { pub fn insert_method_handle(&mut self, handle: &MethodHandle) -> Result<()> { let (field_or_method_id, method_handle_type) = match handle { MethodHandle::StaticPut(StaticPut(field)) => ( - *self.field_ids.get(field).ok_or(anyhow!( - "Field {} not found in dex writer", - field.__repr__() - ))? as u16, + *self + .field_ids + .get(field) + .ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))? + as u16, MethodHandleType::StaticPut, ), MethodHandle::StaticGet(StaticGet(field)) => ( - *self.field_ids.get(field).ok_or(anyhow!( - "Field {} not found in dex writer", - field.__repr__() - ))? as u16, + *self + .field_ids + .get(field) + .ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))? + as u16, MethodHandleType::StaticGet, ), MethodHandle::InstancePut(InstancePut(field)) => ( - *self.field_ids.get(field).ok_or(anyhow!( - "Field {} not found in dex writer", - field.__repr__() - ))? as u16, + *self + .field_ids + .get(field) + .ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))? + as u16, MethodHandleType::InstancePut, ), MethodHandle::InstanceGet(InstanceGet(field)) => ( - *self.field_ids.get(field).ok_or(anyhow!( - "Field {} not found in dex writer", - field.__repr__() - ))? as u16, + *self + .field_ids + .get(field) + .ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))? + as u16, MethodHandleType::InstanceGet, ), MethodHandle::InvokeStatic(InvokeStatic(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, + *self + .method_ids + .get(meth) + .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? + as u16, MethodHandleType::InvokeStatic, ), MethodHandle::InvokeInstance(InvokeInstance(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, + *self + .method_ids + .get(meth) + .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? + as u16, MethodHandleType::InvokeInstance, ), MethodHandle::InvokeConstructor(InvokeConstructor(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, + *self + .method_ids + .get(meth) + .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? + as u16, MethodHandleType::InvokeConstructor, ), MethodHandle::InvokeDirect(InvokeDirect(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, + *self + .method_ids + .get(meth) + .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? + as u16, MethodHandleType::InvokeDirect, ), MethodHandle::InvokeInterface(InvokeInterface(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, + *self + .method_ids + .get(meth) + .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? + as u16, MethodHandleType::InvokeInterface, ), }; @@ -1476,7 +1660,7 @@ impl DexFragment { DexValue::MethodType(val) => Ok(EncodedValue::MethodType( *self.proto_ids.get(val).ok_or(anyhow!( "Prototype {} not found in dex writer", - val.__repr__() + val.__str__() ))? as u32, )), DexValue::MethodHandle(val) => { @@ -1489,35 +1673,35 @@ impl DexFragment { *self .strings .get(val) - .ok_or(anyhow!("String {} not found in dex writer", val.__repr__()))? + .ok_or(anyhow!("String {} not found in dex writer", val.__str__()))? as u32, )), DexValue::Type(val) => Ok(EncodedValue::Type( *self .type_ids .get(val) - .ok_or(anyhow!("Type {} not found in dex writer", val.__repr__()))? + .ok_or(anyhow!("Type {} not found in dex writer", val.__str__()))? as u32, )), DexValue::Field(val) => Ok(EncodedValue::Field( *self .field_ids .get(val) - .ok_or(anyhow!("Field {} not found in dex writer", val.__repr__()))? + .ok_or(anyhow!("Field {} not found in dex writer", val.__str__()))? as u32, )), DexValue::Method(val) => Ok(EncodedValue::Method( *self .method_ids .get(val) - .ok_or(anyhow!("Method {} not found in dex writer", val.__repr__()))? + .ok_or(anyhow!("Method {} not found in dex writer", val.__str__()))? as u32, )), DexValue::Enum(IdEnum(val)) => Ok(EncodedValue::Enum( *self .field_ids .get(val) - .ok_or(anyhow!("Field {} not found in dex writer", val.__repr__()))? + .ok_or(anyhow!("Field {} not found in dex writer", val.__str__()))? as u32, )), DexValue::Array(DexArray(arr)) => { @@ -1592,9 +1776,9 @@ impl DexFragment { } else { array.push(field.descriptor.type_.get_default_value().ok_or(anyhow!( "The type {} (for field {} in class {}) does not have a default value", - field.descriptor.type_.__repr__(), - field.descriptor.__repr__(), - class_id.__repr__() + field.descriptor.type_.__str__(), + field.descriptor.__str__(), + class_id.__str__() ))?); } } @@ -1602,7 +1786,7 @@ impl DexFragment { .with_context(|| { format!( "Failed to serialize static values of class {}", - class_id.__repr__() + class_id.__str__() ) }) } @@ -1629,52 +1813,12 @@ impl DexFragment { Ok(EncodedAnnotation { type_idx: Uleb128(*self.type_ids.get(&type_).ok_or(anyhow!( "Annotation type {} not found in dex builder", - type_.__repr__(), + type_.__str__(), ))? as u32), elements: encoded_elements, }) } - /// Insert the annnotations set for a class. - fn insert_class_annotation_set(&mut self, class_id: &IdType) -> Result<()> { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let mut annotations = class.annotations.clone(); - - let mut annotation_set = AnnotationSetItem { entries: vec![] }; - annotations.sort_by_key(|annot| annot.annotation.type_.clone()); - for annot in annotations { - annotation_set.entries.push(AnnotationOffItem { - annotation_off: self - .section_manager - .get_aligned_size(Section::AnnotationItem), - }); // linked in link_annotations() - - let item = AnnotationItem { - visibility: match ( - annot.visibility_build, - annot.visibility_runtime, - annot.visibility_system, - ) { - (true, false, false) => AnnotationVisibility::Build, - (false, true, false) => AnnotationVisibility::Runtime, - (false, false, true) => AnnotationVisibility::System, - _ => bail!( - "Annotation need visibility set to one and only one of build, \ - runtime or system" - ), // TODO: check if this is true - }, - annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, - }; - self.section_manager - .add_elt(Section::AnnotationItem, Some(item.size())); - self.annotation_items.push(item); - } - self.section_manager - .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); - self.annotation_set_items.push(annotation_set); - Ok(()) - } - /// Insert the annnotations set for a class. fn insert_field_annotation_set( &mut self, @@ -1861,150 +2005,13 @@ impl DexFragment { Ok(()) } - /// Insert a class annotations (including field, methods and parameters annotations). - /// - /// # Note - /// - /// annotation_set_item objects are 4 bytes aligns, so their offset cannot be odd. - /// - /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset - /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow - /// to distinguish the offset of the first item (equal to zero before linking) and the value - /// 0 used to indicate an abscence of item. - fn insert_annotations(&mut self, class_id: &IdType) -> Result<()> { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let class_annotations_off = if !class.annotations.is_empty() { - let class_annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetItem); - self.insert_class_annotation_set(class_id) - .with_context(|| { - format!( - "Failed to insert class annotation for class {}", - class_id.__repr__() - ) - })?; - class_annotations_off + 1 - } else { - 0 - }; - - let mut field_ids = vec![]; - let (class, _) = self.class_defs.get(class_id).unwrap(); - field_ids.extend(class.static_fields.keys().cloned()); - field_ids.extend(class.instance_fields.keys().cloned()); - field_ids.sort(); - let mut field_annotations = vec![]; - for field_id in field_ids { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let static_field = class.static_fields.get(&field_id); - let instance_field = class.instance_fields.get(&field_id); - let (is_static, field) = match (static_field, instance_field) { - (Some(field), None) => (true, field), - (None, Some(field)) => (false, field), - _ => bail!( - "Unexpected configuration: field {} is both a static and a instance field in {}", - field_id.__repr__(), class_id.__repr__()), - }; - if !field.annotations.is_empty() { - let annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetItem) - + 1; - self.insert_field_annotation_set(&field_id, is_static)?; - field_annotations.push(FieldAnnotation { - field_idx: *self.field_ids.get(&field_id).ok_or(anyhow!( - "Field {} in {} not found in dex builder", - field_id.__repr__(), - class_id.__repr__(), - ))? as u32, - annotations_off, // linked in link_annotations() - }); - } - } - - let mut method_ids = vec![]; - let (class, _) = self.class_defs.get(class_id).unwrap(); - method_ids.extend(class.direct_methods.keys().cloned()); - method_ids.extend(class.virtual_methods.keys().cloned()); - method_ids.sort(); - let mut method_annotations = vec![]; - for method_id in &method_ids { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let direct_method = class.direct_methods.get(method_id); - let virtual_method = class.virtual_methods.get(method_id); - let (is_direct, method) = match (direct_method, virtual_method) { - (Some(method), None) => (true, method), - (None, Some(method)) => (false, method), - _ => bail!( - "Unexpected configuration: method {} is both a direct and a virtual method in {}", - method_id.__repr__(), class_id.__repr__()), - }; - if !method.annotations.is_empty() { - let annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetItem) - + 1; - self.insert_method_annotation_set(method_id, is_direct)?; - method_annotations.push(MethodAnnotation { - method_idx: *self.method_ids.get(method_id).ok_or(anyhow!( - "Method {} in {} not found in dex builder", - method_id.__repr__(), - class_id.__repr__(), - ))? as u32, - annotations_off, // linked in link_annotations() - }); - } - } - - let mut parameter_annotations = vec![]; - for method_id in method_ids { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let direct_method = class.direct_methods.get(&method_id); - let virtual_method = class.virtual_methods.get(&method_id); - let (is_direct, method) = match (direct_method, virtual_method) { - (Some(method), None) => (true, method), - (None, Some(method)) => (false, method), - _ => bail!( - "Unexpected configuration: method {} is both a direct and a virtual method in {}", - method_id.__repr__(), class_id.__repr__()), - }; - if !method.parameters_annotations.is_empty() { - let annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetRefList) - + 1; - self.insert_parameters_annotation_set_list(&method_id, is_direct)?; - parameter_annotations.push(ParameterAnnotation { - method_idx: *self.method_ids.get(&method_id).ok_or(anyhow!( - "Method {} in {} not found in dex builder", - method_id.__repr__(), - class_id.__repr__(), - ))? as u32, - annotations_off, // linked in link_annotations() - }); - } - } - - let item = AnnotationsDirectoryItem { - class_annotations_off, // linked in link_annotations() - field_annotations, - method_annotations, - parameter_annotations, - }; - self.section_manager - .add_elt(Section::AnnotationsDirectoryItem, Some(item.size())); - self.annotations_directory_items.push(item); - Ok(()) - } - fn gen_type_list_section(&mut self) -> Result<()> { debug!("Generate the type_list section"); // Collect all type lists for proto in self.proto_ids.keys() { if !proto.parameters.is_empty() { let type_list = self.gen_type_list(&proto.parameters).with_context(|| { - format!("Failed to generate param list for {}", proto.__repr__()) + format!("Failed to generate param list for {}", proto.__str__()) })?; self.type_lists_index.insert(type_list, 0); } @@ -2012,7 +2019,7 @@ impl DexFragment { for (class, _) in self.class_defs.values() { if !class.interfaces.is_empty() { let type_list = self.gen_type_list(&class.interfaces).with_context(|| { - format!("Failed to generate interface list for {}", class.__repr__()) + format!("Failed to generate interface list for {}", class.__str__()) })?; self.type_lists_index.insert(type_list, 0); } @@ -2177,7 +2184,7 @@ impl DexFragment { for (proto, idx) in &self.proto_ids { if !proto.parameters.is_empty() { let type_list = self.gen_type_list(&proto.parameters).with_context(|| { - format!("Failed to generate param list for {}", proto.__repr__()) + format!("Failed to generate param list for {}", proto.__str__()) })?; let offset = self.section_manager.get_offset(Section::TypeList) + self.type_lists_with_offset[*self.type_lists_index.get(&type_list).unwrap()] @@ -2227,7 +2234,7 @@ impl DexFragment { for (cls, idx) in self.class_defs.values() { if !cls.interfaces.is_empty() { let type_list = self.gen_type_list(&cls.interfaces).with_context(|| { - format!("Failed to generate interface list for {}", cls.__repr__()) + format!("Failed to generate interface list for {}", cls.__str__()) })?; let offset = self.section_manager.get_offset(Section::TypeList) + self.type_lists_with_offset[*self.type_lists_index.get(&type_list).unwrap()] @@ -2591,7 +2598,7 @@ impl DexFragment { type_list.list.push(TypeItem { type_idx: *self.type_ids.get(ty).ok_or(anyhow!( "Could not found type {} in dex builder", - ty.__repr__() + ty.__str__() ))? as u16, }); } diff --git a/androscalpel/src/dex_writer.rs b/androscalpel/src/dex_writer.rs index a54c3f9..f967970 100644 --- a/androscalpel/src/dex_writer.rs +++ b/androscalpel/src/dex_writer.rs @@ -2568,6 +2568,7 @@ impl DexWriter { for annotation_set in self.annotation_set_items.iter_mut() { for entry in annotation_set.entries.iter_mut() { entry.annotation_off += self.section_manager.get_offset(Section::AnnotationItem); + // -1 now when linking frag } } for list in self.annotation_set_lists.iter_mut() { From 3a2f45b28ba1bdbf9b302dfbbba6844c2d3e90d9 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Wed, 13 Mar 2024 17:16:44 +0100 Subject: [PATCH 04/14] insert annotation in frag --- TODO.md | 5 +- androscalpel/src/dex_fragment.rs | 525 +++++++++++++++---------------- 2 files changed, 262 insertions(+), 268 deletions(-) diff --git a/TODO.md b/TODO.md index 940aa4b..392a652 100644 --- a/TODO.md +++ b/TODO.md @@ -5,4 +5,7 @@ - no nop when no payload - option to get label at every code addresses - name register / parameters -- ord in python + +# frag meth +- insert_class_data_item +- insert_class_static_values diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs index e5ddb9f..6a0cabe 100644 --- a/androscalpel/src/dex_fragment.rs +++ b/androscalpel/src/dex_fragment.rs @@ -151,17 +151,25 @@ impl DexFragment { .map(|(idx, method)| (method.clone(), idx)) .collect::>(); + let index = FragIndex { + strings: strings_index, + types: types_index, + protos: protos_index, + fields: fields_index, + methods: methods_index, + }; + frag.section_manager .add_elt(FragSection::ClassDefItem, None); - frag.class_def.class_idx = *types_index.get(&class.descriptor).ok_or(anyhow!( + frag.class_def.class_idx = *index.types.get(&class.descriptor).ok_or(anyhow!( "Type {} (type of class {}) not found in type list", class.descriptor.__str__(), class.__str__() ))? as u32; frag.class_def.access_flags = class.get_raw_access_flags(); frag.class_def.superclass_idx = if let Some(sup) = &class.superclass { - *types_index.get(sup).ok_or(anyhow!( + *index.types.get(sup).ok_or(anyhow!( "Type {} (superclass of class {}) not found in dex builder", sup.__str__(), class.__str__() @@ -171,7 +179,7 @@ impl DexFragment { }; frag.class_def.interfaces_off = 0; // set when linking frag.class_def.source_file_idx = if let Some(file) = &class.source_file { - *strings_index.get(file).ok_or(anyhow!( + *index.strings.get(file).ok_or(anyhow!( "String {} (source file of class {}) not found in dex builder", file.__str__(), class.__str__() @@ -180,7 +188,7 @@ impl DexFragment { NO_INDEX.0 }; frag.class_def.annotations_off = if class.has_annotations() { - frag.insert_annotations(class)?; + frag.insert_annotations(class, &index)?; 1 } else { 0 @@ -214,30 +222,29 @@ impl DexFragment { /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow /// to distinguish the offset of the first item (equal to zero before linking) and the value /// 0 used to indicate an abscence of item. - fn insert_annotations(&mut self, class: &Class) -> Result<()> { + fn insert_annotations(&mut self, class: &Class, index: &FragIndex) -> Result<()> { let class_annotations_off = if !class.annotations.is_empty() { let class_annotations_off = self .section_manager .get_aligned_size(FragSection::AnnotationSetItem); - self.insert_class_annotation_set(class).with_context(|| { - format!( - "Failed to insert class annotation for class {}", - class.__str__() - ) - })?; + self.insert_class_annotation_set(class, index) + .with_context(|| { + format!( + "Failed to insert class annotation for class {}", + class.__str__() + ) + })?; class_annotations_off + 1 } else { 0 }; let mut field_ids = vec![]; - let (class, _) = self.class_defs.get(class_id).unwrap(); field_ids.extend(class.static_fields.keys().cloned()); field_ids.extend(class.instance_fields.keys().cloned()); field_ids.sort(); let mut field_annotations = vec![]; for field_id in field_ids { - let (class, _) = self.class_defs.get(class_id).unwrap(); let static_field = class.static_fields.get(&field_id); let instance_field = class.instance_fields.get(&field_id); let (is_static, field) = match (static_field, instance_field) { @@ -245,33 +252,31 @@ impl DexFragment { (None, Some(field)) => (false, field), _ => bail!( "Unexpected configuration: field {} is both a static and a instance field in {}", - field_id.__str__(), class_id.__str__()), + field_id.__str__(), class.__str__()), }; if !field.annotations.is_empty() { let annotations_off = self .section_manager - .get_aligned_size(Section::AnnotationSetItem) + .get_aligned_size(FragSection::AnnotationSetItem) + 1; - self.insert_field_annotation_set(&field_id, is_static)?; + self.insert_field_annotation_set(field, index)?; field_annotations.push(FieldAnnotation { - field_idx: *self.field_ids.get(&field_id).ok_or(anyhow!( + field_idx: *index.fields.get(&field_id).ok_or(anyhow!( "Field {} in {} not found in dex builder", field_id.__str__(), - class_id.__str__(), + class.__str__(), ))? as u32, - annotations_off, // linked in link_annotations() + annotations_off, }); } } let mut method_ids = vec![]; - let (class, _) = self.class_defs.get(class_id).unwrap(); method_ids.extend(class.direct_methods.keys().cloned()); method_ids.extend(class.virtual_methods.keys().cloned()); method_ids.sort(); let mut method_annotations = vec![]; for method_id in &method_ids { - let (class, _) = self.class_defs.get(class_id).unwrap(); let direct_method = class.direct_methods.get(method_id); let virtual_method = class.virtual_methods.get(method_id); let (is_direct, method) = match (direct_method, virtual_method) { @@ -279,28 +284,27 @@ impl DexFragment { (None, Some(method)) => (false, method), _ => bail!( "Unexpected configuration: method {} is both a direct and a virtual method in {}", - method_id.__str__(), class_id.__str__()), + method_id.__str__(), class.__str__()), }; if !method.annotations.is_empty() { let annotations_off = self .section_manager - .get_aligned_size(Section::AnnotationSetItem) + .get_aligned_size(FragSection::AnnotationSetItem) + 1; - self.insert_method_annotation_set(method_id, is_direct)?; + self.insert_method_annotation_set(method, index)?; method_annotations.push(MethodAnnotation { - method_idx: *self.method_ids.get(method_id).ok_or(anyhow!( + method_idx: *index.methods.get(method_id).ok_or(anyhow!( "Method {} in {} not found in dex builder", method_id.__str__(), - class_id.__str__(), + class.__str__(), ))? as u32, - annotations_off, // linked in link_annotations() + annotations_off, }); } } let mut parameter_annotations = vec![]; for method_id in method_ids { - let (class, _) = self.class_defs.get(class_id).unwrap(); let direct_method = class.direct_methods.get(&method_id); let virtual_method = class.virtual_methods.get(&method_id); let (is_direct, method) = match (direct_method, virtual_method) { @@ -308,39 +312,39 @@ impl DexFragment { (None, Some(method)) => (false, method), _ => bail!( "Unexpected configuration: method {} is both a direct and a virtual method in {}", - method_id.__str__(), class_id.__str__()), + method_id.__str__(), class.__str__()), }; if !method.parameters_annotations.is_empty() { let annotations_off = self .section_manager - .get_aligned_size(Section::AnnotationSetRefList) + .get_aligned_size(FragSection::AnnotationSetRefList) + 1; - self.insert_parameters_annotation_set_list(&method_id, is_direct)?; + self.insert_parameters_annotation_set_list(method, index)?; parameter_annotations.push(ParameterAnnotation { - method_idx: *self.method_ids.get(&method_id).ok_or(anyhow!( + method_idx: *index.methods.get(&method_id).ok_or(anyhow!( "Method {} in {} not found in dex builder", method_id.__str__(), - class_id.__str__(), + class.__str__(), ))? as u32, - annotations_off, // linked in link_annotations() + annotations_off, }); } } let item = AnnotationsDirectoryItem { - class_annotations_off, // linked in link_annotations() + class_annotations_off, field_annotations, method_annotations, parameter_annotations, }; self.section_manager .add_elt(FragSection::AnnotationsDirectoryItem, Some(item.size())); - self.annotations_directory_items.push(item); + self.annotations_directory_items = Some(item); Ok(()) } /// Insert the annnotations set for a class. - fn insert_class_annotation_set(&mut self, class: &Class) -> Result<()> { + fn insert_class_annotation_set(&mut self, class: &Class, index: &FragIndex) -> Result<()> { let mut annotations = class.annotations.clone(); let mut annotation_set = AnnotationSetItem { entries: vec![] }; @@ -367,7 +371,7 @@ impl DexFragment { runtime or system" ), // TODO: check if this is true }, - annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, + annotation: self.dex_annotation_to_encoded_annotation(annot.annotation, index)?, }; self.section_manager .add_elt(FragSection::AnnotationItem, Some(item.size())); @@ -379,6 +383,170 @@ impl DexFragment { Ok(()) } + /// Insert the annnotations set for a class. + fn insert_field_annotation_set(&mut self, field: &Field, index: &FragIndex) -> Result<()> { + let mut annotations = field.annotations.clone(); + + let mut annotation_set = AnnotationSetItem { entries: vec![] }; + annotations.sort_by_key(|annot| annot.annotation.type_.clone()); + for annot in annotations { + annotation_set.entries.push(AnnotationOffItem { + annotation_off: self + .section_manager + .get_aligned_size(FragSection::AnnotationItem), + }); + + let item = AnnotationItem { + visibility: match ( + annot.visibility_build, + annot.visibility_runtime, + annot.visibility_system, + ) { + (true, false, false) => AnnotationVisibility::Build, + (false, true, false) => AnnotationVisibility::Runtime, + (false, false, true) => AnnotationVisibility::System, + _ => bail!( + "Annotation need visibility set to one and only one of build, \ + runtime or system" + ), // TODO: check if this is true + }, + annotation: self.dex_annotation_to_encoded_annotation(annot.annotation, index)?, + }; + self.section_manager + .add_elt(FragSection::AnnotationItem, Some(item.size())); + self.annotation_items.push(item); + } + self.section_manager + .add_elt(FragSection::AnnotationSetItem, Some(annotation_set.size())); + self.annotation_set_items.push(annotation_set); + Ok(()) + } + + /// Insert the annnotations set for a method (but not the parameters annotations). + fn insert_method_annotation_set(&mut self, method: &Method, index: &FragIndex) -> Result<()> { + let mut annotations = method.annotations.clone(); + + let mut annotation_set = AnnotationSetItem { entries: vec![] }; + annotations.sort_by_key(|annot| annot.annotation.type_.clone()); + for annot in annotations { + annotation_set.entries.push(AnnotationOffItem { + annotation_off: self + .section_manager + .get_aligned_size(FragSection::AnnotationItem) + + 1, + }); + + let item = AnnotationItem { + visibility: match ( + annot.visibility_build, + annot.visibility_runtime, + annot.visibility_system, + ) { + (true, false, false) => AnnotationVisibility::Build, + (false, true, false) => AnnotationVisibility::Runtime, + (false, false, true) => AnnotationVisibility::System, + _ => bail!( + "Annotation need visibility set to one and only one of build, \ + runtime or system" + ), // TODO: check if this is true + }, + annotation: self.dex_annotation_to_encoded_annotation(annot.annotation, index)?, + }; + self.section_manager + .add_elt(FragSection::AnnotationItem, Some(item.size())); + self.annotation_items.push(item); + } + self.section_manager + .add_elt(FragSection::AnnotationSetItem, Some(annotation_set.size())); + self.annotation_set_items.push(annotation_set); + Ok(()) + } + + /// Insert a [`MethodHandle`]. + pub fn insert_method_handle(&mut self, handle: &MethodHandle, index: &FragIndex) -> Result<()> { + let (field_or_method_id, method_handle_type) = match handle { + MethodHandle::StaticPut(StaticPut(field)) => ( + *index + .fields + .get(field) + .ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))? + as u16, + MethodHandleType::StaticPut, + ), + MethodHandle::StaticGet(StaticGet(field)) => ( + *index + .fields + .get(field) + .ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))? + as u16, + MethodHandleType::StaticGet, + ), + MethodHandle::InstancePut(InstancePut(field)) => ( + *index + .fields + .get(field) + .ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))? + as u16, + MethodHandleType::InstancePut, + ), + MethodHandle::InstanceGet(InstanceGet(field)) => ( + *index + .fields + .get(field) + .ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))? + as u16, + MethodHandleType::InstanceGet, + ), + MethodHandle::InvokeStatic(InvokeStatic(meth)) => ( + *index + .methods + .get(meth) + .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? + as u16, + MethodHandleType::InvokeStatic, + ), + MethodHandle::InvokeInstance(InvokeInstance(meth)) => ( + *index + .methods + .get(meth) + .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? + as u16, + MethodHandleType::InvokeInstance, + ), + MethodHandle::InvokeConstructor(InvokeConstructor(meth)) => ( + *index + .methods + .get(meth) + .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? + as u16, + MethodHandleType::InvokeConstructor, + ), + MethodHandle::InvokeDirect(InvokeDirect(meth)) => ( + *index + .methods + .get(meth) + .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? + as u16, + MethodHandleType::InvokeDirect, + ), + MethodHandle::InvokeInterface(InvokeInterface(meth)) => ( + *index + .methods + .get(meth) + .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? + as u16, + MethodHandleType::InvokeInterface, + ), + }; + self.method_handles.push(MethodHandleItem { + method_handle_type, + field_or_method_id, + unused1: 0, + unused2: 0, + }); + Ok(()) + } + /// Insert a code_item. /// /// # Warning @@ -1283,7 +1451,7 @@ impl DexFragment { } Instruction::ConstMethodHandle(ins) => { let method_handle_idx = self.method_handles.len(); - self.insert_method_handle(&ins.handle)?; + self.insert_method_handle(&ins.handle, index)?; let ins = ins.get_raw_ins(method_handle_idx); addr += ins.size() / 2; insns.push(ins); @@ -1558,97 +1726,16 @@ impl DexFragment { Ok(()) } - /// Insert a [`MethodHandle`]. - pub fn insert_method_handle(&mut self, handle: &MethodHandle) -> Result<()> { - let (field_or_method_id, method_handle_type) = match handle { - MethodHandle::StaticPut(StaticPut(field)) => ( - *self - .field_ids - .get(field) - .ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))? - as u16, - MethodHandleType::StaticPut, - ), - MethodHandle::StaticGet(StaticGet(field)) => ( - *self - .field_ids - .get(field) - .ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))? - as u16, - MethodHandleType::StaticGet, - ), - MethodHandle::InstancePut(InstancePut(field)) => ( - *self - .field_ids - .get(field) - .ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))? - as u16, - MethodHandleType::InstancePut, - ), - MethodHandle::InstanceGet(InstanceGet(field)) => ( - *self - .field_ids - .get(field) - .ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))? - as u16, - MethodHandleType::InstanceGet, - ), - MethodHandle::InvokeStatic(InvokeStatic(meth)) => ( - *self - .method_ids - .get(meth) - .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? - as u16, - MethodHandleType::InvokeStatic, - ), - MethodHandle::InvokeInstance(InvokeInstance(meth)) => ( - *self - .method_ids - .get(meth) - .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? - as u16, - MethodHandleType::InvokeInstance, - ), - MethodHandle::InvokeConstructor(InvokeConstructor(meth)) => ( - *self - .method_ids - .get(meth) - .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? - as u16, - MethodHandleType::InvokeConstructor, - ), - MethodHandle::InvokeDirect(InvokeDirect(meth)) => ( - *self - .method_ids - .get(meth) - .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? - as u16, - MethodHandleType::InvokeDirect, - ), - MethodHandle::InvokeInterface(InvokeInterface(meth)) => ( - *self - .method_ids - .get(meth) - .ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))? - as u16, - MethodHandleType::InvokeInterface, - ), - }; - self.method_handles.push(MethodHandleItem { - method_handle_type, - field_or_method_id, - unused1: 0, - unused2: 0, - }); - Ok(()) - } - /// Convert a [`DexValue`] to an [`EncodedValue`]. /// /// # Warning /// /// This method can insert element in the dex file like method_handles. - pub fn dex_value_to_encoded_value(&mut self, value: &DexValue) -> Result { + pub fn dex_value_to_encoded_value( + &mut self, + value: &DexValue, + index: &FragIndex, + ) -> Result { match value { DexValue::Byte(DexByte(val)) => Ok(EncodedValue::Byte(*val)), DexValue::Short(DexShort(val)) => Ok(EncodedValue::Short(*val)), @@ -1658,48 +1745,47 @@ impl DexFragment { DexValue::Float(DexFloat(val)) => Ok(EncodedValue::Float(*val)), DexValue::Double(DexDouble(val)) => Ok(EncodedValue::Double(*val)), DexValue::MethodType(val) => Ok(EncodedValue::MethodType( - *self.proto_ids.get(val).ok_or(anyhow!( + *index.protos.get(val).ok_or(anyhow!( "Prototype {} not found in dex writer", val.__str__() ))? as u32, )), DexValue::MethodHandle(val) => { - // TODO: move to a method let idx = self.method_handles.len() as u32; - self.insert_method_handle(val)?; + self.insert_method_handle(val, index)?; Ok(EncodedValue::MethodHandle(idx)) } DexValue::String(val) => Ok(EncodedValue::String( - *self + *index .strings .get(val) .ok_or(anyhow!("String {} not found in dex writer", val.__str__()))? as u32, )), DexValue::Type(val) => Ok(EncodedValue::Type( - *self - .type_ids + *index + .types .get(val) .ok_or(anyhow!("Type {} not found in dex writer", val.__str__()))? as u32, )), DexValue::Field(val) => Ok(EncodedValue::Field( - *self - .field_ids + *index + .fields .get(val) .ok_or(anyhow!("Field {} not found in dex writer", val.__str__()))? as u32, )), DexValue::Method(val) => Ok(EncodedValue::Method( - *self - .method_ids + *index + .methods .get(val) .ok_or(anyhow!("Method {} not found in dex writer", val.__str__()))? as u32, )), DexValue::Enum(IdEnum(val)) => Ok(EncodedValue::Enum( - *self - .field_ids + *index + .fields .get(val) .ok_or(anyhow!("Field {} not found in dex writer", val.__str__()))? as u32, @@ -1708,14 +1794,14 @@ impl DexFragment { let mut values = vec![]; for val in arr { values.push( - self.dex_value_to_encoded_value(val) + self.dex_value_to_encoded_value(val, index) .context("Error while serializing a array")?, ); } Ok(EncodedValue::Array(EncodedArray { values })) } DexValue::Annotation(val) => Ok(EncodedValue::Annotation( - self.dex_annotation_to_encoded_annotation(val.clone())?, + self.dex_annotation_to_encoded_annotation(val.clone(), index)?, )), DexValue::Null(DexNull) => Ok(EncodedValue::Null), DexValue::Boolean(DexBoolean(val)) => Ok(EncodedValue::Boolean(*val)), @@ -1723,16 +1809,20 @@ impl DexFragment { } /// Insert an encoded_array in the encoded_array_item section. - fn insert_encoded_array_item(&mut self, DexArray(array): DexArray) -> Result<()> { + fn insert_encoded_array_item( + &mut self, + DexArray(array): DexArray, + index: &FragIndex, + ) -> Result<()> { let mut values = vec![]; for value in array { - values.push(self.dex_value_to_encoded_value(&value)?); + values.push(self.dex_value_to_encoded_value(&value, index)?); } let item = EncodedArrayItem { value: EncodedArray { values }, }; self.section_manager - .add_elt(Section::EncodedArrayItem, Some(item.size())); + .add_elt(FragSection::EncodedArrayItem, Some(item.size())); self.encoded_array_items.push(item); Ok(()) } @@ -1794,6 +1884,7 @@ impl DexFragment { fn dex_annotation_to_encoded_annotation( &mut self, DexAnnotation { type_, elements }: DexAnnotation, + index: &FragIndex, ) -> Result { let mut encoded_elements = vec![]; @@ -1803,7 +1894,7 @@ impl DexFragment { for name in elements_names { let elt = elements.get(name).unwrap(); encoded_elements.push(AnnotationElement { - name_idx: Uleb128(*self.strings.get(name).ok_or(anyhow!( + name_idx: Uleb128(*index.strings.get(name).ok_or(anyhow!( "{} (annotation element name) not found in dex builder", name.__str__() ))? as u32), @@ -1811,7 +1902,7 @@ impl DexFragment { }); } Ok(EncodedAnnotation { - type_idx: Uleb128(*self.type_ids.get(&type_).ok_or(anyhow!( + type_idx: Uleb128(*index.types.get(&type_).ok_or(anyhow!( "Annotation type {} not found in dex builder", type_.__str__(), ))? as u32), @@ -1819,117 +1910,13 @@ impl DexFragment { }) } - /// Insert the annnotations set for a class. - fn insert_field_annotation_set( - &mut self, - field_id: &IdField, - is_static_field: bool, - ) -> Result<()> { - let (class, _) = self.class_defs.get(&field_id.class_).unwrap(); - let field = if is_static_field { - class.static_fields.get(field_id).unwrap() - } else { - class.instance_fields.get(field_id).unwrap() - }; - let mut annotations = field.annotations.clone(); - - let mut annotation_set = AnnotationSetItem { entries: vec![] }; - annotations.sort_by_key(|annot| annot.annotation.type_.clone()); - for annot in annotations { - annotation_set.entries.push(AnnotationOffItem { - annotation_off: self - .section_manager - .get_aligned_size(Section::AnnotationItem), - }); // linked in link_annotations() - - let item = AnnotationItem { - visibility: match ( - annot.visibility_build, - annot.visibility_runtime, - annot.visibility_system, - ) { - (true, false, false) => AnnotationVisibility::Build, - (false, true, false) => AnnotationVisibility::Runtime, - (false, false, true) => AnnotationVisibility::System, - _ => bail!( - "Annotation need visibility set to one and only one of build, \ - runtime or system" - ), // TODO: check if this is true - }, - annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, - }; - self.section_manager - .add_elt(Section::AnnotationItem, Some(item.size())); - self.annotation_items.push(item); - } - self.section_manager - .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); - self.annotation_set_items.push(annotation_set); - Ok(()) - } - - /// Insert the annnotations set for a method (but not the parameters annotations). - fn insert_method_annotation_set( - &mut self, - method_id: &IdMethod, - is_direct_method: bool, - ) -> Result<()> { - let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); - let method = if is_direct_method { - class.direct_methods.get(method_id).unwrap() - } else { - class.virtual_methods.get(method_id).unwrap() - }; - let mut annotations = method.annotations.clone(); - - let mut annotation_set = AnnotationSetItem { entries: vec![] }; - annotations.sort_by_key(|annot| annot.annotation.type_.clone()); - for annot in annotations { - annotation_set.entries.push(AnnotationOffItem { - annotation_off: self - .section_manager - .get_aligned_size(Section::AnnotationItem), - }); // linked in link_annotations() - - let item = AnnotationItem { - visibility: match ( - annot.visibility_build, - annot.visibility_runtime, - annot.visibility_system, - ) { - (true, false, false) => AnnotationVisibility::Build, - (false, true, false) => AnnotationVisibility::Runtime, - (false, false, true) => AnnotationVisibility::System, - _ => bail!( - "Annotation need visibility set to one and only one of build, \ - runtime or system" - ), // TODO: check if this is true - }, - annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, - }; - self.section_manager - .add_elt(Section::AnnotationItem, Some(item.size())); - self.annotation_items.push(item); - } - self.section_manager - .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); - self.annotation_set_items.push(annotation_set); - Ok(()) - } - /// Insert the annotations set for a method parameter. fn insert_parameters_annotation_set( &mut self, - method_id: &IdMethod, - is_direct_method: bool, + method: &Method, parameter_idx: usize, + index: &FragIndex, ) -> Result<()> { - let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); - let method = if is_direct_method { - class.direct_methods.get(method_id).unwrap() - } else { - class.virtual_methods.get(method_id).unwrap() - }; let mut annotations = method.parameters_annotations[parameter_idx].clone(); let mut annotation_set = AnnotationSetItem { entries: vec![] }; @@ -1938,8 +1925,9 @@ impl DexFragment { annotation_set.entries.push(AnnotationOffItem { annotation_off: self .section_manager - .get_aligned_size(Section::AnnotationItem), - }); // linked in link_annotations() + .get_aligned_size(FragSection::AnnotationItem) + + 1, + }); let item = AnnotationItem { visibility: match ( @@ -1955,14 +1943,14 @@ impl DexFragment { runtime or system" ), // TODO: check if this is true }, - annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, + annotation: self.dex_annotation_to_encoded_annotation(annot.annotation, index)?, }; self.section_manager - .add_elt(Section::AnnotationItem, Some(item.size())); + .add_elt(FragSection::AnnotationItem, Some(item.size())); self.annotation_items.push(item); } self.section_manager - .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); + .add_elt(FragSection::AnnotationSetItem, Some(annotation_set.size())); self.annotation_set_items.push(annotation_set); Ok(()) } @@ -1970,16 +1958,10 @@ impl DexFragment { /// Insert the annotations set list for a method parameters. fn insert_parameters_annotation_set_list( &mut self, - method_id: &IdMethod, - is_direct_method: bool, + method: &Method, + index: &FragIndex, ) -> Result<()> { let mut list = AnnotationSetRefList { list: vec![] }; - let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); - let method = if is_direct_method { - class.direct_methods.get(method_id).unwrap() - } else { - class.virtual_methods.get(method_id).unwrap() - }; let param_has_annotation: Vec<_> = method .parameters_annotations .iter() @@ -1990,17 +1972,17 @@ impl DexFragment { annotations_off: if has_annotation { let annotation_off = self .section_manager - .get_aligned_size(Section::AnnotationSetItem); - self.insert_parameters_annotation_set(method_id, is_direct_method, param_idx)?; + .get_aligned_size(FragSection::AnnotationSetItem); + self.insert_parameters_annotation_set(method, param_idx, index)?; annotation_off + 1 } else { 0 - }, // linked in link_annotations() + }, }); } self.section_manager - .add_elt(Section::AnnotationSetRefList, Some(list.size())); + .add_elt(FragSection::AnnotationSetRefList, Some(list.size())); self.annotation_set_lists.push(list); Ok(()) } @@ -2879,3 +2861,12 @@ impl FragSectionManager { } } } + +/// Index that associate a type to its local id in a fragment. +struct FragIndex { + pub strings: HashMap, + pub types: HashMap, + pub protos: HashMap, + pub fields: HashMap, + pub methods: HashMap, +} From d28968c2e368e7053d70a3b9c475ac84f6a4da1c Mon Sep 17 00:00:00 2001 From: Jean-Marie 'Histausse' Mineau Date: Thu, 14 Mar 2024 16:22:24 +0100 Subject: [PATCH 05/14] finish fragement generation --- TODO.md | 11 +- androscalpel/src/dex_fragment.rs | 1125 +++++------------------------- 2 files changed, 203 insertions(+), 933 deletions(-) diff --git a/TODO.md b/TODO.md index 392a652..5cb22cb 100644 --- a/TODO.md +++ b/TODO.md @@ -7,5 +7,14 @@ - name register / parameters # frag meth -- insert_class_data_item - insert_class_static_values + +# relink + +data.static_fields: field_idx_diff +data.direct_methods: method_idx_diff +data.code_off +code_item.try_item +instr + +EncodedValue diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs index 6a0cabe..27bc792 100644 --- a/androscalpel/src/dex_fragment.rs +++ b/androscalpel/src/dex_fragment.rs @@ -1,8 +1,7 @@ //! The structure that generate a .dex from classes. -use std::collections::{HashMap, HashSet, VecDeque}; -use std::io; -use std::io::{Cursor, Seek, SeekFrom, Write}; +use std::collections::HashMap; +use std::io::Cursor; use anyhow::{anyhow, bail, Context}; use log::debug; @@ -24,8 +23,8 @@ use androscalpel_serializer::Instruction as InsFormat; /// level format. #[derive(Debug, Clone)] pub struct DexFragment { - /// The id of the class represented by this fragment. - class_id: IdType, + // /// The id of the class represented by this fragment. + // class_id: IdType, /// The strings in the dex file, sorted. strings: Vec, /// The types in the dex file, sorted. @@ -48,10 +47,10 @@ pub struct DexFragment { class_data: Option, // TODO: type list should be handle like other ids // TODO: type list inside of proto ids are not handled here - /// The type lists found in the classes associated to their index in the type_lists section. - type_lists_index: HashMap, - /// The type_lists section and the offset of the lists inside the section. - type_lists_with_offset: Vec<(TypeList, u32)>, + // /// The type lists found in the classes associated to their index in the type_lists section. + // type_lists_index: HashMap, + // /// The type_lists section and the offset of the lists inside the section. + // type_lists_with_offset: Vec<(TypeList, u32)>, /// The encoded_array_items section. encoded_array_items: Vec, /// The annotations_directory_item. @@ -79,7 +78,7 @@ impl DexFragment { class.descriptor.__str__() ); let mut frag = Self { - class_id: class.descriptor.clone(), + //class_id: class.descriptor.clone(), strings: vec![], type_ids: vec![], proto_ids: vec![], @@ -98,8 +97,8 @@ impl DexFragment { call_site_ids: vec![], section_manager: FragSectionManager::default(), class_data: None, - type_lists_index: HashMap::new(), - type_lists_with_offset: vec![], + // type_lists_index: HashMap::new(), + // type_lists_with_offset: vec![], encoded_array_items: vec![], method_handles: vec![], code_items: vec![], @@ -194,7 +193,7 @@ impl DexFragment { 0 }; frag.class_def.class_data_off = if class.has_data_item() { - //TODO: frag.insert_class_data_item(class_id)?; + frag.insert_class_data_item(class, &index)?; 1 } else { 0 @@ -203,7 +202,7 @@ impl DexFragment { let static_values_off = frag .section_manager .get_aligned_size(FragSection::EncodedArrayItem); - // TODO: frag.insert_class_static_values(class)?; + frag.insert_class_static_values(class, &index)?; static_values_off + 1 } else { 0 @@ -247,9 +246,9 @@ impl DexFragment { for field_id in field_ids { let static_field = class.static_fields.get(&field_id); let instance_field = class.instance_fields.get(&field_id); - let (is_static, field) = match (static_field, instance_field) { - (Some(field), None) => (true, field), - (None, Some(field)) => (false, field), + let field = match (static_field, instance_field) { + (Some(field), None) => field, + (None, Some(field)) => field, _ => bail!( "Unexpected configuration: field {} is both a static and a instance field in {}", field_id.__str__(), class.__str__()), @@ -279,9 +278,9 @@ impl DexFragment { for method_id in &method_ids { let direct_method = class.direct_methods.get(method_id); let virtual_method = class.virtual_methods.get(method_id); - let (is_direct, method) = match (direct_method, virtual_method) { - (Some(method), None) => (true, method), - (None, Some(method)) => (false, method), + let method = match (direct_method, virtual_method) { + (Some(method), None) => method, + (None, Some(method)) => method, _ => bail!( "Unexpected configuration: method {} is both a direct and a virtual method in {}", method_id.__str__(), class.__str__()), @@ -307,9 +306,9 @@ impl DexFragment { for method_id in method_ids { let direct_method = class.direct_methods.get(&method_id); let virtual_method = class.virtual_methods.get(&method_id); - let (is_direct, method) = match (direct_method, virtual_method) { - (Some(method), None) => (true, method), - (None, Some(method)) => (false, method), + let method = match (direct_method, virtual_method) { + (Some(method), None) => method, + (None, Some(method)) => method, _ => bail!( "Unexpected configuration: method {} is both a direct and a virtual method in {}", method_id.__str__(), class.__str__()), @@ -463,7 +462,7 @@ impl DexFragment { } /// Insert a [`MethodHandle`]. - pub fn insert_method_handle(&mut self, handle: &MethodHandle, index: &FragIndex) -> Result<()> { + fn insert_method_handle(&mut self, handle: &MethodHandle, index: &FragIndex) -> Result<()> { let (field_or_method_id, method_handle_type) = match handle { MethodHandle::StaticPut(StaticPut(field)) => ( *index @@ -552,32 +551,7 @@ impl DexFragment { /// # Warning /// /// 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 { - self.class_defs - .get(&method_id.class_) - .unwrap() - .0 - .direct_methods - .get(&method_id) - .unwrap() - .code - .as_ref() - .unwrap() - .clone() - } else { - self.class_defs - .get(&method_id.class_) - .unwrap() - .0 - .virtual_methods - .get(&method_id) - .unwrap() - .code - .as_ref() - .unwrap() - .clone() - }; + fn insert_code_item(&mut self, code: &Code, index: &FragIndex) -> Result<()> { // Estimate instructions addresses let mut min_addr = 0; let mut max_addr = 0; @@ -591,10 +565,9 @@ impl DexFragment { max_addr += ins.max_ins_size() / 2; } Instruction::ConstString(ins) => { - let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( - "String {} (found in code of {}) not found in dex builder", + let string_idx = index.strings.get(&ins.lit).ok_or(anyhow!( + "String {} not found in dex builder", ins.lit.__str__(), - method_id.__str__() ))?; let size = ins.get_raw_ins(*string_idx).size() / 2; min_addr += size; @@ -617,10 +590,9 @@ impl DexFragment { addr += ins.max_ins_size() / 2; } Instruction::ConstString(ins) => { - let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( - "String {} (found in code of {}) not found in dex builder", + let string_idx = index.strings.get(&ins.lit).ok_or(anyhow!( + "String {} not found in dex builder", ins.lit.__str__(), - method_id.__str__() ))?; addr += ins.get_raw_ins(*string_idx).size() / 2; } @@ -650,70 +622,63 @@ impl DexFragment { for ins in &code.insns { match ins { Instruction::ConstString(ins) => { - let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( - "String {} (found in code of {}) not found in dex builder", + let string_idx = index.strings.get(&ins.lit).ok_or(anyhow!( + "String {} not found in dex fragment", ins.lit.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*string_idx); addr += ins.size() / 2; insns.push(ins); } Instruction::ConstClass(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Class {} (type of class found in code of {}) not found in dex builder", + let class_idx = *index.types.get(&ins.lit).ok_or(anyhow!( + "Class {} not found in dex fragment", ins.lit.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(class_idx); addr += ins.size() / 2; insns.push(ins); } Instruction::CheckCast(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Class {} (type of class found in code of {}) not found in dex builder", + let class_idx = *index.types.get(&ins.lit).ok_or(anyhow!( + "Class {} not found in dex fragment", ins.lit.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(class_idx); addr += ins.size() / 2; insns.push(ins); } Instruction::InstanceOf(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Class {} (type of class found in code of {}) not found in dex builder", + let class_idx = *index.types.get(&ins.lit).ok_or(anyhow!( + "Class {} not found in dex fragment", ins.lit.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(class_idx); addr += ins.size() / 2; insns.push(ins); } Instruction::NewInstance(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Class {} (type of class found in code of {}) not found in dex builder", + let class_idx = *index.types.get(&ins.lit).ok_or(anyhow!( + "Class {} not found in dex fragment", ins.lit.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(class_idx); addr += ins.size() / 2; insns.push(ins); } Instruction::NewArray(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Type {} (type found in code of {}) not found in dex builder", + let class_idx = *index.types.get(&ins.lit).ok_or(anyhow!( + "Type {} not found in dex fragment", ins.lit.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(class_idx); addr += ins.size() / 2; insns.push(ins); } Instruction::FilledNewArray(ins) => { - let class_idx = *self.type_ids.get(&ins.type_).ok_or(anyhow!( - "Type {} (type found in code of {}) not found in dex builder", + let class_idx = *index.types.get(&ins.type_).ok_or(anyhow!( + "Type {} not found in dex fragment", ins.type_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(class_idx); addr += ins.size() / 2; @@ -743,9 +708,8 @@ impl DexFragment { let goto_size = goto_sizes[goto_idx]; goto_idx += 1; let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found goto with this label", + "Label {} not found in code, but found goto with this label", ins.label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; let ins = ins.get_raw_ins(branch_offset, goto_size); @@ -756,19 +720,17 @@ impl DexFragment { let mut key_targets = vec![]; for (key, label) in &ins.branches { let label_addr = label_addrs.get(label).ok_or(anyhow!( - "Label {} not found in code of {}, but found goto with this label", + "Label {} not found in code, but found goto with this label", label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; key_targets.push((*key, branch_offset)); } key_targets.sort_by_key(|(key, _)| *key); let payload = if ins.is_packed() { - let (first_key, _) = *key_targets.first().ok_or(anyhow!( - "Found empty swith in code of {}", - method_id.__str__() - ))?; + let (first_key, _) = *key_targets + .first() + .ok_or(anyhow!("Found empty swith in code"))?; let targets: Vec<_> = key_targets.into_iter().map(|(_, target)| target).collect(); InsFormat::FormatPackedSwitchPayload { first_key, targets } @@ -792,16 +754,12 @@ impl DexFragment { } Instruction::IfEq(ins) => { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", + "Label {} not found in code, but found if with this label", ins.label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__str__() - ); + bail!("Found an if that jump to far from the instruction in code"); } let ins = ins.get_raw_ins(branch_offset as i16); addr += ins.size() / 2; @@ -809,16 +767,12 @@ impl DexFragment { } Instruction::IfNe(ins) => { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", + "Label {} not found in code, but found if with this label", ins.label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__str__() - ); + bail!("Found an if that jump to far from the instruction in code"); } let ins = ins.get_raw_ins(branch_offset as i16); addr += ins.size() / 2; @@ -826,16 +780,12 @@ impl DexFragment { } Instruction::IfLt(ins) => { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", + "Label {} not found in code, but found if with this label", ins.label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__str__() - ); + bail!("Found an if that jump to far from the instruction in code"); } let ins = ins.get_raw_ins(branch_offset as i16); addr += ins.size() / 2; @@ -843,16 +793,12 @@ impl DexFragment { } Instruction::IfGe(ins) => { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", + "Label {} not found in code, but found if with this label", ins.label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__str__() - ); + bail!("Found an if that jump to far from the instruction in code"); } let ins = ins.get_raw_ins(branch_offset as i16); addr += ins.size() / 2; @@ -860,16 +806,12 @@ impl DexFragment { } Instruction::IfGt(ins) => { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", + "Label {} not found in code, but found if with this label", ins.label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__str__() - ); + bail!("Found an if that jump to far from the instruction in code"); } let ins = ins.get_raw_ins(branch_offset as i16); addr += ins.size() / 2; @@ -877,16 +819,12 @@ impl DexFragment { } Instruction::IfLe(ins) => { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", + "Label {} not found in code, but found if with this label", ins.label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__str__() - ); + bail!("Found an if that jump to far from the instruction in code"); } let ins = ins.get_raw_ins(branch_offset as i16); addr += ins.size() / 2; @@ -894,16 +832,12 @@ impl DexFragment { } Instruction::IfEqZ(ins) => { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", + "Label {} not found in code, but found if with this label", ins.label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__str__() - ); + bail!("Found an if that jump to far from the instruction in code"); } let ins = ins.get_raw_ins(branch_offset as i16); addr += ins.size() / 2; @@ -911,16 +845,12 @@ impl DexFragment { } Instruction::IfNeZ(ins) => { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", + "Label {} not found in code, but found if with this label", ins.label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__str__() - ); + bail!("Found an if that jump to far from the instruction in code"); } let ins = ins.get_raw_ins(branch_offset as i16); addr += ins.size() / 2; @@ -928,16 +858,12 @@ impl DexFragment { } Instruction::IfLtZ(ins) => { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", + "Label {} not found in code, but found if with this label", ins.label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__str__() - ); + bail!("Found an if that jump to far from the instruction in code"); } let ins = ins.get_raw_ins(branch_offset as i16); addr += ins.size() / 2; @@ -945,16 +871,12 @@ impl DexFragment { } Instruction::IfGeZ(ins) => { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", + "Label {} not found in code, but found if with this label", ins.label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__str__() - ); + bail!("Found an if that jump to far from the instruction in code"); } let ins = ins.get_raw_ins(branch_offset as i16); addr += ins.size() / 2; @@ -962,16 +884,12 @@ impl DexFragment { } Instruction::IfGtZ(ins) => { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", + "Label {} not found in code, but found if with this label", ins.label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__str__() - ); + bail!("Found an if that jump to far from the instruction in code"); } let ins = ins.get_raw_ins(branch_offset as i16); addr += ins.size() / 2; @@ -979,16 +897,12 @@ impl DexFragment { } Instruction::IfLeZ(ins) => { let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", + "Label {} not found in code, but found if with this label", ins.label, - method_id.__str__() ))?; let branch_offset = *label_addr as i32 - addr as i32; if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__str__() - ); + bail!("Found an if that jump to far from the instruction in code"); } let ins = ins.get_raw_ins(branch_offset as i16); addr += ins.size() / 2; @@ -996,11 +910,10 @@ impl DexFragment { } Instruction::IGet(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1008,11 +921,10 @@ impl DexFragment { } Instruction::IGetWide(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1020,11 +932,10 @@ impl DexFragment { } Instruction::IGetObject(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1032,11 +943,10 @@ impl DexFragment { } Instruction::IGetBoolean(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1044,11 +954,10 @@ impl DexFragment { } Instruction::IGetByte(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1056,11 +965,10 @@ impl DexFragment { } Instruction::IGetChar(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1068,11 +976,10 @@ impl DexFragment { } Instruction::IGetShort(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1080,11 +987,10 @@ impl DexFragment { } Instruction::IPut(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1092,11 +998,10 @@ impl DexFragment { } Instruction::IPutWide(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1104,11 +1009,10 @@ impl DexFragment { } Instruction::IPutObject(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1116,11 +1020,10 @@ impl DexFragment { } Instruction::IPutBoolean(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1128,11 +1031,10 @@ impl DexFragment { } Instruction::IPutByte(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1140,11 +1042,10 @@ impl DexFragment { } Instruction::IPutChar(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1152,11 +1053,10 @@ impl DexFragment { } Instruction::IPutShort(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1164,11 +1064,10 @@ impl DexFragment { } Instruction::SGet(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1176,11 +1075,10 @@ impl DexFragment { } Instruction::SGetWide(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1188,11 +1086,10 @@ impl DexFragment { } Instruction::SGetObject(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1200,11 +1097,10 @@ impl DexFragment { } Instruction::SGetBoolean(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1212,11 +1108,10 @@ impl DexFragment { } Instruction::SGetByte(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1224,11 +1119,10 @@ impl DexFragment { } Instruction::SGetChar(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1236,11 +1130,10 @@ impl DexFragment { } Instruction::SGetShort(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1248,11 +1141,10 @@ impl DexFragment { } Instruction::SPut(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1260,11 +1152,10 @@ impl DexFragment { } Instruction::SPutWide(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1272,11 +1163,10 @@ impl DexFragment { } Instruction::SPutObject(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1284,11 +1174,10 @@ impl DexFragment { } Instruction::SPutBoolean(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1296,11 +1185,10 @@ impl DexFragment { } Instruction::SPutByte(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1308,11 +1196,10 @@ impl DexFragment { } Instruction::SPutChar(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1320,11 +1207,10 @@ impl DexFragment { } Instruction::SPutShort(ins) => { let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", + let field_idx = index.fields.get(field).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex fragment", field.__str__(), field.class_.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*field_idx); addr += ins.size() / 2; @@ -1332,11 +1218,10 @@ impl DexFragment { } Instruction::InvokeVirtual(ins) => { let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", + let meth_idx = index.methods.get(meth).ok_or(anyhow!( + "Method {} (method of class {}) not found in dex fragment", meth.__str__(), meth.class_.__str__(), - method_id.__str__() ))?; debug_assert!( *meth_idx <= u16::MAX as usize, @@ -1349,11 +1234,10 @@ impl DexFragment { } Instruction::InvokeSuper(ins) => { let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", + let meth_idx = index.methods.get(meth).ok_or(anyhow!( + "Method {} (method of class {}) not found in dex fragment", meth.__str__(), meth.class_.__str__(), - method_id.__str__() ))?; debug_assert!( *meth_idx <= u16::MAX as usize, @@ -1366,11 +1250,10 @@ impl DexFragment { } Instruction::InvokeDirect(ins) => { let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", + let meth_idx = index.methods.get(meth).ok_or(anyhow!( + "Method {} (method of class {}) not found in dex fragment", meth.__str__(), meth.class_.__str__(), - method_id.__str__() ))?; debug_assert!( *meth_idx <= u16::MAX as usize, @@ -1383,11 +1266,10 @@ impl DexFragment { } Instruction::InvokeStatic(ins) => { let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", + let meth_idx = index.methods.get(meth).ok_or(anyhow!( + "Method {} (method of class {}) not found in dex fragment", meth.__str__(), meth.class_.__str__(), - method_id.__str__() ))?; debug_assert!( *meth_idx <= u16::MAX as usize, @@ -1400,11 +1282,10 @@ impl DexFragment { } Instruction::InvokeInterface(ins) => { let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", + let meth_idx = index.methods.get(meth).ok_or(anyhow!( + "Method {} (method of class {}) not found in dex fragment", meth.__str__(), meth.class_.__str__(), - method_id.__str__() ))?; debug_assert!( *meth_idx <= u16::MAX as usize, @@ -1417,16 +1298,14 @@ impl DexFragment { } Instruction::InvokePolymorphic(ins) => { let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", + let meth_idx = index.methods.get(meth).ok_or(anyhow!( + "Method {} (method of class {}) not found in dex fragment", meth.__str__(), meth.class_.__str__(), - method_id.__str__() ))?; - let proto_idx = self.proto_ids.get(&ins.proto).ok_or(anyhow!( - "Prototype {} (found in code of {}) not found in dex builder", + let proto_idx = index.protos.get(&ins.proto).ok_or(anyhow!( + "Prototype {} (found in code) not found in dex fragment", ins.proto.__str__(), - method_id.__str__() ))?; debug_assert!( *meth_idx <= u16::MAX as usize, @@ -1444,7 +1323,7 @@ impl DexFragment { } Instruction::InvokeCustom(ins) => { let call_site_idx = self.call_site_ids.len(); - self.insert_call_site_item(&ins.call_site)?; + self.insert_call_site_item(&ins.call_site, index)?; let ins = ins.get_raw_ins(call_site_idx); addr += ins.size() / 2; insns.push(ins); @@ -1457,10 +1336,9 @@ impl DexFragment { insns.push(ins); } Instruction::ConstMethodType(ins) => { - let proto_idx = self.proto_ids.get(&ins.proto).ok_or(anyhow!( - "Prototype {} (found in code of {}) not found in dex builder", + let proto_idx = index.protos.get(&ins.proto).ok_or(anyhow!( + "Prototype {} (found in code) not found in dex fragment", ins.proto.__str__(), - method_id.__str__() ))?; let ins = ins.get_raw_ins(*proto_idx); addr += ins.size() / 2; @@ -1468,15 +1346,11 @@ impl DexFragment { } Instruction::Try(try_) => { let end_block_addr = *label_addrs.get(&try_.end_label).ok_or(anyhow!( - "Label {} not found in code of {}, but found try with this label", + "Label {} not found in code, but found try with this label", &try_.end_label, - method_id.__str__() ))?; if end_block_addr < addr { - bail!( - "Found end label of a try block before the try instruction in code of {}", - method_id.__str__() - ) + bail!("Found end label of a try block before the try instruction in code") } let try_item = TryItem { start_addr: addr as u32, @@ -1490,17 +1364,14 @@ impl DexFragment { catch_all_addr: None, }; for (ty, label) in &try_.handlers { - let type_idx = Uleb128(*self.type_ids.get(ty).ok_or(anyhow!( - "Could not found type {} captured by a try block in {}\ - in the dex builder", + let type_idx = Uleb128(*index.types.get(ty).ok_or(anyhow!( + "Could not found type {} in dex fragment captured by a try block", ty.__str__(), - method_id.__str__() ))? as u32); let addr = Uleb128(*label_addrs.get(label).ok_or(anyhow!( - "Label {} not found in code of {}, but found try \ + "Label {} not found in code, but found try \ with this label as catch for type {}", &try_.end_label, - method_id.__str__(), ty.__str__(), ))? as u32); catches @@ -1509,10 +1380,9 @@ impl DexFragment { } if let Some(ref label) = try_.default_handler { let catch_all_addr = *label_addrs.get(label).ok_or(anyhow!( - "Label {} not found in code of {}, but found try \ + "Label {} not found in code, but found try \ with this label as catch all", &try_.end_label, - method_id.__str__() ))?; catches.catch_all_addr = Some(Uleb128(catch_all_addr as u32)); } @@ -1523,10 +1393,9 @@ impl DexFragment { _ => { let ins = ins.get_raw_ins().with_context(|| { format!( - "Failed to convert instruction {} (found in code of {}) to raw instruction", - ins.__str__(), - method_id.__str__() - ) + "Failed to convert instruction {} (found in code) to raw instruction", + ins.__str__(), + ) })?; addr += ins.size() / 2; insns.push(ins); @@ -1550,21 +1419,20 @@ impl DexFragment { } else { let debug_info_off = self .section_manager - .get_aligned_size(Section::DebugInfoItem); - let mut cursor = Cursor::new(code.debug_info.1); + .get_aligned_size(FragSection::DebugInfoItem); + let mut cursor = Cursor::new(&code.debug_info.1); let mut item = DebugInfoItem { line_start: Uleb128(code.debug_info.0), parameter_names: vec![], bytecode: Vec::::deserialize(&mut cursor, DbgBytecode::EndSequence)?, }; - if let Some(parameter_names) = code.parameter_names { - for name in ¶meter_names { + if let Some(parameter_names) = &code.parameter_names { + for name in parameter_names { if let Some(name) = name { item.parameter_names - .push(Uleb128p1(*self.strings.get(name).ok_or(anyhow!( - "String {} (name of param of {}) not found", + .push(Uleb128p1(*index.strings.get(name).ok_or(anyhow!( + "String {} (name of parameter) not found", name.__str__(), - method_id.__str__() ))? as u32)); } else { item.parameter_names.push(NO_INDEX); @@ -1572,7 +1440,7 @@ impl DexFragment { } } self.section_manager - .add_elt(Section::DebugInfoItem, Some(item.size())); + .add_elt(FragSection::DebugInfoItem, Some(item.size())); self.debug_info_items.push(item); debug_info_off + 1 }; @@ -1591,7 +1459,7 @@ impl DexFragment { handlers, }; self.section_manager - .add_elt(Section::CodeItem, Some(item.size())); + .add_elt(FragSection::CodeItem, Some(item.size())); self.code_items.push(item); Ok(()) } @@ -1609,15 +1477,14 @@ impl DexFragment { /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow /// to distinguish the offset of the first item (equal to zero before linking) and the value /// 0 used to indicate an abscence of item. - fn insert_class_data_item(&mut self, class_id: &IdType) -> Result<()> { + fn insert_class_data_item(&mut self, class: &Class, index: &FragIndex) -> Result<()> { let mut data = ClassDataItem::default(); - let (class, _) = self.class_defs.get(class_id).unwrap(); let mut static_fields: Vec = class.static_fields.keys().cloned().collect(); static_fields.sort(); let mut last_field_id = 0; for id in &static_fields { - let idx = self.field_ids.get(id).ok_or(anyhow!( + let idx = index.fields.get(id).ok_or(anyhow!( "Field {} (field of class {}) not found in dex builder", id.__str__(), class.__str__() @@ -1635,7 +1502,7 @@ impl DexFragment { instance_fields.sort(); let mut last_field_id = 0; for id in &instance_fields { - let idx = self.field_ids.get(id).ok_or(anyhow!( + let idx = index.fields.get(id).ok_or(anyhow!( "Field {} (field of class {}) not found in dex builder", id.__str__(), class.__str__() @@ -1660,8 +1527,7 @@ impl DexFragment { let mut last_method_id = 0; for id in &direct_methods { // &mut vs & of self and class make things difficult... - let (class, _) = self.class_defs.get(class_id).unwrap(); - let idx = self.method_ids.get(id).ok_or(anyhow!( + let idx = index.methods.get(id).ok_or(anyhow!( "Method {} (method of class {}) not found in dex builder", id.__str__(), class.__str__() @@ -1670,10 +1536,11 @@ impl DexFragment { last_method_id = *idx; let access_flags = Uleb128(class.direct_methods.get(id).unwrap().get_raw_access_flags()); - // No if let because ownership gunfooterie - let code_off = if class.direct_methods.get(id).unwrap().code.is_some() { - let code_off = self.section_manager.get_aligned_size(Section::CodeItem); - self.insert_code_item(id.clone(), true)?; + let code_off = if let Some(code) = &class.direct_methods.get(id).unwrap().code { + let code_off = self.section_manager.get_aligned_size(FragSection::CodeItem); + self.insert_code_item(code, index).with_context(|| { + format!("Failed to convert serialize code of {}", id.__str__()) + })?; Uleb128(code_off + 1) } else { Uleb128(0) @@ -1685,13 +1552,11 @@ impl DexFragment { }); } - let (class, _) = self.class_defs.get(class_id).unwrap(); let mut virtual_methods: Vec = class.virtual_methods.keys().cloned().collect(); virtual_methods.sort(); let mut last_method_id = 0; for id in &virtual_methods { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let idx = self.method_ids.get(id).ok_or(anyhow!( + let idx = index.methods.get(id).ok_or(anyhow!( "Method {} (method of class {}) not found in dex builder", id.__str__(), class.__str__() @@ -1705,10 +1570,11 @@ impl DexFragment { .unwrap() .get_raw_access_flags(), ); - // No if let because ownership gunfooterie - let code_off = if class.virtual_methods.get(id).unwrap().code.is_some() { - let code_off = self.section_manager.get_aligned_size(Section::CodeItem); - self.insert_code_item(id.clone(), false)?; + let code_off = if let Some(code) = &class.virtual_methods.get(id).unwrap().code { + let code_off = self.section_manager.get_aligned_size(FragSection::CodeItem); + self.insert_code_item(code, index).with_context(|| { + format!("Failed to convert serialize code of {}", id.__str__()) + })?; Uleb128(code_off + 1) } else { Uleb128(0) @@ -1720,9 +1586,9 @@ impl DexFragment { }); } self.section_manager - .add_elt(Section::ClassDataItem, Some(data.size())); + .add_elt(FragSection::ClassDataItem, Some(data.size())); //assert_eq!(data.size(), data.serialize_to_vec().unwrap().len()); - self.class_data_list.push(data); + self.class_data = Some(data); Ok(()) } @@ -1731,7 +1597,7 @@ impl DexFragment { /// # Warning /// /// This method can insert element in the dex file like method_handles. - pub fn dex_value_to_encoded_value( + fn dex_value_to_encoded_value( &mut self, value: &DexValue, index: &FragIndex, @@ -1832,7 +1698,7 @@ impl DexFragment { /// # Warning /// /// This method can insert element in the dex file like method_handles. - pub fn insert_call_site_item(&mut self, call_site: &CallSite) -> Result<()> { + fn insert_call_site_item(&mut self, call_site: &CallSite, index: &FragIndex) -> Result<()> { let mut values = vec![]; values.push(DexValue::MethodHandle(call_site.method_handle.clone())); values.push(DexValue::String(call_site.name.clone())); @@ -1841,15 +1707,15 @@ impl DexFragment { self.call_site_ids.push(CallSiteIdItem { call_site_off: self .section_manager - .get_aligned_size(Section::EncodedArrayItem), - }); // linked in link_call_site_ids() - self.section_manager.add_elt(Section::CallSiteIdItem, None); - self.insert_encoded_array_item(DexArray(values)) + .get_aligned_size(FragSection::EncodedArrayItem), + }); + self.section_manager + .add_elt(FragSection::CallSiteIdItem, None); + self.insert_encoded_array_item(DexArray(values), index) } /// Insert the encoded_array_item encoding the static_values of a class. - fn insert_class_static_values(&mut self, class_id: &IdType) -> Result<()> { - let (class, _) = self.class_defs.get(class_id).unwrap(); + fn insert_class_static_values(&mut self, class: &Class, index: &FragIndex) -> Result<()> { let mut static_fields: Vec = class.static_fields.keys().cloned().collect(); static_fields.sort(); let mut array = vec![]; @@ -1868,15 +1734,15 @@ impl DexFragment { "The type {} (for field {} in class {}) does not have a default value", field.descriptor.type_.__str__(), field.descriptor.__str__(), - class_id.__str__() + class.__str__() ))?); } } - self.insert_encoded_array_item(DexArray(array)) + self.insert_encoded_array_item(DexArray(array), index) .with_context(|| { format!( "Failed to serialize static values of class {}", - class_id.__str__() + class.__str__() ) }) } @@ -1898,7 +1764,7 @@ impl DexFragment { "{} (annotation element name) not found in dex builder", name.__str__() ))? as u32), - value: self.dex_value_to_encoded_value(elt)?, + value: self.dex_value_to_encoded_value(elt, index)?, }); } Ok(EncodedAnnotation { @@ -1986,612 +1852,6 @@ impl DexFragment { self.annotation_set_lists.push(list); Ok(()) } - - fn gen_type_list_section(&mut self) -> Result<()> { - debug!("Generate the type_list section"); - // Collect all type lists - for proto in self.proto_ids.keys() { - if !proto.parameters.is_empty() { - let type_list = self.gen_type_list(&proto.parameters).with_context(|| { - format!("Failed to generate param list for {}", proto.__str__()) - })?; - self.type_lists_index.insert(type_list, 0); - } - } - for (class, _) in self.class_defs.values() { - if !class.interfaces.is_empty() { - let type_list = self.gen_type_list(&class.interfaces).with_context(|| { - format!("Failed to generate interface list for {}", class.__str__()) - })?; - self.type_lists_index.insert(type_list, 0); - } - } - - // safe type lists with their offset in the section - let mut offset = 0; - for (i, (list, idx)) in self.type_lists_index.iter_mut().enumerate() { - while offset % 4 != 0 { - // Alignment - self.section_manager.incr_section_size(Section::TypeList, 1); - offset += 1; - } - *idx = i; - self.type_lists_with_offset.push((list.clone(), offset)); - self.section_manager - .add_elt(Section::TypeList, Some(list.size())); - offset += list.size() as u32; - } - // The next section requires alignment to 4 - while offset % 4 != 0 { - // Alignment - self.section_manager.incr_section_size(Section::TypeList, 1); - offset += 1; - } - Ok(()) - } - - /// Generate the map list. - /// - /// # Warning - /// - /// All sections must be generated (but not linked) before generating the map list. - /// - /// This method switch the section manager from edit mode to read only. - fn gen_map_list(&mut self) -> Result<()> { - debug!("Generate the map_list"); - // Get the size of a map item - let map_item_size = 12; /* = MapItem { - type_: MapItemType::HeaderItem, - unused: 0, - size: 0, - offset: 0, - } - .size(); */ - // Empty map has a size 4, then we add the size of a MapItem for each element - // The size of the map_list must be computed before generating the map list, - // as it affect the offset of some sections. - self.section_manager.add_elt(Section::MapList, Some(4)); - for section in Section::VARIANT_LIST { - if !section.is_data() && self.section_manager.get_nb_elt(*section) != 0 { - self.section_manager - .incr_section_size(Section::MapList, map_item_size); - } - } - // All sections are knowns and should not be eddited anymore - self.section_manager.finalize_sections(); - for section in Section::VARIANT_LIST { - if !section.is_data() && self.section_manager.get_nb_elt(*section) != 0 { - /* - match section { - // Alignment - // Until Section::MapList included, the section are naturally alligned to 4 - _ => (), - } - */ - self.map_list.list.push(MapItem { - type_: section.get_map_item_type(), - unused: 0, - size: self.section_manager.get_nb_elt(*section) as u32, - offset: self.section_manager.get_offset(*section), - }); - } - } - Ok(()) - } - - /// Link the offsets in the header. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_header(&mut self) { - debug!("Link the header section"); - self.header.map_off = self.section_manager.get_offset(Section::MapList); - self.header.string_ids_size = self.section_manager.get_nb_elt(Section::StringIdItem) as u32; - self.header.string_ids_off = self.section_manager.get_offset(Section::StringIdItem); - self.header.type_ids_size = self.section_manager.get_nb_elt(Section::TypeIdItem) as u32; - self.header.type_ids_off = self.section_manager.get_offset(Section::TypeIdItem); - self.header.proto_ids_size = self.section_manager.get_nb_elt(Section::ProtoIdItem) as u32; - self.header.proto_ids_off = self.section_manager.get_offset(Section::ProtoIdItem); - self.header.field_ids_size = self.section_manager.get_nb_elt(Section::FieldIdItem) as u32; - self.header.field_ids_off = self.section_manager.get_offset(Section::FieldIdItem); - self.header.method_ids_size = self.section_manager.get_nb_elt(Section::MethodIdItem) as u32; - self.header.method_ids_off = self.section_manager.get_offset(Section::MethodIdItem); - self.header.class_defs_size = self.section_manager.get_nb_elt(Section::ClassDefItem) as u32; - self.header.class_defs_off = self.section_manager.get_offset(Section::ClassDefItem); - self.header.data_size = self.section_manager.get_unaligned_size(Section::Data); - self.header.data_off = self.section_manager.get_offset(Section::Data); - } - - /// Link the offsets in the call site id items. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_call_site_ids(&mut self) { - debug!("Link call site id items"); - for id in &mut self.call_site_ids { - id.call_site_off += self.section_manager.get_offset(Section::EncodedArrayItem); - } - } - - /// Link the offsets in class_def_items. - /// - /// # Warning - /// - /// This is the only link method called before generating the map list and finilizing the - /// section: - /// - /// Linking can only occur once all sections are entirelly generated, however, - /// `class_data_item.direct|virtual_methods[.].code_off` are Uleb128 encoded, meaning - /// that linking class_data_item modify the size of the class_data_items, hence the position - /// of the class_data_item and all element located after, as well as the size of the data - /// section. This is pretty bothersome and means that the sections **are** modified. - fn link_class_data(&mut self) -> Result<()> { - debug!("Link class data items"); - let mut unlinked_local_offset = 0; - let mut linked_local_offset = 0; - let code_section_off = self.section_manager.get_code_item_offset_prefinalized(); - for data in self.class_data_list.iter_mut() { - let unlinked_size = data.size() as u32; - for method in &mut data.direct_methods { - if method.code_off.0 != 0 { - method.code_off.0 += code_section_off - 1; - } - } - for method in &mut data.virtual_methods { - if method.code_off.0 != 0 { - method.code_off.0 += code_section_off - 1; - } - } - self.corrected_class_data_offset - .insert(unlinked_local_offset, linked_local_offset); - linked_local_offset += data.size() as u32; - unlinked_local_offset += unlinked_size; - } - self.section_manager.incr_section_size( - Section::ClassDataItem, - linked_local_offset as usize - unlinked_local_offset as usize, - ); - Ok(()) - } - - /// Link the offsets in proto_id_items. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_proto_id(&mut self) -> Result<()> { - debug!("Link proto id items"); - for (proto, idx) in &self.proto_ids { - if !proto.parameters.is_empty() { - let type_list = self.gen_type_list(&proto.parameters).with_context(|| { - format!("Failed to generate param list for {}", proto.__str__()) - })?; - let offset = self.section_manager.get_offset(Section::TypeList) - + self.type_lists_with_offset[*self.type_lists_index.get(&type_list).unwrap()] - .1; - self.proto_ids_list[*idx].parameters_off = offset; - } - } - Ok(()) - } - - /// Link the offsets of class_data_items in class_def_items. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_class_def(&mut self) -> Result<()> { - debug!("Link class_def_items"); - for class_def in self.class_defs_list.iter_mut() { - // Link the class_data_item entries - // prelink value is set to offset in the section + 1 (to distinguish with 0) - if class_def.class_data_off != 0 { - let unlinked_local_offset = class_def.class_data_off - 1; - let linked_local_offset = *self - .corrected_class_data_offset - .get(&unlinked_local_offset) - .expect( - "Unlinked class_data_item offset not found in corrected_class_data_offset", - ); - class_def.class_data_off = - self.section_manager.get_offset(Section::ClassDataItem) + linked_local_offset; - } - // Link the annotations_directory_item entrie - // prelink value is set to offset in the section + 1 (to distinguish with 0) - if class_def.annotations_off != 0 { - class_def.annotations_off += self - .section_manager - .get_offset(Section::AnnotationsDirectoryItem) - - 1; - } - - // Link the static_values entries - if class_def.static_values_off != 0 { - class_def.static_values_off += - self.section_manager.get_offset(Section::EncodedArrayItem) - 1; - } - } - for (cls, idx) in self.class_defs.values() { - if !cls.interfaces.is_empty() { - let type_list = self.gen_type_list(&cls.interfaces).with_context(|| { - format!("Failed to generate interface list for {}", cls.__str__()) - })?; - let offset = self.section_manager.get_offset(Section::TypeList) - + self.type_lists_with_offset[*self.type_lists_index.get(&type_list).unwrap()] - .1; - self.class_defs_list[*idx].interfaces_off = offset; - } - } - Ok(()) - } - - /// Link the offset of debug info item in code items. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_code(&mut self) { - debug!("Link the debug_info_off entries in code_items"); - for code in self.code_items.iter_mut() { - if code.debug_info_off != 0 { - code.debug_info_off += self.section_manager.get_offset(Section::DebugInfoItem) - 1; - } - } - } - - /// Link all annotations objects. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_annotations(&mut self) { - for annotation in self.annotations_directory_items.iter_mut() { - if annotation.class_annotations_off != 0 { - annotation.class_annotations_off += - self.section_manager.get_offset(Section::AnnotationSetItem) - 1; - } - for field_annotation in annotation.field_annotations.iter_mut() { - if field_annotation.annotations_off != 0 { - field_annotation.annotations_off += - self.section_manager.get_offset(Section::AnnotationSetItem) - 1; - } - } - for method_annotation in annotation.method_annotations.iter_mut() { - if method_annotation.annotations_off != 0 { - method_annotation.annotations_off += - self.section_manager.get_offset(Section::AnnotationSetItem) - 1; - } - } - for parameter_annotation in annotation.parameter_annotations.iter_mut() { - if parameter_annotation.annotations_off != 0 { - parameter_annotation.annotations_off += self - .section_manager - .get_offset(Section::AnnotationSetRefList) - - 1; - } - } - } - for annotation_set in self.annotation_set_items.iter_mut() { - for entry in annotation_set.entries.iter_mut() { - entry.annotation_off += self.section_manager.get_offset(Section::AnnotationItem); - } - } - for list in self.annotation_set_lists.iter_mut() { - for annotation in list.list.iter_mut() { - if annotation.annotations_off != 0 { - annotation.annotations_off += - self.section_manager.get_offset(Section::AnnotationSetItem) - 1; - } - } - } - } - - fn write_dex_file(&mut self, writer: &mut dyn Write) -> Result<()> { - self.section_manager.reset(); - self.section_manager.add_elt(Section::HeaderItem, None); - - self.gen_string_data_section()?; - self.gen_type_ids_section()?; - self.gen_proto_ids_section()?; - self.gen_field_ids_section()?; - self.gen_method_ids_section()?; - - debug!("Sort classes and generate the class_defs and class_data section"); - for class_id in self.get_sorted_class_def()? { - self.insert_class_def_item(&class_id)?; - } - self.gen_type_list_section()?; - - // start by linking class_data_items to populate self.corrected_class_data_offset - // and update the class_data_item sections size. - // Why before gen_map_list? Because the offsets in class_data_items are F***ing Uleb128 - // encoded, so there size change when linking (see doc of self.corrected_class_data_offset). - let code_offset = self.section_manager.get_code_item_offset_prefinalized(); - self.link_class_data()?; - self.gen_map_list()?; - assert_eq!( - code_offset, - self.section_manager.get_offset(Section::CodeItem), - "Prelinking computed value and post linking value for \ - the offset of the code_item section don't match" - ); - - // From now on, all section are generated and the value in section_manager do not change, - - self.link_header(); - self.link_call_site_ids(); - self.link_proto_id()?; - self.link_class_def()?; - self.link_code(); - self.link_annotations(); - - debug!("Serialize the dex file"); - let mut buffer = Cursor::new(Vec::::new()); - - self.check_section_offset(&buffer, Section::HeaderItem); - Self::fix_section_alignement(&mut buffer, Section::HeaderItem)?; - self.header.serialize(&mut buffer)?; - // StringIdItem section - let mut string_off = self.section_manager.get_offset(Section::StringDataItem); - self.check_section_offset(&buffer, Section::StringIdItem); - for string in self.string_data_list.iter() { - let str_id = StringIdItem { - string_data_off: string_off, - }; - Self::fix_section_alignement(&mut buffer, Section::StringIdItem)?; - str_id.serialize(&mut buffer)?; - string_off += string.size() as u32; - } - // TypeId section - self.check_section_offset(&buffer, Section::TypeIdItem); - for ty in &self.type_ids_list { - Self::fix_section_alignement(&mut buffer, Section::TypeIdItem)?; - ty.serialize(&mut buffer)?; - } - // ProtoId section - self.check_section_offset(&buffer, Section::ProtoIdItem); - for proto in &self.proto_ids_list { - Self::fix_section_alignement(&mut buffer, Section::ProtoIdItem)?; - proto.serialize(&mut buffer)?; - } - // FieldIdItem section - self.check_section_offset(&buffer, Section::FieldIdItem); - for field_id in &self.field_ids_list { - Self::fix_section_alignement(&mut buffer, Section::FieldIdItem)?; - field_id.serialize(&mut buffer)?; - } - // MethodIdItem section - self.check_section_offset(&buffer, Section::MethodIdItem); - for method_id in &self.method_ids_list { - Self::fix_section_alignement(&mut buffer, Section::MethodIdItem)?; - method_id.serialize(&mut buffer)?; - } - // ClassDefItem section - self.check_section_offset(&buffer, Section::ClassDefItem); - for class_def in &self.class_defs_list { - Self::fix_section_alignement(&mut buffer, Section::ClassDefItem)?; - class_def.serialize(&mut buffer)?; - } - // CallSiteIdItem, data are inserted as encoded array item later - self.check_section_offset(&buffer, Section::CallSiteIdItem); - for call_site_id in &self.call_site_ids { - Self::fix_section_alignement(&mut buffer, Section::CallSiteIdItem)?; - call_site_id.serialize(&mut buffer)?; - } - - // MethodHandleItem section - self.check_section_offset(&buffer, Section::MethodHandleItem); - for handle in &self.method_handles { - Self::fix_section_alignement(&mut buffer, Section::MethodHandleItem)?; - handle.serialize(&mut buffer)?; - } - // MapList - self.check_section_offset(&buffer, Section::Data); - self.check_section_offset(&buffer, Section::MapList); - Self::fix_section_alignement(&mut buffer, Section::MapList)?; - self.map_list.serialize(&mut buffer)?; - // TypeList, - self.check_section_offset(&buffer, Section::TypeList); - for (list, _) in &self.type_lists_with_offset { - Self::fix_section_alignement(&mut buffer, Section::TypeList)?; - list.serialize(&mut buffer)?; - } - // AnnotationSetRefList section - self.check_section_offset(&buffer, Section::AnnotationSetRefList); - for list in &self.annotation_set_lists { - Self::fix_section_alignement(&mut buffer, Section::AnnotationSetRefList)?; - list.serialize(&mut buffer)?; - } - // AnnotationSetItem section - self.check_section_offset(&buffer, Section::AnnotationSetItem); - for set in &self.annotation_set_items { - Self::fix_section_alignement(&mut buffer, Section::AnnotationSetItem)?; - set.serialize(&mut buffer)?; - } - // CodeItem section - self.check_section_offset(&buffer, Section::CodeItem); - for code_item in &self.code_items { - Self::fix_section_alignement(&mut buffer, Section::CodeItem)?; - code_item.serialize(&mut buffer)? - } - // StringDataItem section - self.check_section_offset(&buffer, Section::StringDataItem); - for string in &self.string_data_list { - Self::fix_section_alignement(&mut buffer, Section::StringDataItem)?; - string.serialize(&mut buffer)?; - } - // DebugInfoItem section - self.check_section_offset(&buffer, Section::DebugInfoItem); - for debug_info in &self.debug_info_items { - Self::fix_section_alignement(&mut buffer, Section::DebugInfoItem)?; - debug_info.serialize(&mut buffer)?; - } - // AnnotationItem section - self.check_section_offset(&buffer, Section::AnnotationItem); - for annot in &self.annotation_items { - Self::fix_section_alignement(&mut buffer, Section::AnnotationItem)?; - annot.serialize(&mut buffer)?; - } - // EncodedArrayItem section - self.check_section_offset(&buffer, Section::EncodedArrayItem); - for array in &self.encoded_array_items { - Self::fix_section_alignement(&mut buffer, Section::EncodedArrayItem)?; - array.serialize(&mut buffer)?; - } - // AnnotationsDirectoryItem section - self.check_section_offset(&buffer, Section::AnnotationsDirectoryItem); - for dir in &self.annotations_directory_items { - Self::fix_section_alignement(&mut buffer, Section::AnnotationsDirectoryItem)?; - dir.serialize(&mut buffer)?; - } - // ClassDataItem section - self.check_section_offset(&buffer, Section::ClassDataItem); - for data in &self.class_data_list { - Self::fix_section_alignement(&mut buffer, Section::ClassDataItem)?; - data.serialize(&mut buffer)?; - } - // TODO: HiddenapiClassDataItem, - /* - self.check_section_offset(&buffer, Section::HiddenapiClassDataItem); - Self::fix_section_alignement(&mut buffer, Section::HiddenapiClassDataItem)?; - */ - - let end_data = buffer.position(); - assert_eq!( - end_data as u32, - self.header.data_off + self.header.data_size - ); - - // compute signature - buffer.seek(SeekFrom::Start(8 + 4 + 20))?; - let mut hasher = Sha1::new(); - io::copy(&mut buffer, &mut hasher)?; - self.header.signature = hasher.finalize().into(); - let size = buffer.seek(SeekFrom::End(0))? as u32; - self.header.file_size = size; - buffer.rewind()?; - self.header.serialize(&mut buffer)?; - - // Compute checksum - //buffer.seek(SeekFrom::Start(8 + 4))?; - let mut adler = Adler32::new(); - adler.write_slice(&buffer.get_ref()[8 + 4..]); - self.header.checksum = adler.checksum(); - buffer.rewind()?; - self.header.serialize(&mut buffer)?; - - // copy buffer to output - buffer.rewind()?; - io::copy(&mut buffer, writer)?; - - Ok(()) - } - - /// Insert 0 to a buffer until the right alignment is reached for an element of the - /// given section. - fn fix_section_alignement(buffer: &mut Cursor>, section: Section) -> Result<()> { - while buffer.position() % section.get_item_alignment() as u64 != 0 { - Serializable::serialize(&0u8, buffer)?; - } - Ok(()) - } - - /// Check if a section - fn check_section_offset(&self, buffer: &Cursor, section: Section) { - let mut pos = buffer.position(); - while pos % section.get_item_alignment() as u64 != 0 { - pos += 1; - } - let expected = self.section_manager.get_offset(section) as u64; - assert_eq!( - pos, expected, - "Computed section offset and actual section offset do not match for section \ - {section:?}, expected 0x{expected:x}, found 0x{pos:x}" - ); - } - - /// Compute the order of the classes in the section `class_defs`. - /// Class definitions must be sorted so that a class's superclass and interfaces - /// are before the class. - fn get_sorted_class_def(&self) -> Result> { - // Use Kahn's algorithm - let mut graph: HashMap<&IdType, (HashSet<&IdType>, HashSet<&IdType>)> = HashMap::new(); - for (ty, (def, _)) in &self.class_defs { - let mut edges_to = HashSet::new(); - if let Some(sup) = def.superclass.as_ref() { - if self.class_defs.get(sup).is_some() { - edges_to.insert(sup); - } - } - for sup in &def.interfaces { - if self.class_defs.get(sup).is_some() { - edges_to.insert(sup); - } - } - for n_to in &edges_to { - let (from, _) = graph - .entry(n_to) - .or_insert((HashSet::new(), HashSet::new())); - from.insert(ty); - } - let (_, to) = graph.entry(ty).or_insert((HashSet::new(), HashSet::new())); - to.extend(edges_to); - } - - let mut sorted = vec![]; - let mut no_outgoing: VecDeque<&IdType> = VecDeque::new(); - no_outgoing.extend( - graph - .iter() - .filter(|(_, (_, to))| to.is_empty()) - .map(|(ty, _)| ty), - ); - if no_outgoing.is_empty() { - bail!("The class inheritance topoloy is either empty or cyclic"); - } - - while let Some(n) = no_outgoing.pop_front() { - sorted.push(n.clone()); - let (from, _) = graph.get(n).cloned().unwrap(); - for n_from in from { - graph.entry(n_from).and_modify(|(_, to)| _ = to.remove(n)); - let (_, to) = graph.get(n_from).unwrap(); - if to.is_empty() { - no_outgoing.push_back(n_from); - } - } - graph - .entry(n) - .and_modify(|(from, _)| *from = HashSet::new()); - } - for (_, (from, to)) in graph { - if !from.is_empty() || !to.is_empty() { - bail!("The class inheritance topology is cyclic"); - } - } - Ok(sorted) - } - - fn gen_type_list(&self, list: &[IdType]) -> Result { - let mut type_list = TypeList { list: vec![] }; - for ty in list { - type_list.list.push(TypeItem { - type_idx: *self.type_ids.get(ty).ok_or(anyhow!( - "Could not found type {} in dex builder", - ty.__str__() - ))? as u16, - }); - } - Ok(type_list) - } - - pub fn gen_dex_file_to_vec(&mut self) -> Result> { - let mut output = Cursor::new(Vec::::new()); - self.write_dex_file(&mut output)?; - Ok(output.into_inner()) - } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -2712,7 +1972,7 @@ impl FragSection { struct FragSectionManager { sizes: [u32; Self::NB_SECTION], nb_elt: [usize; Self::NB_SECTION], - offsets: [u32; Self::NB_SECTION], + //offsets: [u32; Self::NB_SECTION], editable: bool, } @@ -2722,7 +1982,7 @@ impl FragSectionManager { fn reset(&mut self) { self.sizes = [0; Self::NB_SECTION]; self.nb_elt = [0; Self::NB_SECTION]; - self.offsets = [0; Self::NB_SECTION]; + //self.offsets = [0; Self::NB_SECTION]; self.editable = true; } @@ -2858,6 +2118,7 @@ impl FragSectionManager { "{section:?}: 0x{offset:x} -> 0x{new_offset:x} (size: 0x{size:x}, \ nb elt: {nb_elt})" ); + offset = new_offset; } } } From 609e6c69cdfd0df11d29d185d1e4784495f356a6 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Thu, 21 Mar 2024 14:20:44 +0100 Subject: [PATCH 06/14] wip --- androscalpel/src/dex_fragment.rs | 264 ++- androscalpel/src/dex_writer.rs | 3315 +----------------------------- 2 files changed, 336 insertions(+), 3243 deletions(-) diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs index 27bc792..aaea8db 100644 --- a/androscalpel/src/dex_fragment.rs +++ b/androscalpel/src/dex_fragment.rs @@ -45,12 +45,6 @@ pub struct DexFragment { class_def: ClassDefItem, /// The class_data. class_data: Option, - // TODO: type list should be handle like other ids - // TODO: type list inside of proto ids are not handled here - // /// The type lists found in the classes associated to their index in the type_lists section. - // type_lists_index: HashMap, - // /// The type_lists section and the offset of the lists inside the section. - // type_lists_with_offset: Vec<(TypeList, u32)>, /// The encoded_array_items section. encoded_array_items: Vec, /// The annotations_directory_item. @@ -69,9 +63,40 @@ pub struct DexFragment { debug_info_items: Vec, /// The list of interfaces of the class. interfaces: Vec, + /// The current link state of the fragment. + link_state: FragLinkState, } impl DexFragment { + /// The strings in the dex fragment, sorted. + pub fn strings(&self) -> &[DexString] { + &self.strings + } + /// The types in the dex fragment, sorted. + pub fn type_ids(&self) -> &[IdType] { + &self.type_ids + } + /// The prototypes in the dex fragment, sorted. + pub fn proto_ids(&self) -> &[IdMethodType] { + &self.proto_ids + } + /// The field ids in the dex fragment, sorted. + pub fn field_ids(&self) -> &[IdField] { + &self.field_ids + } + /// The methods ids in the dex fragment, sorted. + pub fn method_ids(&self) -> &[IdMethod] { + &self.method_ids + } + /// The list of interfaces of the class. + pub fn interfaces(&self) -> &[IdType] { + &self.interfaces + } + /// The method_handles section. + pub fn method_handles(&self) -> &[MethodHandleItem] { + &self.method_handles + } + pub fn new(class: &Class) -> Result { debug!( "Building dex fragment for class {}", @@ -108,6 +133,7 @@ impl DexFragment { annotation_set_lists: vec![], debug_info_items: vec![], interfaces: class.interfaces.clone(), + link_state: FragLinkState::Unlinked, }; frag.strings = class.get_all_strings().into_iter().collect(); frag.strings.sort(); @@ -1852,6 +1878,168 @@ impl DexFragment { self.annotation_set_lists.push(list); Ok(()) } + + /// Some structure change size depending of the value of the index they refere to. + /// The new size needs to be known before the offset can be linked, so the idx are + /// linked before. + pub fn link_global_ids( + &mut self, + global_strings: &[DexString], + global_type_ids: &[IdType], + global_proto_ids: &[IdMethodType], + global_field_ids: &[IdField], + global_method_ids: &[IdMethod], + nb_method_handle_before_fragment: usize, + ) -> Result<()> { + self.link_state.start_linking_idx()?; + let string_reindex = Vec::with_capacity(self.strings.len()); + let mut global_idx = 0; + for s in self.strings { + while global_idx < global_strings.len() && global_strings[global_idx] != s { + global_idx += 1; + } + if global_idx == global_strings.len() { + bail!("String {} not found in global index", s.__str__()); + } + string_reindex.push(global_idx as u32); + } + let type_reindex = Vec::with_capacity(self.type_ids.len()); + let mut global_idx = 0; + for ty in self.type_ids { + while global_idx < global_type_ids.len() && global_type_ids[global_idx] != ty { + global_idx += 1; + } + if global_idx == global_type_ids.len() { + bail!("Type {} not found in global index", ty.__str__()); + } + type_reindex.push(global_idx as u32); + } + let proto_reindex = Vec::with_capacity(self.proto_ids.len()); + let mut global_idx = 0; + for proto in self.proto_ids { + while global_idx < global_proto_ids.len() && global_proto_ids[global_idx] != proto { + global_idx += 1; + } + if global_idx == global_proto_ids.len() { + bail!("Prototype {} not found in global index", proto.__str__()); + } + proto_reindex.push(global_idx as u32); + } + let field_reindex = Vec::with_capacity(self.field_ids.len()); + let mut global_idx = 0; + for field in self.field_ids { + while global_idx < global_field_ids.len() && global_field_ids[global_idx] != field { + global_idx += 1; + } + if global_idx == global_field_ids.len() { + bail!("Field {} not found in global index", field.__str__()); + } + field_reindex.push(global_idx as u16); + } + let method_reindex = Vec::with_capacity(self.method_ids.len()); + let mut global_idx = 0; + for meth in self.method_ids { + while global_idx < global_method_ids.len() && global_method_ids[global_idx] != meth { + global_idx += 1; + } + if global_idx == global_method_ids.len() { + bail!("Method {} not found in global index", meth.__str__()); + } + method_reindex.push(global_idx as u16); + } + + self.link_id_class_def(&string_reindex, &type_reindex); + self.link_id_method_handle(&field_reindex, &method_reindex); + + todo!() + } + + fn link_id_class_def(&mut self, string_reindex: &[u32], type_reindex: &[u32]) { + self.class_def.class_idx = type_reindex[self.class_def.class_idx as usize]; + self.class_def.superclass_idx = type_reindex[self.class_def.superclass_idx as usize]; + self.class_def.source_file_idx = string_reindex[self.class_def.source_file_idx as usize]; + } + + fn link_id_method_handle(&mut self, field_reindex: &[u16], method_reindex: &[u16]) { + for handle in self.method_handles { + let reindexer = match handle.method_handle_type { + MethodHandleType::StaticPut => field_reindex, + MethodHandleType::StaticGet => field_reindex, + MethodHandleType::InstancePut => field_reindex, + MethodHandleType::InstanceGet => field_reindex, + MethodHandleType::InvokeStatic => method_reindex, + MethodHandleType::InvokeInstance => method_reindex, + MethodHandleType::InvokeConstructor => method_reindex, + MethodHandleType::InvokeDirect => method_reindex, + MethodHandleType::InvokeInterface => method_reindex, + }; + handle.field_or_method_id = reindexer[handle.field_or_method_id as usize]; + } + } + + fn link_id_code(&mut self, string_reindex: &[u32], type_reindex: &[u32]) { + let mut total_size = 0; + let mut code_item_relocation = if let FragLinkState::LinkedIdx { + code_item_relocation, + .. + } = self.link_state + { + code_item_relocation + } else { + // link_global_ids() should prevent that + panic!("link_id_code should not be run outside of fn link_global_ids(..)"); + }; + for code in self.code_items { + let current_size = code.size(); + for ins in &mut code.insns { + Self::link_id_ins(ins, string_reindex); + } + // TODO: TryItem recompute handler_off + if let Some(handlers) = code.handlers { + let mut handler_off_reindex = HashMap::new(); + let mut current_offset = handlers.size_field().size(); + let mut old_offset = handlers.size_field().size(); + for handlers in handlers.list { + handler_off_reindex.insert(old_offset as u16, current_offset as u16); + old_offset += handlers.size(); + for handler in handlers.handlers { + handler.type_idx.0 = type_reindex[handler.type_idx.0 as usize]; + } + current_offset += handlers.size(); + } + for try_ in code.tries { + try_.handler_off = *handler_off_reindex + .get(&try_.handler_off) + .expect("Something whent wrong with the handle reindexing"); + } + } + } + } + fn link_id_ins(ins: &mut InsFormat, string_reindex: &[u32]) { + match ins { + InsFormat::Format31C { op: 0x1b, b, .. } => *b = string_reindex[b as usize], + InsFormat::Format21C { op: 0x1a, b, .. } => todo!(), // TODO FUCK this + _ => todo!(), + } + } + fn link_id_string_data(&mut self) { + todo!() + } + fn link_id_debug_info(&mut self) { + todo!() + } + fn link_id_annotation(&mut self) { + todo!() + } + fn link_id_encoded_array(&mut self) { + todo!() + } + fn link_id_annotation_dir(&mut self) { + todo!() + } + fn link_id_class_data(&mut self) { + todo!() + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -1859,7 +2047,6 @@ enum FragSection { ClassDefItem, CallSiteIdItem, MethodHandleItem, - TypeList, AnnotationSetRefList, AnnotationSetItem, CodeItem, @@ -1877,7 +2064,6 @@ impl FragSection { Self::ClassDefItem, Self::CallSiteIdItem, Self::MethodHandleItem, - Self::TypeList, Self::AnnotationSetRefList, Self::AnnotationSetItem, Self::CodeItem, @@ -1886,8 +2072,7 @@ impl FragSection { Self::AnnotationItem, Self::EncodedArrayItem, Self::AnnotationsDirectoryItem, - // must be last because contains offsets in Uleb, - // so size change when linking ! + // /!\ contains offsets in Uleb, so size change when linking ! Self::ClassDataItem, // Self::HiddenapiClassDataItem, ]; @@ -1897,17 +2082,16 @@ impl FragSection { Self::ClassDefItem => 0, Self::CallSiteIdItem => 1, Self::MethodHandleItem => 2, - Self::TypeList => 3, - Self::AnnotationSetRefList => 4, - Self::AnnotationSetItem => 5, - Self::CodeItem => 6, - Self::StringDataItem => 7, - Self::DebugInfoItem => 8, - Self::AnnotationItem => 9, - Self::EncodedArrayItem => 10, - Self::AnnotationsDirectoryItem => 11, - Self::ClassDataItem => 12, - // Self::HiddenapiClassDataItem => 13, + Self::AnnotationSetRefList => 3, + Self::AnnotationSetItem => 4, + Self::CodeItem => 5, + Self::StringDataItem => 6, + Self::DebugInfoItem => 7, + Self::AnnotationItem => 8, + Self::EncodedArrayItem => 9, + Self::AnnotationsDirectoryItem => 10, + Self::ClassDataItem => 11, + // Self::HiddenapiClassDataItem => 12, } } @@ -1916,7 +2100,6 @@ impl FragSection { Self::ClassDefItem => Some(0x20), Self::CallSiteIdItem => Some(4), Self::MethodHandleItem => Some(8), - Self::TypeList => None, Self::AnnotationSetRefList => None, Self::AnnotationSetItem => None, Self::CodeItem => None, @@ -1953,7 +2136,6 @@ impl FragSection { Self::ClassDefItem => 4, Self::CallSiteIdItem => 1, Self::MethodHandleItem => 4, - Self::TypeList => 4, Self::AnnotationSetRefList => 4, Self::AnnotationSetItem => 4, Self::CodeItem => 4, @@ -2124,6 +2306,7 @@ impl FragSectionManager { } /// Index that associate a type to its local id in a fragment. +#[derive(Debug, Clone)] struct FragIndex { pub strings: HashMap, pub types: HashMap, @@ -2131,3 +2314,38 @@ struct FragIndex { pub fields: HashMap, pub methods: HashMap, } + +/// The link state of a fragment. Describe the linking of a fragment and the related data. +#[derive(Debug, Clone)] +enum FragLinkState { + /// The fragment is not linked + Unlinked, + /// The index used in the fragment are linked of beeing linked. + /// The new local addresses of the element whose size depend on the value + /// of the index are stored here. + LinkedIdx { + code_item_relocation: HashMap, + debug_info_item_relocation: HashMap, + annotation_item_relocation: HashMap, + encoded_array_relocation: HashMap, // TODO only one? + }, +} + +impl FragLinkState { + fn start_linking_idx(&mut self) -> Result<()> { + match self { + Self::Unlinked => { + *self = Self::LinkedIdx { + code_item_relocation: HashMap::::new(), + debug_info_item_relocation: HashMap::::new(), + annotation_item_relocation: HashMap::::new(), + encoded_array_relocation: HashMap::::new(), + }; + Ok(()) + } + _ => { + bail!("Cannot link the idx in a fragment that is already linked"); + } + } + } +} diff --git a/androscalpel/src/dex_writer.rs b/androscalpel/src/dex_writer.rs index f967970..840872c 100644 --- a/androscalpel/src/dex_writer.rs +++ b/androscalpel/src/dex_writer.rs @@ -1,3255 +1,130 @@ //! The structure that generate a .dex from classes. +use std::collections::{HashSet, VecDeque}; -use std::collections::{HashMap, HashSet, VecDeque}; -use std::io; -use std::io::{Cursor, Seek, SeekFrom, Write}; - -use adler::Adler32; -use anyhow::{anyhow, bail, Context}; -use log::debug; -use sha1::{Digest, Sha1}; - -use crate::Result; -use crate::*; -use androscalpel_serializer::*; - -use crate::ins::{CallSite, Instruction}; -use crate::instructions::*; -use androscalpel_serializer::Instruction as InsFormat; +use crate::dex_fragment::DexFragment; +use crate::{DexString, IdField, IdMethod, IdMethodType, IdType}; #[derive(Debug, Clone)] pub struct DexWriter { - header: HeaderItem, - /// The strings in the dex file. Initially values are set to zero. - /// Once the strings are sorted, the values of the map are set to the - /// strings idx - strings: HashMap, - /// The types in the dex file. Initially values are set to zero. - /// Once the types are sorted, the values of the map are set to the - /// types idx - type_ids: HashMap, - /// The prototypes in the dex file. Initially values are set to zero. - /// Once the prototypes are sorted, the values of the map are set to the - /// prototypes idx - proto_ids: HashMap, - /// The field ids in the dex file. Initially values are set to zero. - /// Once the fields are sorted, the values of the map are set to the - /// fields idx - field_ids: HashMap, - /// The methods ids in the dex file. Initially values are set to zero. - /// Once the methods are sorted, the values of the map are set to the - /// method idx - method_ids: HashMap, - /// The classes defined in the dex file. Initially values are set to the classes and zero. - /// Once the class definitions are sorted, the values are set to the classes and there - /// index in the `class_defs` section. - class_defs: HashMap, - /// The call sites refered to in the bytecode. - call_site_ids: Vec, - /// A struct that keep track of sections size, nb elements and offsets. - section_manager: SectionManager, - /// The string_data section. Once populated, the elements must be sorted according to the spec. - string_data_list: Vec, - /// The type_ids sections. Once populated, the elements must be sorted according to the spec. - type_ids_list: Vec, - /// The proto_ids section. Once populated, the elements must be sorted according to the spec. - /// the `parameter_off` field of the prototypes must be linked before serializing the section. - proto_ids_list: Vec, - /// The field_ids section. Once populated, the elements must be sorted according to the spec. - field_ids_list: Vec, - /// The method_ids section. Once populated, the elements must be sorted according to the spec. - method_ids_list: Vec, - /// The class_defs section. Once populated, the elements must be sorted according to the spec. - /// The `interfaces_off`, `annotations_off`, `class_data_off` and `static_values_off` fields - /// must be linked before serializing. Before liking, the `annotations_off`, `class_data_off` - /// and `static_values_off` field take the value of the offset from the start of theire - /// respective section (or zero if the class does not have a value associated to the field). - class_defs_list: Vec, - /// The class_data section. - class_data_list: Vec, - /// The type lists found in the classes associated to their index in the type_lists section. - type_lists_index: HashMap, - /// The type_lists section and the offset of the lists inside the section. - type_lists_with_offset: Vec<(TypeList, u32)>, - /// The encoded_array_items section. - encoded_array_items: Vec, - /// The annotations_directory_item section. - annotations_directory_items: Vec, - /// The annotation_set_item section. - annotation_set_items: Vec, - /// The annotation item section. - annotation_items: Vec, - /// The annotation_set_ref_list section. - annotation_set_lists: Vec, - /// The method_handles section. - method_handles: Vec, - /// The code_items sections. - code_items: Vec, - /// The debug info items section. - debug_info_items: Vec, - /// The map_list - map_list: MapList, - /// Map the **local** offset of **unlinked** class data offset to the **local** - /// offset of the **linked**. - /// - /// local offset means the offset of the first data item in the section is 0 (i.e. not - /// the offset from the beginning of the file). - /// - /// This hack is necessary (well, maybe not necessary but that would require some refactoring) - /// because someone thought that encoding offsets in Uleb format was a good idea. It was not. - /// The size of the encoded offset depend on the value of the offset, so the linking change the - /// size of the struct, and doing so change the offset of the next struct, ect. - /// - /// When generating the structs refering to `class_data_item`s, the local offset of the - /// unlinked `class_data_item` is known, but not the linked offset. This struct is filled - /// when linking the `class_data_item`s, and can then be used to link the structs refering to - /// the `class_data_item`s. - corrected_class_data_offset: HashMap, + fragments: VecDeque, } impl Default for DexWriter { fn default() -> Self { Self { - header: HeaderItem { - magic: DexFileMagic { - //version: [0x30, 0x33, 0x39], - version: [0x30, 0x33, 0x37], - }, // TODO: find a better default version - checksum: 0, - signature: [0u8; 20], - file_size: 0, - header_size: 0x70, - endian_tag: EndianConstant::EndianConstant, - link_size: 0, - link_off: 0, - map_off: 0, - string_ids_size: 0, - string_ids_off: 0, - type_ids_size: 0, // At most 0xffff - type_ids_off: 0, - proto_ids_size: 0, // At most 0xffff - proto_ids_off: 0, - field_ids_size: 0, - field_ids_off: 0, - method_ids_size: 0, - method_ids_off: 0, - class_defs_size: 0, - class_defs_off: 0, - data_size: 0, // Must be an even multiple of sizeof(uint) -> % 8 = 0 - data_off: 0, - }, - strings: HashMap::new(), - type_ids: HashMap::new(), - proto_ids: HashMap::new(), - field_ids: HashMap::new(), - method_ids: HashMap::new(), - class_defs: HashMap::new(), - call_site_ids: vec![], - section_manager: SectionManager::default(), - string_data_list: vec![], - type_ids_list: vec![], - proto_ids_list: vec![], - field_ids_list: vec![], - method_ids_list: vec![], - class_defs_list: vec![], - class_data_list: vec![], - type_lists_index: HashMap::new(), - type_lists_with_offset: vec![], - map_list: MapList::default(), - encoded_array_items: vec![], - method_handles: vec![], - code_items: vec![], - annotations_directory_items: vec![], - annotation_set_items: vec![], - annotation_items: vec![], - annotation_set_lists: vec![], - debug_info_items: vec![], - corrected_class_data_offset: HashMap::new(), + fragments: VecDeque::new(), } } } -pub enum DexWritterError { - OutOfSpace(String), -} - impl DexWriter { pub fn new() -> Self { Self::default() } - pub fn add_class(&mut self, class: &Class) -> Result<(), DexWritterError> { - debug!("Adding class {} to dex builder", class.descriptor.__str__()); - let new_strings = class.get_all_strings(); - - let new_types = class.get_all_types(); - let new_nb_types = self.type_ids.len() - + new_types - .iter() - .filter(|ty| self.type_ids.get(ty).is_none()) - .count(); - if new_nb_types >= u16::MAX as usize { - return Err(DexWritterError::OutOfSpace( - "To many types for one dex file".into(), - )); - } - - let new_protos = class.get_all_protos(); - let new_nb_protos = self.proto_ids.len() - + new_protos - .iter() - .filter(|proto| self.proto_ids.get(proto).is_none()) - .count(); - if new_nb_protos >= u16::MAX as usize { - return Err(DexWritterError::OutOfSpace( - "To many prototypes for one dex file".into(), - )); - } - - let new_field_ids = class.get_all_field_ids(); - let new_nb_field_ids = self.field_ids.len() - + new_field_ids - .iter() - .filter(|field| self.field_ids.get(field).is_none()) - .count(); - if new_nb_field_ids >= u16::MAX as usize { - return Err(DexWritterError::OutOfSpace( - "To many field ids for one dex file".into(), - )); - } - - let new_method_ids = class.get_all_method_ids(); - let new_nb_method_ids = self.method_ids.len() - + new_method_ids - .iter() - .filter(|meth| self.method_ids.get(meth).is_none()) - .count(); - if new_nb_method_ids >= u16::MAX as usize { - return Err(DexWritterError::OutOfSpace( - "To many method ids for one dex file".into(), - )); - } - - for string in new_strings { - self.strings.insert(string, 0); - } - for ty in new_types { - self.type_ids.insert(ty, 0); - } - for proto in new_protos { - self.proto_ids.insert(proto, 0); - } - for field in new_field_ids { - self.field_ids.insert(field, 0); - } - for method in new_method_ids { - self.method_ids.insert(method, 0); - } - self.class_defs - .insert(class.descriptor.clone(), (class.clone(), 0)); - Ok(()) + pub fn empty(&self) -> bool { + self.fragments.is_empty() } - pub fn add_string(&mut self, string: DexString) { - self.strings.insert(string, 0); - } - - fn gen_string_data_section(&mut self) -> Result<()> { - debug!("Sort string and generate string_data_item and string_ids sections"); - let mut string_ids_list: Vec = self.strings.keys().cloned().collect(); - string_ids_list.sort(); - for (idx, string) in string_ids_list.iter().enumerate() { - self.strings - .entry(string.clone()) - .and_modify(|val| *val = idx); - self.section_manager.add_elt(Section::StringIdItem, None); - self.section_manager - .add_elt(Section::StringDataItem, Some(string.0.size())); - } - self.string_data_list = string_ids_list - .into_iter() - .map(|string| string.into()) - .collect(); - Ok(()) - } - - fn gen_type_ids_section(&mut self) -> Result<()> { - debug!("Sort types and generate type_id_item section"); - let mut type_ids_list: Vec = self.type_ids.keys().cloned().collect(); - type_ids_list.sort(); - for (idx, ty) in type_ids_list.iter().enumerate() { - self.type_ids.entry(ty.clone()).and_modify(|val| *val = idx); - self.section_manager.add_elt(Section::TypeIdItem, None); - } - let mut type_ids_list_aux = vec![]; - for ty in type_ids_list.into_iter() { - type_ids_list_aux.push(TypeIdItem { - descriptor_idx: *self.strings.get(&ty.0).ok_or(anyhow!( - "String {} (name of type {}) not found in dex builder", - ty.0.__repr__(), - ty.__repr__() - ))? as u32, - }); - } - self.type_ids_list = type_ids_list_aux; - Ok(()) - } - - fn gen_proto_ids_section(&mut self) -> Result<()> { - debug!("Sort prototypes and generate proto_id_item section"); - let mut proto_ids_list: Vec = self.proto_ids.keys().cloned().collect(); - proto_ids_list.sort(); - for (idx, proto) in proto_ids_list.iter().enumerate() { - self.proto_ids - .entry(proto.clone()) - .and_modify(|val| *val = idx); - self.section_manager.add_elt(Section::ProtoIdItem, None); - } - let mut proto_ids_list_aux = vec![]; - for proto in proto_ids_list { - proto_ids_list_aux.push(ProtoIdItem { - shorty_idx: *self.strings.get(&proto.shorty).ok_or(anyhow!( - "String {}, (shorty of prototype {}) not found in dex builder", - proto.shorty.__repr__(), - proto.__repr__() - ))? as u32, - return_type_idx: *self.type_ids.get(&proto.return_type).ok_or(anyhow!( - "Type {}, (return type of prototype {}) not found in dex builder", - proto.shorty.__repr__(), - proto.__repr__() - ))? as u32, - parameters_off: 0, // Will be linked later - }); - } - self.proto_ids_list = proto_ids_list_aux; - Ok(()) - } - - fn gen_field_ids_section(&mut self) -> Result<()> { - debug!("Sort field ids and generate field_ids_item"); - let mut field_ids_list: Vec = self.field_ids.keys().cloned().collect(); - field_ids_list.sort(); - for (idx, field_id) in field_ids_list.iter().enumerate() { - self.field_ids - .entry(field_id.clone()) - .and_modify(|val| *val = idx); - self.section_manager.add_elt(Section::FieldIdItem, None); - } - let mut field_ids_list_aux = vec![]; - for field in field_ids_list.into_iter() { - field_ids_list_aux.push(FieldIdItem { - class_idx: *self.type_ids.get(&field.class_).ok_or(anyhow!( - "Type {} (class of field {}) not found in dex builder", - field.class_.__repr__(), - field.__repr__() - ))? as u16, - type_idx: *self.type_ids.get(&field.type_).ok_or(anyhow!( - "Type {} (type of field {}) not found in dex builder", - field.type_.__repr__(), - field.__repr__() - ))? as u16, - name_idx: *self.strings.get(&field.name).ok_or(anyhow!( - "String {} (name of field {}) not found in dex builder", - field.name.__repr__(), - field.__repr__() - ))? as u32, - }); - } - self.field_ids_list = field_ids_list_aux; - - Ok(()) - } - - fn gen_method_ids_section(&mut self) -> Result<()> { - debug!("Sort method ids and generate method_id_item section"); - let mut method_ids_list: Vec = self.method_ids.keys().cloned().collect(); - method_ids_list.sort(); - for (idx, method_id) in method_ids_list.iter().enumerate() { - self.method_ids - .entry(method_id.clone()) - .and_modify(|val| *val = idx); - self.section_manager.add_elt(Section::MethodIdItem, None); - } - let mut method_ids_list_aux = vec![]; - for method in method_ids_list.into_iter() { - method_ids_list_aux.push(MethodIdItem { - class_idx: *self.type_ids.get(&method.class_).ok_or(anyhow!( - "Type {} (class of method {}) not found in dex builder", - method.class_.__repr__(), - method.__repr__() - ))? as u16, - proto_idx: *self.proto_ids.get(&method.proto).ok_or(anyhow!( - "Prototype {} (signature of method {}) not found in dex builder", - method.proto.__repr__(), - method.__repr__() - ))? as u16, - name_idx: *self.strings.get(&method.name).ok_or(anyhow!( - "String {} (name of method {}) not found in dex builder", - method.name.__repr__(), - method.__repr__() - ))? as u32, - }); - } - self.method_ids_list = method_ids_list_aux; - Ok(()) - } - - /// Insert a code_item. - /// - /// # Warning - /// - /// 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 { - self.class_defs - .get(&method_id.class_) - .unwrap() - .0 - .direct_methods - .get(&method_id) - .unwrap() - .code - .as_ref() - .unwrap() - .clone() - } else { - self.class_defs - .get(&method_id.class_) - .unwrap() - .0 - .virtual_methods - .get(&method_id) - .unwrap() - .code - .as_ref() - .unwrap() - .clone() - }; - // Estimate instructions addresses - let mut min_addr = 0; - let mut max_addr = 0; - let mut label_min_max_addrs: HashMap = HashMap::new(); - - for ins in &code.insns { - match ins { - Instruction::Label(Label { name }) => { - label_min_max_addrs.insert(name.clone(), (min_addr, max_addr)); - min_addr += ins.min_ins_size() / 2; - max_addr += ins.max_ins_size() / 2; - } - Instruction::ConstString(ins) => { - let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( - "String {} (found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let size = ins.get_raw_ins(*string_idx).size() / 2; - min_addr += size; - max_addr += size; - } - _ => { - min_addr += ins.min_ins_size() / 2; - max_addr += ins.max_ins_size() / 2; - } - } - } - // Compute instruction size and precise addresses - let mut addr = 0; - let mut label_addrs = HashMap::new(); - let mut goto_sizes = vec![]; - for ins in &code.insns { - match ins { - Instruction::Label(Label { name }) => { - label_addrs.insert(name.clone(), addr); - addr += ins.max_ins_size() / 2; - } - Instruction::ConstString(ins) => { - let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( - "String {} (found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - addr += ins.get_raw_ins(*string_idx).size() / 2; - } - Instruction::Goto(Goto { label }) => { - let (min_addr, max_addr) = label_min_max_addrs - .get(label) - .ok_or(anyhow!("Label {label} not found in label estimation map"))?; - let size = Goto::size_from_branch_offset_interval(addr, *min_addr, *max_addr)?; - goto_sizes.push(size); - addr += size / 2; - } - _ => addr += ins.ins_size()? / 2, - } - } - // Serialize instructions - let mut tries = vec![]; - let mut handlers = EncodedCatchHandlerList { list: vec![] }; - let mut handler_off = 0; - let mut insns = vec![]; - let mut payloads = vec![]; - let mut goto_idx = 0; - let mut payload_addr = addr; - if payload_addr % 2 != 0 { - payload_addr += 1; // For switch and array table alignment - } - addr = 0; - for ins in &code.insns { - match ins { - Instruction::ConstString(ins) => { - let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( - "String {} (found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*string_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::ConstClass(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Class {} (type of class found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(class_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::CheckCast(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Class {} (type of class found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(class_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InstanceOf(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Class {} (type of class found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(class_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::NewInstance(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Class {} (type of class found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(class_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::NewArray(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Type {} (type found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(class_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::FilledNewArray(ins) => { - let class_idx = *self.type_ids.get(&ins.type_).ok_or(anyhow!( - "Type {} (type found in code of {}) not found in dex builder", - ins.type_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(class_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::FillArrayData(ins) => { - let payload = InsFormat::FormatFillArrayDataPayload { - elt_width: ins.elt_width, - data: ins.data.clone(), - }; - if payload_addr % 2 != 0 { - // https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/method_verifier.cc;drc=e8c3e7be783937a340cd4f3280b69962d6f1ea0c;l=1347 - // The ART check if the array data table is 4 bytes aligned (= 2 ins alligned) - // TODO: check how it is donne in android and other dex generation code. - let nop = Instruction::Nop(Nop).get_raw_ins()?; - payload_addr += nop.size() / 2; - payloads.push(nop); - } - let data_offset = payload_addr as i32 - addr as i32; - payload_addr += payload.size() / 2; - payloads.push(payload); - let ins = ins.get_raw_ins(data_offset); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::Goto(ins) => { - let goto_size = goto_sizes[goto_idx]; - goto_idx += 1; - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found goto with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - let ins = ins.get_raw_ins(branch_offset, goto_size); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::Switch(ins) => { - let mut key_targets = vec![]; - for (key, label) in &ins.branches { - let label_addr = label_addrs.get(label).ok_or(anyhow!( - "Label {} not found in code of {}, but found goto with this label", - label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - key_targets.push((*key, branch_offset)); - } - key_targets.sort_by_key(|(key, _)| *key); - let payload = if ins.is_packed() { - let (first_key, _) = *key_targets.first().ok_or(anyhow!( - "Found empty swith in code of {}", - method_id.__repr__() - ))?; - let targets: Vec<_> = - key_targets.into_iter().map(|(_, target)| target).collect(); - InsFormat::FormatPackedSwitchPayload { first_key, targets } - } else { - InsFormat::FormatSparseSwitchPayload { key_targets } - }; - if payload_addr % 2 != 0 { - // https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/method_verifier.cc;drc=e8c3e7be783937a340cd4f3280b69962d6f1ea0c;l=1464 - // The ART check if the switch table is 4 bytes aligned (= 2 ins alligned) - // TODO: check how it is donne in android and other dex generation code. - let nop = Instruction::Nop(Nop).get_raw_ins()?; - payload_addr += nop.size() / 2; - payloads.push(nop); - } - let data_offset = payload_addr as i32 - addr as i32; - payload_addr += payload.size() / 2; - payloads.push(payload); - let ins = ins.get_raw_ins(data_offset); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfEq(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfNe(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfLt(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfGe(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfGt(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfLe(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfEqZ(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfNeZ(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfLtZ(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfGeZ(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfGtZ(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfLeZ(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGet(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGetWide(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGetObject(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGetBoolean(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGetByte(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGetChar(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGetShort(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPut(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPutWide(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPutObject(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPutBoolean(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPutByte(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPutChar(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPutShort(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGet(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGetWide(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGetObject(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGetBoolean(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGetByte(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGetChar(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGetShort(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPut(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPutWide(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPutObject(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPutBoolean(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPutByte(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPutChar(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPutShort(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokeVirtual(ins) => { - let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() - ))?; - debug_assert!( - *meth_idx <= u16::MAX as usize, - "methode id too big for invoke instruction" - ); - let meth_idx = *meth_idx as u16; - let ins = ins.get_raw_ins(meth_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokeSuper(ins) => { - let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() - ))?; - debug_assert!( - *meth_idx <= u16::MAX as usize, - "methode id too big for invoke instruction" - ); - let meth_idx = *meth_idx as u16; - let ins = ins.get_raw_ins(meth_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokeDirect(ins) => { - let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() - ))?; - debug_assert!( - *meth_idx <= u16::MAX as usize, - "methode id too big for invoke instruction" - ); - let meth_idx = *meth_idx as u16; - let ins = ins.get_raw_ins(meth_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokeStatic(ins) => { - let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() - ))?; - debug_assert!( - *meth_idx <= u16::MAX as usize, - "methode id too big for invoke instruction" - ); - let meth_idx = *meth_idx as u16; - let ins = ins.get_raw_ins(meth_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokeInterface(ins) => { - let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() - ))?; - debug_assert!( - *meth_idx <= u16::MAX as usize, - "methode id too big for invoke instruction" - ); - let meth_idx = *meth_idx as u16; - let ins = ins.get_raw_ins(meth_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokePolymorphic(ins) => { - let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() - ))?; - let proto_idx = self.proto_ids.get(&ins.proto).ok_or(anyhow!( - "Prototype {} (found in code of {}) not found in dex builder", - ins.proto.__repr__(), - method_id.__repr__() - ))?; - debug_assert!( - *meth_idx <= u16::MAX as usize, - "methode id too big for invoke instruction" - ); - debug_assert!( - *proto_idx <= u16::MAX as usize, - "proto id too big for invoke instruction" - ); - let meth_idx = *meth_idx as u16; - let proto_idx = *proto_idx as u16; - let ins = ins.get_raw_ins(meth_idx, proto_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokeCustom(ins) => { - let call_site_idx = self.call_site_ids.len(); - self.insert_call_site_item(&ins.call_site)?; - let ins = ins.get_raw_ins(call_site_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::ConstMethodHandle(ins) => { - let method_handle_idx = self.method_handles.len(); - self.insert_method_handle(&ins.handle)?; - let ins = ins.get_raw_ins(method_handle_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::ConstMethodType(ins) => { - let proto_idx = self.proto_ids.get(&ins.proto).ok_or(anyhow!( - "Prototype {} (found in code of {}) not found in dex builder", - ins.proto.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*proto_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::Try(try_) => { - let end_block_addr = *label_addrs.get(&try_.end_label).ok_or(anyhow!( - "Label {} not found in code of {}, but found try with this label", - &try_.end_label, - method_id.__repr__() - ))?; - if end_block_addr < addr { - bail!( - "Found end label of a try block before the try instruction in code of {}", - method_id.__repr__() - ) - } - let try_item = TryItem { - start_addr: addr as u32, - insn_count: (end_block_addr - addr) as u16, - handler_off: handler_off as u16, // will be ajusted once the size of the - // handler list object is known - }; - tries.push(try_item); - let mut catches = EncodedCatchHandler { - handlers: vec![], - catch_all_addr: None, - }; - for (ty, label) in &try_.handlers { - let type_idx = Uleb128(*self.type_ids.get(ty).ok_or(anyhow!( - "Could not found type {} captured by a try block in {}\ - in the dex builder", - ty.__repr__(), - method_id.__repr__() - ))? as u32); - let addr = Uleb128(*label_addrs.get(label).ok_or(anyhow!( - "Label {} not found in code of {}, but found try \ - with this label as catch for type {}", - &try_.end_label, - method_id.__repr__(), - ty.__repr__(), - ))? as u32); - catches - .handlers - .push(EncodedTypeAddrPair { type_idx, addr }); - } - if let Some(ref label) = try_.default_handler { - let catch_all_addr = *label_addrs.get(label).ok_or(anyhow!( - "Label {} not found in code of {}, but found try \ - with this label as catch all", - &try_.end_label, - method_id.__repr__() - ))?; - catches.catch_all_addr = Some(Uleb128(catch_all_addr as u32)); - } - handler_off += catches.size(); - handlers.list.push(catches); - } - Instruction::Label(_) => (), - _ => { - let ins = ins.get_raw_ins().with_context(|| { - format!( - "Failed to convert instruction {} (found in code of {}) to raw instruction", - ins.__repr__(), - method_id.__repr__() - ) - })?; - addr += ins.size() / 2; - insns.push(ins); - } - } - } - if addr % 2 != 0 { - // make sure the payload section is 4 bytes aligned - let nop = Instruction::Nop(Nop).get_raw_ins()?; - //addr += nop.size() / 2; - insns.push(nop); - } - insns.extend(payloads); - - for try_ in &mut tries { - try_.handler_off += handlers.size_field().size() as u16; - } - - let debug_info_off = if code.debug_info.1.is_empty() && code.parameter_names.is_none() { - 0 - } else { - let debug_info_off = self - .section_manager - .get_aligned_size(Section::DebugInfoItem); - let mut cursor = Cursor::new(code.debug_info.1); - let mut item = DebugInfoItem { - line_start: Uleb128(code.debug_info.0), - parameter_names: vec![], - bytecode: Vec::::deserialize(&mut cursor, DbgBytecode::EndSequence)?, - }; - if let Some(parameter_names) = code.parameter_names { - for name in ¶meter_names { - if let Some(name) = name { - item.parameter_names - .push(Uleb128p1(*self.strings.get(name).ok_or(anyhow!( - "String {} (name of param of {}) not found", - name.__str__(), - method_id.__repr__() - ))? as u32)); - } else { - item.parameter_names.push(NO_INDEX); - } - } - } - self.section_manager - .add_elt(Section::DebugInfoItem, Some(item.size())); - self.debug_info_items.push(item); - debug_info_off + 1 - }; - let handlers = if handlers.list.is_empty() { - None - } else { - Some(handlers) - }; - 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() - insns, - tries, - handlers, - }; - self.section_manager - .add_elt(Section::CodeItem, Some(item.size())); - self.code_items.push(item); - Ok(()) - } - - /// Insert annotation associated to a class. - /// - - /// Insert a class_data_item in the class_data section (in data). - /// - /// # Note - /// - /// code_item objects are 4 bytes aligns, so their offset cannot be odd. - /// - /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset - /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow - /// to distinguish the offset of the first item (equal to zero before linking) and the value - /// 0 used to indicate an abscence of item. - fn insert_class_data_item(&mut self, class_id: &IdType) -> Result<()> { - let mut data = ClassDataItem::default(); - let (class, _) = self.class_defs.get(class_id).unwrap(); - - let mut static_fields: Vec = class.static_fields.keys().cloned().collect(); - static_fields.sort(); - let mut last_field_id = 0; - for id in &static_fields { - let idx = self.field_ids.get(id).ok_or(anyhow!( - "Field {} (field of class {}) not found in dex builder", - id.__repr__(), - class.__repr__() - ))?; - let field_idx_diff = Uleb128((idx - last_field_id) as u32); - last_field_id = *idx; - let access_flags = Uleb128(class.static_fields.get(id).unwrap().get_raw_access_flags()); - data.static_fields.push(EncodedField { - field_idx_diff, - access_flags, - }); - } - - let mut instance_fields: Vec = class.instance_fields.keys().cloned().collect(); - instance_fields.sort(); - let mut last_field_id = 0; - for id in &instance_fields { - let idx = self.field_ids.get(id).ok_or(anyhow!( - "Field {} (field of class {}) not found in dex builder", - id.__repr__(), - class.__repr__() - ))?; - let field_idx_diff = Uleb128((idx - last_field_id) as u32); - last_field_id = *idx; - let access_flags = Uleb128( - class - .instance_fields - .get(id) - .unwrap() - .get_raw_access_flags(), - ); - data.instance_fields.push(EncodedField { - field_idx_diff, - access_flags, - }); - } - - let mut direct_methods: Vec = class.direct_methods.keys().cloned().collect(); - direct_methods.sort(); - let mut last_method_id = 0; - for id in &direct_methods { - // &mut vs & of self and class make things difficult... - let (class, _) = self.class_defs.get(class_id).unwrap(); - let idx = self.method_ids.get(id).ok_or(anyhow!( - "Method {} (method of class {}) not found in dex builder", - id.__repr__(), - class.__repr__() - ))?; - let method_idx_diff = Uleb128((idx - last_method_id) as u32); - last_method_id = *idx; - let access_flags = - Uleb128(class.direct_methods.get(id).unwrap().get_raw_access_flags()); - // No if let because ownership gunfooterie - let code_off = if class.direct_methods.get(id).unwrap().code.is_some() { - let code_off = self.section_manager.get_aligned_size(Section::CodeItem); - self.insert_code_item(id.clone(), true)?; - Uleb128(code_off + 1) + /// Take as many fragments as possible and convert them to a dex file. + pub fn generate_next_dex_file(&mut self) -> Vec { + let mut fragments = vec![]; + let mut string_set: HashSet = HashSet::new(); + let mut type_set: HashSet = HashSet::new(); + let mut proto_set: HashSet = HashSet::new(); + let mut field_set: HashSet = HashSet::new(); + let mut method_set: HashSet = HashSet::new(); + let mut type_list_set: HashSet> = HashSet::new(); + let mut nb_method_handle = 0; + let mut nb_method_handle_before = vec![]; + loop { + let new_fragment = if let Some(new_fragment) = self.fragments.pop_front() { + new_fragment } else { - Uleb128(0) + break; }; - data.direct_methods.push(EncodedMethod { - method_idx_diff, - access_flags, - code_off, // Will be relinked once the offset of the code item section is known - }); - } - - let (class, _) = self.class_defs.get(class_id).unwrap(); - let mut virtual_methods: Vec = class.virtual_methods.keys().cloned().collect(); - virtual_methods.sort(); - let mut last_method_id = 0; - for id in &virtual_methods { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let idx = self.method_ids.get(id).ok_or(anyhow!( - "Method {} (method of class {}) not found in dex builder", - id.__repr__(), - class.__repr__() - ))?; - let method_idx_diff = Uleb128((idx - last_method_id) as u32); - last_method_id = *idx; - let access_flags = Uleb128( - class - .virtual_methods - .get(id) - .unwrap() - .get_raw_access_flags(), - ); - // No if let because ownership gunfooterie - let code_off = if class.virtual_methods.get(id).unwrap().code.is_some() { - let code_off = self.section_manager.get_aligned_size(Section::CodeItem); - self.insert_code_item(id.clone(), false)?; - Uleb128(code_off + 1) - } else { - Uleb128(0) - }; - data.virtual_methods.push(EncodedMethod { - method_idx_diff, - access_flags, - code_off, // Will be relinked once the offset of the code item section is known - }); - } - self.section_manager - .add_elt(Section::ClassDataItem, Some(data.size())); - //assert_eq!(data.size(), data.serialize_to_vec().unwrap().len()); - self.class_data_list.push(data); - Ok(()) - } - - /// Insert a [`MethodHandle`]. - pub fn insert_method_handle(&mut self, handle: &MethodHandle) -> Result<()> { - let (field_or_method_id, method_handle_type) = match handle { - MethodHandle::StaticPut(StaticPut(field)) => ( - *self.field_ids.get(field).ok_or(anyhow!( - "Field {} not found in dex writer", - field.__repr__() - ))? as u16, - MethodHandleType::StaticPut, - ), - MethodHandle::StaticGet(StaticGet(field)) => ( - *self.field_ids.get(field).ok_or(anyhow!( - "Field {} not found in dex writer", - field.__repr__() - ))? as u16, - MethodHandleType::StaticGet, - ), - MethodHandle::InstancePut(InstancePut(field)) => ( - *self.field_ids.get(field).ok_or(anyhow!( - "Field {} not found in dex writer", - field.__repr__() - ))? as u16, - MethodHandleType::InstancePut, - ), - MethodHandle::InstanceGet(InstanceGet(field)) => ( - *self.field_ids.get(field).ok_or(anyhow!( - "Field {} not found in dex writer", - field.__repr__() - ))? as u16, - MethodHandleType::InstanceGet, - ), - MethodHandle::InvokeStatic(InvokeStatic(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, - MethodHandleType::InvokeStatic, - ), - MethodHandle::InvokeInstance(InvokeInstance(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, - MethodHandleType::InvokeInstance, - ), - MethodHandle::InvokeConstructor(InvokeConstructor(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, - MethodHandleType::InvokeConstructor, - ), - MethodHandle::InvokeDirect(InvokeDirect(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, - MethodHandleType::InvokeDirect, - ), - MethodHandle::InvokeInterface(InvokeInterface(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, - MethodHandleType::InvokeInterface, - ), - }; - self.method_handles.push(MethodHandleItem { - method_handle_type, - field_or_method_id, - unused1: 0, - unused2: 0, - }); - Ok(()) - } - - /// Convert a [`DexValue`] to an [`EncodedValue`]. - /// - /// # Warning - /// - /// This method can insert element in the dex file like method_handles. - pub fn dex_value_to_encoded_value(&mut self, value: &DexValue) -> Result { - match value { - DexValue::Byte(DexByte(val)) => Ok(EncodedValue::Byte(*val)), - DexValue::Short(DexShort(val)) => Ok(EncodedValue::Short(*val)), - DexValue::Char(DexChar(val)) => Ok(EncodedValue::Char(*val)), - DexValue::Int(DexInt(val)) => Ok(EncodedValue::Int(*val)), - DexValue::Long(DexLong(val)) => Ok(EncodedValue::Long(*val)), - DexValue::Float(DexFloat(val)) => Ok(EncodedValue::Float(*val)), - DexValue::Double(DexDouble(val)) => Ok(EncodedValue::Double(*val)), - DexValue::MethodType(val) => Ok(EncodedValue::MethodType( - *self.proto_ids.get(val).ok_or(anyhow!( - "Prototype {} not found in dex writer", - val.__repr__() - ))? as u32, - )), - DexValue::MethodHandle(val) => { - // TODO: move to a method - let idx = self.method_handles.len() as u32; - self.insert_method_handle(val)?; - Ok(EncodedValue::MethodHandle(idx)) + if type_set.len() + new_fragment.type_ids().len() > u16::MAX as usize + && type_set.len() + + new_fragment + .type_ids() + .iter() + .filter(|ty| type_set.contains(*ty)) + .count() + > u16::MAX as usize + { + self.fragments.push_front(new_fragment); + break; } - DexValue::String(val) => Ok(EncodedValue::String( - *self - .strings - .get(val) - .ok_or(anyhow!("String {} not found in dex writer", val.__repr__()))? - as u32, - )), - DexValue::Type(val) => Ok(EncodedValue::Type( - *self - .type_ids - .get(val) - .ok_or(anyhow!("Type {} not found in dex writer", val.__repr__()))? - as u32, - )), - DexValue::Field(val) => Ok(EncodedValue::Field( - *self - .field_ids - .get(val) - .ok_or(anyhow!("Field {} not found in dex writer", val.__repr__()))? - as u32, - )), - DexValue::Method(val) => Ok(EncodedValue::Method( - *self - .method_ids - .get(val) - .ok_or(anyhow!("Method {} not found in dex writer", val.__repr__()))? - as u32, - )), - DexValue::Enum(IdEnum(val)) => Ok(EncodedValue::Enum( - *self - .field_ids - .get(val) - .ok_or(anyhow!("Field {} not found in dex writer", val.__repr__()))? - as u32, - )), - DexValue::Array(DexArray(arr)) => { - let mut values = vec![]; - for val in arr { - values.push( - self.dex_value_to_encoded_value(val) - .context("Error while serializing a array")?, - ); - } - Ok(EncodedValue::Array(EncodedArray { values })) + if proto_set.len() + new_fragment.proto_ids().len() > u16::MAX as usize + && proto_set.len() + + new_fragment + .proto_ids() + .iter() + .filter(|proto| proto_set.contains(*proto)) + .count() + > u16::MAX as usize + { + self.fragments.push_front(new_fragment); + break; } - DexValue::Annotation(val) => Ok(EncodedValue::Annotation( - self.dex_annotation_to_encoded_annotation(val.clone())?, - )), - DexValue::Null(DexNull) => Ok(EncodedValue::Null), - DexValue::Boolean(DexBoolean(val)) => Ok(EncodedValue::Boolean(*val)), - } - } - - /// Insert an encoded_array in the encoded_array_item section. - fn insert_encoded_array_item(&mut self, DexArray(array): DexArray) -> Result<()> { - let mut values = vec![]; - for value in array { - values.push(self.dex_value_to_encoded_value(&value)?); - } - let item = EncodedArrayItem { - value: EncodedArray { values }, - }; - self.section_manager - .add_elt(Section::EncodedArrayItem, Some(item.size())); - self.encoded_array_items.push(item); - Ok(()) - } - - /// Insert a [`CallSite`] to the encoded array items - /// - /// # Warning - /// - /// This method can insert element in the dex file like method_handles. - pub fn insert_call_site_item(&mut self, call_site: &CallSite) -> Result<()> { - let mut values = vec![]; - values.push(DexValue::MethodHandle(call_site.method_handle.clone())); - values.push(DexValue::String(call_site.name.clone())); - values.push(DexValue::MethodType(call_site.type_.clone())); - values.extend(call_site.args.iter().cloned()); - self.call_site_ids.push(CallSiteIdItem { - call_site_off: self - .section_manager - .get_aligned_size(Section::EncodedArrayItem), - }); // linked in link_call_site_ids() - self.section_manager.add_elt(Section::CallSiteIdItem, None); - self.insert_encoded_array_item(DexArray(values)) - } - - /// Insert the encoded_array_item encoding the static_values of a class. - fn insert_class_static_values(&mut self, class_id: &IdType) -> Result<()> { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let mut static_fields: Vec = class.static_fields.keys().cloned().collect(); - static_fields.sort(); - let mut array = vec![]; - let mut last_defined_field_index = 0; - for (idx, f) in static_fields.iter().enumerate() { - if class.static_fields.get(f).unwrap().value.is_some() { - last_defined_field_index = idx; + if field_set.len() + new_fragment.field_ids().len() > u16::MAX as usize + && field_set.len() + + new_fragment + .field_ids() + .iter() + .filter(|field| field_set.contains(*field)) + .count() + > u16::MAX as usize + { + self.fragments.push_front(new_fragment); + break; } - } - for f in &static_fields[..=last_defined_field_index] { - let field = class.static_fields.get(f).unwrap(); - if let Some(val) = field.value.as_ref() { - array.push(val.clone()); - } else { - array.push(field.descriptor.type_.get_default_value().ok_or(anyhow!( - "The type {} (for field {} in class {}) does not have a default value", - field.descriptor.type_.__repr__(), - field.descriptor.__repr__(), - class_id.__repr__() - ))?); + if method_set.len() + new_fragment.method_ids().len() > u16::MAX as usize + && method_set.len() + + new_fragment + .method_ids() + .iter() + .filter(|meth| method_set.contains(*meth)) + .count() + > u16::MAX as usize + { + self.fragments.push_front(new_fragment); + break; } + string_set.extend(new_fragment.strings().iter().cloned()); + type_set.extend(new_fragment.type_ids().iter().cloned()); + proto_set.extend(new_fragment.proto_ids().iter().cloned()); + field_set.extend(new_fragment.field_ids().iter().cloned()); + method_set.extend(new_fragment.method_ids().iter().cloned()); + type_list_set.insert(new_fragment.interfaces().to_vec()); + nb_method_handle_before.push(nb_method_handle); + nb_method_handle += new_fragment.method_handles().len(); + fragments.push(new_fragment); } - self.insert_encoded_array_item(DexArray(array)) - .with_context(|| { - format!( - "Failed to serialize static values of class {}", - class_id.__repr__() - ) - }) - } - - fn dex_annotation_to_encoded_annotation( - &mut self, - DexAnnotation { type_, elements }: DexAnnotation, - ) -> Result { - let mut encoded_elements = vec![]; - - let mut elements_names: Vec<_> = elements.keys().collect(); - elements_names.sort(); - - for name in elements_names { - let elt = elements.get(name).unwrap(); - encoded_elements.push(AnnotationElement { - name_idx: Uleb128(*self.strings.get(name).ok_or(anyhow!( - "{} (annotation element name) not found in dex builder", - name.__str__() - ))? as u32), - value: self.dex_value_to_encoded_value(elt)?, - }); - } - Ok(EncodedAnnotation { - type_idx: Uleb128(*self.type_ids.get(&type_).ok_or(anyhow!( - "Annotation type {} not found in dex builder", - type_.__repr__(), - ))? as u32), - elements: encoded_elements, - }) - } - - /// Insert the annnotations set for a class. - fn insert_class_annotation_set(&mut self, class_id: &IdType) -> Result<()> { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let mut annotations = class.annotations.clone(); - - let mut annotation_set = AnnotationSetItem { entries: vec![] }; - annotations.sort_by_key(|annot| annot.annotation.type_.clone()); - for annot in annotations { - annotation_set.entries.push(AnnotationOffItem { - annotation_off: self - .section_manager - .get_aligned_size(Section::AnnotationItem), - }); // linked in link_annotations() - - let item = AnnotationItem { - visibility: match ( - annot.visibility_build, - annot.visibility_runtime, - annot.visibility_system, - ) { - (true, false, false) => AnnotationVisibility::Build, - (false, true, false) => AnnotationVisibility::Runtime, - (false, false, true) => AnnotationVisibility::System, - _ => bail!( - "Annotation need visibility set to one and only one of build, \ - runtime or system" - ), // TODO: check if this is true - }, - annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, - }; - self.section_manager - .add_elt(Section::AnnotationItem, Some(item.size())); - self.annotation_items.push(item); - } - self.section_manager - .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); - self.annotation_set_items.push(annotation_set); - Ok(()) - } - - /// Insert the annnotations set for a class. - fn insert_field_annotation_set( - &mut self, - field_id: &IdField, - is_static_field: bool, - ) -> Result<()> { - let (class, _) = self.class_defs.get(&field_id.class_).unwrap(); - let field = if is_static_field { - class.static_fields.get(field_id).unwrap() - } else { - class.instance_fields.get(field_id).unwrap() - }; - let mut annotations = field.annotations.clone(); - - let mut annotation_set = AnnotationSetItem { entries: vec![] }; - annotations.sort_by_key(|annot| annot.annotation.type_.clone()); - for annot in annotations { - annotation_set.entries.push(AnnotationOffItem { - annotation_off: self - .section_manager - .get_aligned_size(Section::AnnotationItem), - }); // linked in link_annotations() - - let item = AnnotationItem { - visibility: match ( - annot.visibility_build, - annot.visibility_runtime, - annot.visibility_system, - ) { - (true, false, false) => AnnotationVisibility::Build, - (false, true, false) => AnnotationVisibility::Runtime, - (false, false, true) => AnnotationVisibility::System, - _ => bail!( - "Annotation need visibility set to one and only one of build, \ - runtime or system" - ), // TODO: check if this is true - }, - annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, - }; - self.section_manager - .add_elt(Section::AnnotationItem, Some(item.size())); - self.annotation_items.push(item); - } - self.section_manager - .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); - self.annotation_set_items.push(annotation_set); - Ok(()) - } - - /// Insert the annnotations set for a method (but not the parameters annotations). - fn insert_method_annotation_set( - &mut self, - method_id: &IdMethod, - is_direct_method: bool, - ) -> Result<()> { - let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); - let method = if is_direct_method { - class.direct_methods.get(method_id).unwrap() - } else { - class.virtual_methods.get(method_id).unwrap() - }; - let mut annotations = method.annotations.clone(); - - let mut annotation_set = AnnotationSetItem { entries: vec![] }; - annotations.sort_by_key(|annot| annot.annotation.type_.clone()); - for annot in annotations { - annotation_set.entries.push(AnnotationOffItem { - annotation_off: self - .section_manager - .get_aligned_size(Section::AnnotationItem), - }); // linked in link_annotations() - - let item = AnnotationItem { - visibility: match ( - annot.visibility_build, - annot.visibility_runtime, - annot.visibility_system, - ) { - (true, false, false) => AnnotationVisibility::Build, - (false, true, false) => AnnotationVisibility::Runtime, - (false, false, true) => AnnotationVisibility::System, - _ => bail!( - "Annotation need visibility set to one and only one of build, \ - runtime or system" - ), // TODO: check if this is true - }, - annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, - }; - self.section_manager - .add_elt(Section::AnnotationItem, Some(item.size())); - self.annotation_items.push(item); - } - self.section_manager - .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); - self.annotation_set_items.push(annotation_set); - Ok(()) - } - - /// Insert the annotations set for a method parameter. - fn insert_parameters_annotation_set( - &mut self, - method_id: &IdMethod, - is_direct_method: bool, - parameter_idx: usize, - ) -> Result<()> { - let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); - let method = if is_direct_method { - class.direct_methods.get(method_id).unwrap() - } else { - class.virtual_methods.get(method_id).unwrap() - }; - let mut annotations = method.parameters_annotations[parameter_idx].clone(); - - let mut annotation_set = AnnotationSetItem { entries: vec![] }; - annotations.sort_by_key(|annot| annot.annotation.type_.clone()); - for annot in annotations { - annotation_set.entries.push(AnnotationOffItem { - annotation_off: self - .section_manager - .get_aligned_size(Section::AnnotationItem), - }); // linked in link_annotations() - - let item = AnnotationItem { - visibility: match ( - annot.visibility_build, - annot.visibility_runtime, - annot.visibility_system, - ) { - (true, false, false) => AnnotationVisibility::Build, - (false, true, false) => AnnotationVisibility::Runtime, - (false, false, true) => AnnotationVisibility::System, - _ => bail!( - "Annotation need visibility set to one and only one of build, \ - runtime or system" - ), // TODO: check if this is true - }, - annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, - }; - self.section_manager - .add_elt(Section::AnnotationItem, Some(item.size())); - self.annotation_items.push(item); - } - self.section_manager - .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); - self.annotation_set_items.push(annotation_set); - Ok(()) - } - - /// Insert the annotations set list for a method parameters. - fn insert_parameters_annotation_set_list( - &mut self, - method_id: &IdMethod, - is_direct_method: bool, - ) -> Result<()> { - let mut list = AnnotationSetRefList { list: vec![] }; - let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); - let method = if is_direct_method { - class.direct_methods.get(method_id).unwrap() - } else { - class.virtual_methods.get(method_id).unwrap() - }; - let param_has_annotation: Vec<_> = method - .parameters_annotations - .iter() - .map(|annots| !annots.is_empty()) - .collect(); - for (param_idx, has_annotation) in param_has_annotation.into_iter().enumerate() { - list.list.push(AnnotationSetRefItem { - annotations_off: if has_annotation { - let annotation_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetItem); - self.insert_parameters_annotation_set(method_id, is_direct_method, param_idx)?; - annotation_off + 1 - } else { - 0 - }, // linked in link_annotations() - }); - } - - self.section_manager - .add_elt(Section::AnnotationSetRefList, Some(list.size())); - self.annotation_set_lists.push(list); - Ok(()) - } - - /// Insert a class annotations (including field, methods and parameters annotations). - /// - /// # Note - /// - /// annotation_set_item objects are 4 bytes aligns, so their offset cannot be odd. - /// - /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset - /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow - /// to distinguish the offset of the first item (equal to zero before linking) and the value - /// 0 used to indicate an abscence of item. - fn insert_annotations(&mut self, class_id: &IdType) -> Result<()> { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let class_annotations_off = if !class.annotations.is_empty() { - let class_annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetItem); - self.insert_class_annotation_set(class_id) - .with_context(|| { - format!( - "Failed to insert class annotation for class {}", - class_id.__repr__() - ) - })?; - class_annotations_off + 1 - } else { - 0 - }; - - let mut field_ids = vec![]; - let (class, _) = self.class_defs.get(class_id).unwrap(); - field_ids.extend(class.static_fields.keys().cloned()); - field_ids.extend(class.instance_fields.keys().cloned()); + type_list_set.extend(proto_set.iter().map(|proto| proto.parameters.clone())); + let mut strings: Vec = string_set.into_iter().collect(); + strings.sort(); + let mut type_ids: Vec = type_set.into_iter().collect(); + type_ids.sort(); + let mut proto_ids: Vec = proto_set.into_iter().collect(); + proto_ids.sort(); + let mut field_ids: Vec = field_set.into_iter().collect(); field_ids.sort(); - let mut field_annotations = vec![]; - for field_id in field_ids { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let static_field = class.static_fields.get(&field_id); - let instance_field = class.instance_fields.get(&field_id); - let (is_static, field) = match (static_field, instance_field) { - (Some(field), None) => (true, field), - (None, Some(field)) => (false, field), - _ => bail!( - "Unexpected configuration: field {} is both a static and a instance field in {}", - field_id.__repr__(), class_id.__repr__()), - }; - if !field.annotations.is_empty() { - let annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetItem) - + 1; - self.insert_field_annotation_set(&field_id, is_static)?; - field_annotations.push(FieldAnnotation { - field_idx: *self.field_ids.get(&field_id).ok_or(anyhow!( - "Field {} in {} not found in dex builder", - field_id.__repr__(), - class_id.__repr__(), - ))? as u32, - annotations_off, // linked in link_annotations() - }); - } - } - - let mut method_ids = vec![]; - let (class, _) = self.class_defs.get(class_id).unwrap(); - method_ids.extend(class.direct_methods.keys().cloned()); - method_ids.extend(class.virtual_methods.keys().cloned()); + let mut method_ids: Vec = method_set.into_iter().collect(); method_ids.sort(); - let mut method_annotations = vec![]; - for method_id in &method_ids { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let direct_method = class.direct_methods.get(method_id); - let virtual_method = class.virtual_methods.get(method_id); - let (is_direct, method) = match (direct_method, virtual_method) { - (Some(method), None) => (true, method), - (None, Some(method)) => (false, method), - _ => bail!( - "Unexpected configuration: method {} is both a direct and a virtual method in {}", - method_id.__repr__(), class_id.__repr__()), - }; - if !method.annotations.is_empty() { - let annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetItem) - + 1; - self.insert_method_annotation_set(method_id, is_direct)?; - method_annotations.push(MethodAnnotation { - method_idx: *self.method_ids.get(method_id).ok_or(anyhow!( - "Method {} in {} not found in dex builder", - method_id.__repr__(), - class_id.__repr__(), - ))? as u32, - annotations_off, // linked in link_annotations() - }); - } - } + let mut type_lists: Vec> = type_list_set.into_iter().collect(); - let mut parameter_annotations = vec![]; - for method_id in method_ids { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let direct_method = class.direct_methods.get(&method_id); - let virtual_method = class.virtual_methods.get(&method_id); - let (is_direct, method) = match (direct_method, virtual_method) { - (Some(method), None) => (true, method), - (None, Some(method)) => (false, method), - _ => bail!( - "Unexpected configuration: method {} is both a direct and a virtual method in {}", - method_id.__repr__(), class_id.__repr__()), - }; - if !method.parameters_annotations.is_empty() { - let annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetRefList) - + 1; - self.insert_parameters_annotation_set_list(&method_id, is_direct)?; - parameter_annotations.push(ParameterAnnotation { - method_idx: *self.method_ids.get(&method_id).ok_or(anyhow!( - "Method {} in {} not found in dex builder", - method_id.__repr__(), - class_id.__repr__(), - ))? as u32, - annotations_off, // linked in link_annotations() - }); - } - } - - let item = AnnotationsDirectoryItem { - class_annotations_off, // linked in link_annotations() - field_annotations, - method_annotations, - parameter_annotations, - }; - self.section_manager - .add_elt(Section::AnnotationsDirectoryItem, Some(item.size())); - self.annotations_directory_items.push(item); - Ok(()) - } - - /// Insert a class_def_item in the class_defs section **and** the other struct that needs to be - /// generated on the fly. - /// - /// # Warning - /// - /// The class_defs section **MUST** be sorted by inheritance dependencies (parents classes and - /// interfaces must appear **before** child classes). Accordingly, this method must be invoked - /// in the right order. - /// - /// # Note - /// - /// annotations_directory_item, encoded_array_item and class_data_item objects are 4 bytes - /// aligns, so their offset cannot be odd. - /// - /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset - /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow - /// to distinguish the offset of the first item (equal to zero before linking) and the value - /// 0 used to indicate an abscence of item. - fn insert_class_def_item(&mut self, class_id: &IdType) -> Result<()> { - let idx = self.class_defs_list.len(); - self.class_defs - .entry(class_id.clone()) - .and_modify(|(_, i)| *i = idx); - let (class, _) = self.class_defs.get(class_id).unwrap(); - let class_data_off = if class.has_data_item() { - let class_data_off = self - .section_manager - .get_aligned_size(Section::ClassDataItem); - self.insert_class_data_item(class_id)?; - class_data_off + 1 - } else { - 0 - }; - // & vs &mut cluster-f, this make rust drop the ref so self hold by `class` before - // mutating self with `insert_class_data_item`, and get a new ref afterward - let (class, _) = self.class_defs.get(class_id).unwrap(); - let static_values_off = if class.has_static_values_array() { - let static_values_off = self - .section_manager - .get_aligned_size(Section::EncodedArrayItem); - self.insert_class_static_values(class_id)?; - static_values_off + 1 - } else { - 0 - }; - let (class, _) = self.class_defs.get(class_id).unwrap(); - let annotations_off = if class.has_annotations() { - let annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationsDirectoryItem); - self.insert_annotations(class_id)?; - annotations_off + 1 - } else { - 0 - }; - let (class, _) = self.class_defs.get(class_id).unwrap(); - self.class_defs_list.push(ClassDefItem { - class_idx: *self.type_ids.get(class_id).ok_or(anyhow!( - "Type {} (type of class {}) not found in dex builder", - class_id.__repr__(), - class.__repr__() - ))? as u32, - access_flags: class.get_raw_access_flags(), - superclass_idx: if let Some(sup) = &class.superclass { - *self.type_ids.get(sup).ok_or(anyhow!( - "Type {} (superclass of class {}) not found in dex builder", - sup.__repr__(), - class.__repr__() - ))? as u32 - } else { - NO_INDEX.0 - }, - interfaces_off: 0, - source_file_idx: if let Some(file) = &class.source_file { - *self.strings.get(file).ok_or(anyhow!( - "String {} (source file of class {}) not found in dex builder", - file.__repr__(), - class.__repr__() - ))? as u32 - } else { - NO_INDEX.0 - }, - - annotations_off, // need relinking once offset of class_def_item section is known - class_data_off, // need relinking once offset of class_data section is known - static_values_off, // need relinking once offset of encoded_array section is known - }); - self.section_manager.add_elt(Section::ClassDefItem, None); - Ok(()) - } - - fn gen_type_list_section(&mut self) -> Result<()> { - debug!("Generate the type_list section"); - // Collect all type lists - for proto in self.proto_ids.keys() { - if !proto.parameters.is_empty() { - let type_list = self.gen_type_list(&proto.parameters).with_context(|| { - format!("Failed to generate param list for {}", proto.__repr__()) - })?; - self.type_lists_index.insert(type_list, 0); - } - } - for (class, _) in self.class_defs.values() { - if !class.interfaces.is_empty() { - let type_list = self.gen_type_list(&class.interfaces).with_context(|| { - format!("Failed to generate interface list for {}", class.__repr__()) - })?; - self.type_lists_index.insert(type_list, 0); - } - } - - // safe type lists with their offset in the section - let mut offset = 0; - for (i, (list, idx)) in self.type_lists_index.iter_mut().enumerate() { - while offset % 4 != 0 { - // Alignment - self.section_manager.incr_section_size(Section::TypeList, 1); - offset += 1; - } - *idx = i; - self.type_lists_with_offset.push((list.clone(), offset)); - self.section_manager - .add_elt(Section::TypeList, Some(list.size())); - offset += list.size() as u32; - } - // The next section requires alignment to 4 - while offset % 4 != 0 { - // Alignment - self.section_manager.incr_section_size(Section::TypeList, 1); - offset += 1; - } - Ok(()) - } - - /// Generate the map list. - /// - /// # Warning - /// - /// All sections must be generated (but not linked) before generating the map list. - /// - /// This method switch the section manager from edit mode to read only. - fn gen_map_list(&mut self) -> Result<()> { - debug!("Generate the map_list"); - // Get the size of a map item - let map_item_size = 12; /* = MapItem { - type_: MapItemType::HeaderItem, - unused: 0, - size: 0, - offset: 0, - } - .size(); */ - // Empty map has a size 4, then we add the size of a MapItem for each element - // The size of the map_list must be computed before generating the map list, - // as it affect the offset of some sections. - self.section_manager.add_elt(Section::MapList, Some(4)); - for section in Section::VARIANT_LIST { - if !section.is_data() && self.section_manager.get_nb_elt(*section) != 0 { - self.section_manager - .incr_section_size(Section::MapList, map_item_size); - } - } - // All sections are knowns and should not be eddited anymore - self.section_manager.finalize_sections(); - for section in Section::VARIANT_LIST { - if !section.is_data() && self.section_manager.get_nb_elt(*section) != 0 { - /* - match section { - // Alignment - // Until Section::MapList included, the section are naturally alligned to 4 - _ => (), - } - */ - self.map_list.list.push(MapItem { - type_: section.get_map_item_type(), - unused: 0, - size: self.section_manager.get_nb_elt(*section) as u32, - offset: self.section_manager.get_offset(*section), - }); - } - } - Ok(()) - } - - /// Link the offsets in the header. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_header(&mut self) { - debug!("Link the header section"); - self.header.map_off = self.section_manager.get_offset(Section::MapList); - self.header.string_ids_size = self.section_manager.get_nb_elt(Section::StringIdItem) as u32; - self.header.string_ids_off = self.section_manager.get_offset(Section::StringIdItem); - self.header.type_ids_size = self.section_manager.get_nb_elt(Section::TypeIdItem) as u32; - self.header.type_ids_off = self.section_manager.get_offset(Section::TypeIdItem); - self.header.proto_ids_size = self.section_manager.get_nb_elt(Section::ProtoIdItem) as u32; - self.header.proto_ids_off = self.section_manager.get_offset(Section::ProtoIdItem); - self.header.field_ids_size = self.section_manager.get_nb_elt(Section::FieldIdItem) as u32; - self.header.field_ids_off = self.section_manager.get_offset(Section::FieldIdItem); - self.header.method_ids_size = self.section_manager.get_nb_elt(Section::MethodIdItem) as u32; - self.header.method_ids_off = self.section_manager.get_offset(Section::MethodIdItem); - self.header.class_defs_size = self.section_manager.get_nb_elt(Section::ClassDefItem) as u32; - self.header.class_defs_off = self.section_manager.get_offset(Section::ClassDefItem); - self.header.data_size = self.section_manager.get_unaligned_size(Section::Data); - self.header.data_off = self.section_manager.get_offset(Section::Data); - } - - /// Link the offsets in the call site id items. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_call_site_ids(&mut self) { - debug!("Link call site id items"); - for id in &mut self.call_site_ids { - id.call_site_off += self.section_manager.get_offset(Section::EncodedArrayItem); - } - } - - /// Link the offsets in class_def_items. - /// - /// # Warning - /// - /// This is the only link method called before generating the map list and finilizing the - /// section: - /// - /// Linking can only occur once all sections are entirelly generated, however, - /// `class_data_item.direct|virtual_methods[.].code_off` are Uleb128 encoded, meaning - /// that linking class_data_item modify the size of the class_data_items, hence the position - /// of the class_data_item and all element located after, as well as the size of the data - /// section. This is pretty bothersome and means that the sections **are** modified. - fn link_class_data(&mut self) -> Result<()> { - debug!("Link class data items"); - let mut unlinked_local_offset = 0; - let mut linked_local_offset = 0; - let code_section_off = self.section_manager.get_code_item_offset_prefinalized(); - for data in self.class_data_list.iter_mut() { - let unlinked_size = data.size() as u32; - for method in &mut data.direct_methods { - if method.code_off.0 != 0 { - method.code_off.0 += code_section_off - 1; - } - } - for method in &mut data.virtual_methods { - if method.code_off.0 != 0 { - method.code_off.0 += code_section_off - 1; - } - } - self.corrected_class_data_offset - .insert(unlinked_local_offset, linked_local_offset); - linked_local_offset += data.size() as u32; - unlinked_local_offset += unlinked_size; - } - self.section_manager.incr_section_size( - Section::ClassDataItem, - linked_local_offset as usize - unlinked_local_offset as usize, - ); - Ok(()) - } - - /// Link the offsets in proto_id_items. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_proto_id(&mut self) -> Result<()> { - debug!("Link proto id items"); - for (proto, idx) in &self.proto_ids { - if !proto.parameters.is_empty() { - let type_list = self.gen_type_list(&proto.parameters).with_context(|| { - format!("Failed to generate param list for {}", proto.__repr__()) - })?; - let offset = self.section_manager.get_offset(Section::TypeList) - + self.type_lists_with_offset[*self.type_lists_index.get(&type_list).unwrap()] - .1; - self.proto_ids_list[*idx].parameters_off = offset; - } - } - Ok(()) - } - - /// Link the offsets of class_data_items in class_def_items. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_class_def(&mut self) -> Result<()> { - debug!("Link class_def_items"); - for class_def in self.class_defs_list.iter_mut() { - // Link the class_data_item entries - // prelink value is set to offset in the section + 1 (to distinguish with 0) - if class_def.class_data_off != 0 { - let unlinked_local_offset = class_def.class_data_off - 1; - let linked_local_offset = *self - .corrected_class_data_offset - .get(&unlinked_local_offset) - .expect( - "Unlinked class_data_item offset not found in corrected_class_data_offset", - ); - class_def.class_data_off = - self.section_manager.get_offset(Section::ClassDataItem) + linked_local_offset; - } - // Link the annotations_directory_item entrie - // prelink value is set to offset in the section + 1 (to distinguish with 0) - if class_def.annotations_off != 0 { - class_def.annotations_off += self - .section_manager - .get_offset(Section::AnnotationsDirectoryItem) - - 1; - } - - // Link the static_values entries - if class_def.static_values_off != 0 { - class_def.static_values_off += - self.section_manager.get_offset(Section::EncodedArrayItem) - 1; - } - } - for (cls, idx) in self.class_defs.values() { - if !cls.interfaces.is_empty() { - let type_list = self.gen_type_list(&cls.interfaces).with_context(|| { - format!("Failed to generate interface list for {}", cls.__repr__()) - })?; - let offset = self.section_manager.get_offset(Section::TypeList) - + self.type_lists_with_offset[*self.type_lists_index.get(&type_list).unwrap()] - .1; - self.class_defs_list[*idx].interfaces_off = offset; - } - } - Ok(()) - } - - /// Link the offset of debug info item in code items. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_code(&mut self) { - debug!("Link the debug_info_off entries in code_items"); - for code in self.code_items.iter_mut() { - if code.debug_info_off != 0 { - code.debug_info_off += self.section_manager.get_offset(Section::DebugInfoItem) - 1; - } - } - } - - /// Link all annotations objects. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_annotations(&mut self) { - for annotation in self.annotations_directory_items.iter_mut() { - if annotation.class_annotations_off != 0 { - annotation.class_annotations_off += - self.section_manager.get_offset(Section::AnnotationSetItem) - 1; - } - for field_annotation in annotation.field_annotations.iter_mut() { - if field_annotation.annotations_off != 0 { - field_annotation.annotations_off += - self.section_manager.get_offset(Section::AnnotationSetItem) - 1; - } - } - for method_annotation in annotation.method_annotations.iter_mut() { - if method_annotation.annotations_off != 0 { - method_annotation.annotations_off += - self.section_manager.get_offset(Section::AnnotationSetItem) - 1; - } - } - for parameter_annotation in annotation.parameter_annotations.iter_mut() { - if parameter_annotation.annotations_off != 0 { - parameter_annotation.annotations_off += self - .section_manager - .get_offset(Section::AnnotationSetRefList) - - 1; - } - } - } - for annotation_set in self.annotation_set_items.iter_mut() { - for entry in annotation_set.entries.iter_mut() { - entry.annotation_off += self.section_manager.get_offset(Section::AnnotationItem); - // -1 now when linking frag - } - } - for list in self.annotation_set_lists.iter_mut() { - for annotation in list.list.iter_mut() { - if annotation.annotations_off != 0 { - annotation.annotations_off += - self.section_manager.get_offset(Section::AnnotationSetItem) - 1; - } - } - } - } - - fn write_dex_file(&mut self, writer: &mut dyn Write) -> Result<()> { - self.section_manager.reset(); - self.section_manager.add_elt(Section::HeaderItem, None); - - self.gen_string_data_section()?; - self.gen_type_ids_section()?; - self.gen_proto_ids_section()?; - self.gen_field_ids_section()?; - self.gen_method_ids_section()?; - - debug!("Sort classes and generate the class_defs and class_data section"); - for class_id in self.get_sorted_class_def()? { - self.insert_class_def_item(&class_id)?; - } - self.gen_type_list_section()?; - - // start by linking class_data_items to populate self.corrected_class_data_offset - // and update the class_data_item sections size. - // Why before gen_map_list? Because the offsets in class_data_items are F***ing Uleb128 - // encoded, so there size change when linking (see doc of self.corrected_class_data_offset). - let code_offset = self.section_manager.get_code_item_offset_prefinalized(); - self.link_class_data()?; - self.gen_map_list()?; - assert_eq!( - code_offset, - self.section_manager.get_offset(Section::CodeItem), - "Prelinking computed value and post linking value for \ - the offset of the code_item section don't match" - ); - - // From now on, all section are generated and the value in section_manager do not change, - - self.link_header(); - self.link_call_site_ids(); - self.link_proto_id()?; - self.link_class_def()?; - self.link_code(); - self.link_annotations(); - - debug!("Serialize the dex file"); - let mut buffer = Cursor::new(Vec::::new()); - - self.check_section_offset(&buffer, Section::HeaderItem); - Self::fix_section_alignement(&mut buffer, Section::HeaderItem)?; - self.header.serialize(&mut buffer)?; - // StringIdItem section - let mut string_off = self.section_manager.get_offset(Section::StringDataItem); - self.check_section_offset(&buffer, Section::StringIdItem); - for string in self.string_data_list.iter() { - let str_id = StringIdItem { - string_data_off: string_off, - }; - Self::fix_section_alignement(&mut buffer, Section::StringIdItem)?; - str_id.serialize(&mut buffer)?; - string_off += string.size() as u32; - } - // TypeId section - self.check_section_offset(&buffer, Section::TypeIdItem); - for ty in &self.type_ids_list { - Self::fix_section_alignement(&mut buffer, Section::TypeIdItem)?; - ty.serialize(&mut buffer)?; - } - // ProtoId section - self.check_section_offset(&buffer, Section::ProtoIdItem); - for proto in &self.proto_ids_list { - Self::fix_section_alignement(&mut buffer, Section::ProtoIdItem)?; - proto.serialize(&mut buffer)?; - } - // FieldIdItem section - self.check_section_offset(&buffer, Section::FieldIdItem); - for field_id in &self.field_ids_list { - Self::fix_section_alignement(&mut buffer, Section::FieldIdItem)?; - field_id.serialize(&mut buffer)?; - } - // MethodIdItem section - self.check_section_offset(&buffer, Section::MethodIdItem); - for method_id in &self.method_ids_list { - Self::fix_section_alignement(&mut buffer, Section::MethodIdItem)?; - method_id.serialize(&mut buffer)?; - } - // ClassDefItem section - self.check_section_offset(&buffer, Section::ClassDefItem); - for class_def in &self.class_defs_list { - Self::fix_section_alignement(&mut buffer, Section::ClassDefItem)?; - class_def.serialize(&mut buffer)?; - } - // CallSiteIdItem, data are inserted as encoded array item later - self.check_section_offset(&buffer, Section::CallSiteIdItem); - for call_site_id in &self.call_site_ids { - Self::fix_section_alignement(&mut buffer, Section::CallSiteIdItem)?; - call_site_id.serialize(&mut buffer)?; - } - - // MethodHandleItem section - self.check_section_offset(&buffer, Section::MethodHandleItem); - for handle in &self.method_handles { - Self::fix_section_alignement(&mut buffer, Section::MethodHandleItem)?; - handle.serialize(&mut buffer)?; - } - // MapList - self.check_section_offset(&buffer, Section::Data); - self.check_section_offset(&buffer, Section::MapList); - Self::fix_section_alignement(&mut buffer, Section::MapList)?; - self.map_list.serialize(&mut buffer)?; - // TypeList, - self.check_section_offset(&buffer, Section::TypeList); - for (list, _) in &self.type_lists_with_offset { - Self::fix_section_alignement(&mut buffer, Section::TypeList)?; - list.serialize(&mut buffer)?; - } - // AnnotationSetRefList section - self.check_section_offset(&buffer, Section::AnnotationSetRefList); - for list in &self.annotation_set_lists { - Self::fix_section_alignement(&mut buffer, Section::AnnotationSetRefList)?; - list.serialize(&mut buffer)?; - } - // AnnotationSetItem section - self.check_section_offset(&buffer, Section::AnnotationSetItem); - for set in &self.annotation_set_items { - Self::fix_section_alignement(&mut buffer, Section::AnnotationSetItem)?; - set.serialize(&mut buffer)?; - } - // CodeItem section - self.check_section_offset(&buffer, Section::CodeItem); - for code_item in &self.code_items { - Self::fix_section_alignement(&mut buffer, Section::CodeItem)?; - code_item.serialize(&mut buffer)? - } - // StringDataItem section - self.check_section_offset(&buffer, Section::StringDataItem); - for string in &self.string_data_list { - Self::fix_section_alignement(&mut buffer, Section::StringDataItem)?; - string.serialize(&mut buffer)?; - } - // DebugInfoItem section - self.check_section_offset(&buffer, Section::DebugInfoItem); - for debug_info in &self.debug_info_items { - Self::fix_section_alignement(&mut buffer, Section::DebugInfoItem)?; - debug_info.serialize(&mut buffer)?; - } - // AnnotationItem section - self.check_section_offset(&buffer, Section::AnnotationItem); - for annot in &self.annotation_items { - Self::fix_section_alignement(&mut buffer, Section::AnnotationItem)?; - annot.serialize(&mut buffer)?; - } - // EncodedArrayItem section - self.check_section_offset(&buffer, Section::EncodedArrayItem); - for array in &self.encoded_array_items { - Self::fix_section_alignement(&mut buffer, Section::EncodedArrayItem)?; - array.serialize(&mut buffer)?; - } - // AnnotationsDirectoryItem section - self.check_section_offset(&buffer, Section::AnnotationsDirectoryItem); - for dir in &self.annotations_directory_items { - Self::fix_section_alignement(&mut buffer, Section::AnnotationsDirectoryItem)?; - dir.serialize(&mut buffer)?; - } - // ClassDataItem section - self.check_section_offset(&buffer, Section::ClassDataItem); - for data in &self.class_data_list { - Self::fix_section_alignement(&mut buffer, Section::ClassDataItem)?; - data.serialize(&mut buffer)?; - } - // TODO: HiddenapiClassDataItem, - /* - self.check_section_offset(&buffer, Section::HiddenapiClassDataItem); - Self::fix_section_alignement(&mut buffer, Section::HiddenapiClassDataItem)?; - */ - - let end_data = buffer.position(); - assert_eq!( - end_data as u32, - self.header.data_off + self.header.data_size - ); - - // compute signature - buffer.seek(SeekFrom::Start(8 + 4 + 20))?; - let mut hasher = Sha1::new(); - io::copy(&mut buffer, &mut hasher)?; - self.header.signature = hasher.finalize().into(); - let size = buffer.seek(SeekFrom::End(0))? as u32; - self.header.file_size = size; - buffer.rewind()?; - self.header.serialize(&mut buffer)?; - - // Compute checksum - //buffer.seek(SeekFrom::Start(8 + 4))?; - let mut adler = Adler32::new(); - adler.write_slice(&buffer.get_ref()[8 + 4..]); - self.header.checksum = adler.checksum(); - buffer.rewind()?; - self.header.serialize(&mut buffer)?; - - // copy buffer to output - buffer.rewind()?; - io::copy(&mut buffer, writer)?; - - Ok(()) - } - - /// Insert 0 to a buffer until the right alignment is reached for an element of the - /// given section. - fn fix_section_alignement(buffer: &mut Cursor>, section: Section) -> Result<()> { - while buffer.position() % section.get_item_alignment() as u64 != 0 { - Serializable::serialize(&0u8, buffer)?; - } - Ok(()) - } - - /// Check if a section - fn check_section_offset(&self, buffer: &Cursor, section: Section) { - let mut pos = buffer.position(); - while pos % section.get_item_alignment() as u64 != 0 { - pos += 1; - } - let expected = self.section_manager.get_offset(section) as u64; - assert_eq!( - pos, expected, - "Computed section offset and actual section offset do not match for section \ - {section:?}, expected 0x{expected:x}, found 0x{pos:x}" - ); - } - - /// Compute the order of the classes in the section `class_defs`. - /// Class definitions must be sorted so that a class's superclass and interfaces - /// are before the class. - fn get_sorted_class_def(&self) -> Result> { - // Use Kahn's algorithm - let mut graph: HashMap<&IdType, (HashSet<&IdType>, HashSet<&IdType>)> = HashMap::new(); - for (ty, (def, _)) in &self.class_defs { - let mut edges_to = HashSet::new(); - if let Some(sup) = def.superclass.as_ref() { - if self.class_defs.get(sup).is_some() { - edges_to.insert(sup); - } - } - for sup in &def.interfaces { - if self.class_defs.get(sup).is_some() { - edges_to.insert(sup); - } - } - for n_to in &edges_to { - let (from, _) = graph - .entry(n_to) - .or_insert((HashSet::new(), HashSet::new())); - from.insert(ty); - } - let (_, to) = graph.entry(ty).or_insert((HashSet::new(), HashSet::new())); - to.extend(edges_to); - } - - let mut sorted = vec![]; - let mut no_outgoing: VecDeque<&IdType> = VecDeque::new(); - no_outgoing.extend( - graph - .iter() - .filter(|(_, (_, to))| to.is_empty()) - .map(|(ty, _)| ty), - ); - if no_outgoing.is_empty() { - bail!("The class inheritance topoloy is either empty or cyclic"); - } - - while let Some(n) = no_outgoing.pop_front() { - sorted.push(n.clone()); - let (from, _) = graph.get(n).cloned().unwrap(); - for n_from in from { - graph.entry(n_from).and_modify(|(_, to)| _ = to.remove(n)); - let (_, to) = graph.get(n_from).unwrap(); - if to.is_empty() { - no_outgoing.push_back(n_from); - } - } - graph - .entry(n) - .and_modify(|(from, _)| *from = HashSet::new()); - } - for (_, (from, to)) in graph { - if !from.is_empty() || !to.is_empty() { - bail!("The class inheritance topology is cyclic"); - } - } - Ok(sorted) - } - - fn gen_type_list(&self, list: &[IdType]) -> Result { - let mut type_list = TypeList { list: vec![] }; - for ty in list { - type_list.list.push(TypeItem { - type_idx: *self.type_ids.get(ty).ok_or(anyhow!( - "Could not found type {} in dex builder", - ty.__repr__() - ))? as u16, - }); - } - Ok(type_list) - } - - pub fn gen_dex_file_to_vec(&mut self) -> Result> { - let mut output = Cursor::new(Vec::::new()); - self.write_dex_file(&mut output)?; - Ok(output.into_inner()) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum Section { - HeaderItem, - StringIdItem, - TypeIdItem, - ProtoIdItem, - FieldIdItem, - MethodIdItem, - ClassDefItem, - CallSiteIdItem, - MethodHandleItem, - Data, - MapList, - TypeList, - AnnotationSetRefList, - AnnotationSetItem, - CodeItem, - StringDataItem, - DebugInfoItem, - AnnotationItem, - EncodedArrayItem, - AnnotationsDirectoryItem, - HiddenapiClassDataItem, - ClassDataItem, -} - -impl Section { - const VARIANT_LIST: &'static [Self] = &[ - Self::HeaderItem, - Self::StringIdItem, - Self::TypeIdItem, - Self::ProtoIdItem, - Self::FieldIdItem, - Self::MethodIdItem, - Self::ClassDefItem, - Self::CallSiteIdItem, - Self::MethodHandleItem, - Self::Data, - Self::MapList, - Self::TypeList, - Self::AnnotationSetRefList, - Self::AnnotationSetItem, - Self::CodeItem, - Self::StringDataItem, - Self::DebugInfoItem, - Self::AnnotationItem, - Self::EncodedArrayItem, - Self::AnnotationsDirectoryItem, - Self::ClassDataItem, // must be last because contains offsets in Uleb, - // so size change when linking ! - Self::HiddenapiClassDataItem, - ]; - - fn get_index(&self) -> usize { - match self { - Self::HeaderItem => 0, - Self::StringIdItem => 1, - Self::TypeIdItem => 2, - Self::ProtoIdItem => 3, - Self::FieldIdItem => 4, - Self::MethodIdItem => 5, - Self::ClassDefItem => 6, - Self::CallSiteIdItem => 7, - Self::MethodHandleItem => 8, - Self::Data => 9, - Self::MapList => 10, - Self::TypeList => 11, - Self::AnnotationSetRefList => 12, - Self::AnnotationSetItem => 13, - Self::CodeItem => 14, - Self::StringDataItem => 15, - Self::DebugInfoItem => 16, - Self::AnnotationItem => 17, - Self::EncodedArrayItem => 18, - Self::AnnotationsDirectoryItem => 19, - Self::ClassDataItem => 20, - Self::HiddenapiClassDataItem => 21, - } - } - - fn get_elt_size(&self, default_size: Option) -> usize { - let fixed_size = match self { - Self::HeaderItem => Some(0x70), - Self::StringIdItem => Some(4), - Self::TypeIdItem => Some(4), - Self::ProtoIdItem => Some(0xc), - Self::FieldIdItem => Some(8), - Self::MethodIdItem => Some(8), - Self::ClassDefItem => Some(0x20), - Self::CallSiteIdItem => Some(4), - Self::MethodHandleItem => Some(8), - Self::Data => panic!("Element cannot be inserted in data dirctly"), - Self::MapList => None, - Self::TypeList => None, - Self::AnnotationSetRefList => None, - Self::AnnotationSetItem => None, - Self::ClassDataItem => None, - Self::CodeItem => None, - Self::StringDataItem => None, - Self::DebugInfoItem => None, - Self::AnnotationItem => None, - Self::EncodedArrayItem => None, - Self::AnnotationsDirectoryItem => None, - Self::HiddenapiClassDataItem => None, - }; - if let (Some(fixed_size), Some(default_size)) = (fixed_size, default_size) { - if fixed_size == default_size { - default_size - } else { - panic!( - "Element in {:?} have a size of {}, not {}", - self, fixed_size, default_size - ) - } - } else { - fixed_size.or(default_size).unwrap_or_else(|| { - panic!( - "Element of {:?} don't have a fixed size, you need to provide one", - self - ) - }) - } - } - - /// Return the previous section if it exist. - fn prev(&self) -> Option { - match self { - Self::HeaderItem => None, - Self::StringIdItem => Some(Self::HeaderItem), - Self::TypeIdItem => Some(Self::StringIdItem), - Self::ProtoIdItem => Some(Self::TypeIdItem), - Self::FieldIdItem => Some(Self::ProtoIdItem), - Self::MethodIdItem => Some(Self::FieldIdItem), - Self::ClassDefItem => Some(Self::MethodIdItem), - Self::CallSiteIdItem => Some(Self::ClassDefItem), - Self::MethodHandleItem => Some(Self::CallSiteIdItem), - Self::Data => Some(Self::MethodHandleItem), - Self::MapList => Some(Self::MethodHandleItem), // Data is just an indicator - Self::TypeList => Some(Self::MapList), - Self::AnnotationSetRefList => Some(Self::TypeList), - Self::AnnotationSetItem => Some(Self::AnnotationSetRefList), - Self::CodeItem => Some(Self::AnnotationSetItem), - Self::StringDataItem => Some(Self::CodeItem), - Self::DebugInfoItem => Some(Self::StringDataItem), - Self::AnnotationItem => Some(Self::DebugInfoItem), - Self::EncodedArrayItem => Some(Self::AnnotationItem), - Self::AnnotationsDirectoryItem => Some(Self::EncodedArrayItem), - Self::ClassDataItem => Some(Self::AnnotationsDirectoryItem), - Self::HiddenapiClassDataItem => Some(Self::ClassDataItem), - } - } - - fn get_map_item_type(&self) -> MapItemType { - match self { - Self::HeaderItem => MapItemType::HeaderItem, - Self::StringIdItem => MapItemType::StringIdItem, - Self::TypeIdItem => MapItemType::TypeIdItem, - Self::ProtoIdItem => MapItemType::ProtoIdItem, - Self::FieldIdItem => MapItemType::FieldIdItem, - Self::MethodIdItem => MapItemType::MethodIdItem, - Self::ClassDefItem => MapItemType::ClassDefItem, - Self::CallSiteIdItem => MapItemType::CallSiteIdItem, - Self::MethodHandleItem => MapItemType::MethodHandleItem, - Self::Data => panic!("Data is not a MatItemType"), - Self::MapList => MapItemType::MapList, - Self::TypeList => MapItemType::TypeList, - Self::AnnotationSetRefList => MapItemType::AnnotationSetRefList, - Self::AnnotationSetItem => MapItemType::AnnotationSetItem, - Self::CodeItem => MapItemType::CodeItem, - Self::StringDataItem => MapItemType::StringDataItem, - Self::DebugInfoItem => MapItemType::DebugInfoItem, - Self::AnnotationItem => MapItemType::AnnotationItem, - Self::EncodedArrayItem => MapItemType::EncodedArrayItem, - Self::AnnotationsDirectoryItem => MapItemType::AnnotationsDirectoryItem, - Self::ClassDataItem => MapItemType::ClassDataItem, - Self::HiddenapiClassDataItem => MapItemType::HiddenapiClassDataItem, - } - } - - /// Return the alignment of the item in byte. - fn get_item_alignment(&self) -> u32 { - match self { - Self::HeaderItem => 4, - Self::StringIdItem => 4, - Self::TypeIdItem => 4, - Self::ProtoIdItem => 4, - Self::FieldIdItem => 4, - Self::MethodIdItem => 4, - Self::ClassDefItem => 4, - Self::CallSiteIdItem => 1, - Self::MethodHandleItem => 4, - Self::Data => 1, - Self::MapList => 4, - Self::TypeList => 4, - Self::AnnotationSetRefList => 4, - Self::AnnotationSetItem => 4, - Self::CodeItem => 4, - Self::StringDataItem => 1, - Self::DebugInfoItem => 1, - Self::AnnotationItem => 1, - Self::EncodedArrayItem => 1, - Self::AnnotationsDirectoryItem => 4, - Self::ClassDataItem => 1, - Self::HiddenapiClassDataItem => 1, - } - } - - fn is_data(&self) -> bool { - matches!(self, Self::Data) - } -} - -#[derive(Debug, Default, Clone)] -struct SectionManager { - sizes: [u32; Self::NB_SECTION], - nb_elt: [usize; Self::NB_SECTION], - offsets: [u32; Self::NB_SECTION], - editable: bool, -} - -impl SectionManager { - const NB_SECTION: usize = 22; - - fn reset(&mut self) { - self.sizes = [0; Self::NB_SECTION]; - self.nb_elt = [0; Self::NB_SECTION]; - self.offsets = [0; Self::NB_SECTION]; - self.editable = true; - } - - fn add_elt(&mut self, section: Section, size: Option) { - if !self.editable { - panic!("Try to modify a section when the sections are set to read only"); - } - if section.is_data() { - panic!("Cannot add element directly in section data"); - } - while self.sizes[section.get_index()] % section.get_item_alignment() != 0 { - self.sizes[section.get_index()] += 1; - } - self.sizes[section.get_index()] += section.get_elt_size(size) as u32; - self.nb_elt[section.get_index()] += 1; - } - - fn incr_section_size(&mut self, section: Section, size: usize) { - if !self.editable { - panic!("Try to modify a section when the sections are set to read only"); - } - self.sizes[section.get_index()] += size as u32; - } - - fn get_offset(&self, section: Section) -> u32 { - if self.editable { - panic!("Try to get section offset before sections are finilized"); - } - let size = self.offsets[section.get_index()]; - let alignment = section.get_item_alignment(); - if size % alignment != 0 { - panic!( - "section {section:?} should be aligned on {alignment} bytes, \ - found section offset 0x{size:x}" - ); // avoid by finilized - } - size - } - - fn get_unaligned_size(&self, section: Section) -> u32 { - if section.is_data() { - self.sizes[section.get_index()..].iter().sum() - } else { - self.sizes[section.get_index()] - } - } - - /// The position of a potential new item in the section considering alignment. - fn get_aligned_size(&self, section: Section) -> u32 { - let mut size = self.get_unaligned_size(section); - while size % section.get_item_alignment() != 0 { - size += 1; - } - size - } - - fn get_nb_elt(&self, section: Section) -> usize { - self.nb_elt[section.get_index()] - } - - /// Finialize the sections: switch to read only and fix the section alignment. - fn finalize_sections(&mut self) { - for section in Section::VARIANT_LIST { - while self.sizes[..section.get_index()].iter().sum::() - % section.get_item_alignment() - != 0 - { - self.incr_section_size( - section.prev().expect( - "First section (Header) should alway be aligned but \ - found unaligned section without predecessor", - ), - 1, - ); - } - } - let mut offset = 0; - for section in Section::VARIANT_LIST { - self.offsets[section.get_index()] = offset; - offset += self.sizes[section.get_index()]; - } - - self.editable = false; - } - - /// This method exist for the only purpose of linking the method code offset inside - /// the class data items. This linking needs to be done before finilizing because it change the - /// size of the class data item section. - /// - /// Seriously, avoid using this. - fn get_code_item_offset_prefinalized(&mut self) -> u32 { - if !self.editable || self.get_nb_elt(Section::MapList) != 0 { - panic!("Don't use this method for other purpose than linking class_data_items"); - } - let mut map_list_size = 4; - let map_item_size = 12; /* = MapItem { - type_: MapItemType::HeaderItem, - unused: 0, - size: 0, - offset: 0, - } - .size(); */ - for section in Section::VARIANT_LIST { - if !section.is_data() - && (self.get_nb_elt(*section) != 0 || section == &Section::MapList) - { - map_list_size += map_item_size; - } - } - let mut offset = map_list_size; // This is aligned so it wont affect alignment - for section in &Section::VARIANT_LIST[..Section::CodeItem.get_index()] { - // size Section::Data and size Section::MapList are 0 - while offset % section.get_item_alignment() != 0 { - offset += 1; - } - offset += self.sizes[section.get_index()]; - } - - offset - } - - /// Display the sections informations. - #[allow(dead_code)] - fn show(&self) { - let mut offset = 0; - for section in Section::VARIANT_LIST { - let size = self.get_unaligned_size(*section); - let new_offset = offset + size; - let nb_elt = self.get_nb_elt(*section); - println!( - "{section:?}: 0x{offset:x} -> 0x{new_offset:x} (size: 0x{size:x}, \ - nb elt: {nb_elt})" + for (i, fragment) in fragments.iter().enumerate() { + fragment.link_global_ids( + &strings, + &type_ids, + &proto_ids, + &field_ids, + &method_ids, + nb_method_handle_before[i], ); - if !section.is_data() { - offset = new_offset; - } } + + vec![] } } From 232b906db1dcf1c3466ba035d435b0b0d7f8da15 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Thu, 21 Mar 2024 14:21:54 +0100 Subject: [PATCH 07/14] wip --- androscalpel/src/dex_writer_old.rs | 3255 ++++++++++++++++++++++++++++ 1 file changed, 3255 insertions(+) create mode 100644 androscalpel/src/dex_writer_old.rs diff --git a/androscalpel/src/dex_writer_old.rs b/androscalpel/src/dex_writer_old.rs new file mode 100644 index 0000000..f967970 --- /dev/null +++ b/androscalpel/src/dex_writer_old.rs @@ -0,0 +1,3255 @@ +//! The structure that generate a .dex from classes. + +use std::collections::{HashMap, HashSet, VecDeque}; +use std::io; +use std::io::{Cursor, Seek, SeekFrom, Write}; + +use adler::Adler32; +use anyhow::{anyhow, bail, Context}; +use log::debug; +use sha1::{Digest, Sha1}; + +use crate::Result; +use crate::*; +use androscalpel_serializer::*; + +use crate::ins::{CallSite, Instruction}; +use crate::instructions::*; +use androscalpel_serializer::Instruction as InsFormat; + +#[derive(Debug, Clone)] +pub struct DexWriter { + header: HeaderItem, + /// The strings in the dex file. Initially values are set to zero. + /// Once the strings are sorted, the values of the map are set to the + /// strings idx + strings: HashMap, + /// The types in the dex file. Initially values are set to zero. + /// Once the types are sorted, the values of the map are set to the + /// types idx + type_ids: HashMap, + /// The prototypes in the dex file. Initially values are set to zero. + /// Once the prototypes are sorted, the values of the map are set to the + /// prototypes idx + proto_ids: HashMap, + /// The field ids in the dex file. Initially values are set to zero. + /// Once the fields are sorted, the values of the map are set to the + /// fields idx + field_ids: HashMap, + /// The methods ids in the dex file. Initially values are set to zero. + /// Once the methods are sorted, the values of the map are set to the + /// method idx + method_ids: HashMap, + /// The classes defined in the dex file. Initially values are set to the classes and zero. + /// Once the class definitions are sorted, the values are set to the classes and there + /// index in the `class_defs` section. + class_defs: HashMap, + /// The call sites refered to in the bytecode. + call_site_ids: Vec, + /// A struct that keep track of sections size, nb elements and offsets. + section_manager: SectionManager, + /// The string_data section. Once populated, the elements must be sorted according to the spec. + string_data_list: Vec, + /// The type_ids sections. Once populated, the elements must be sorted according to the spec. + type_ids_list: Vec, + /// The proto_ids section. Once populated, the elements must be sorted according to the spec. + /// the `parameter_off` field of the prototypes must be linked before serializing the section. + proto_ids_list: Vec, + /// The field_ids section. Once populated, the elements must be sorted according to the spec. + field_ids_list: Vec, + /// The method_ids section. Once populated, the elements must be sorted according to the spec. + method_ids_list: Vec, + /// The class_defs section. Once populated, the elements must be sorted according to the spec. + /// The `interfaces_off`, `annotations_off`, `class_data_off` and `static_values_off` fields + /// must be linked before serializing. Before liking, the `annotations_off`, `class_data_off` + /// and `static_values_off` field take the value of the offset from the start of theire + /// respective section (or zero if the class does not have a value associated to the field). + class_defs_list: Vec, + /// The class_data section. + class_data_list: Vec, + /// The type lists found in the classes associated to their index in the type_lists section. + type_lists_index: HashMap, + /// The type_lists section and the offset of the lists inside the section. + type_lists_with_offset: Vec<(TypeList, u32)>, + /// The encoded_array_items section. + encoded_array_items: Vec, + /// The annotations_directory_item section. + annotations_directory_items: Vec, + /// The annotation_set_item section. + annotation_set_items: Vec, + /// The annotation item section. + annotation_items: Vec, + /// The annotation_set_ref_list section. + annotation_set_lists: Vec, + /// The method_handles section. + method_handles: Vec, + /// The code_items sections. + code_items: Vec, + /// The debug info items section. + debug_info_items: Vec, + /// The map_list + map_list: MapList, + /// Map the **local** offset of **unlinked** class data offset to the **local** + /// offset of the **linked**. + /// + /// local offset means the offset of the first data item in the section is 0 (i.e. not + /// the offset from the beginning of the file). + /// + /// This hack is necessary (well, maybe not necessary but that would require some refactoring) + /// because someone thought that encoding offsets in Uleb format was a good idea. It was not. + /// The size of the encoded offset depend on the value of the offset, so the linking change the + /// size of the struct, and doing so change the offset of the next struct, ect. + /// + /// When generating the structs refering to `class_data_item`s, the local offset of the + /// unlinked `class_data_item` is known, but not the linked offset. This struct is filled + /// when linking the `class_data_item`s, and can then be used to link the structs refering to + /// the `class_data_item`s. + corrected_class_data_offset: HashMap, +} + +impl Default for DexWriter { + fn default() -> Self { + Self { + header: HeaderItem { + magic: DexFileMagic { + //version: [0x30, 0x33, 0x39], + version: [0x30, 0x33, 0x37], + }, // TODO: find a better default version + checksum: 0, + signature: [0u8; 20], + file_size: 0, + header_size: 0x70, + endian_tag: EndianConstant::EndianConstant, + link_size: 0, + link_off: 0, + map_off: 0, + string_ids_size: 0, + string_ids_off: 0, + type_ids_size: 0, // At most 0xffff + type_ids_off: 0, + proto_ids_size: 0, // At most 0xffff + proto_ids_off: 0, + field_ids_size: 0, + field_ids_off: 0, + method_ids_size: 0, + method_ids_off: 0, + class_defs_size: 0, + class_defs_off: 0, + data_size: 0, // Must be an even multiple of sizeof(uint) -> % 8 = 0 + data_off: 0, + }, + strings: HashMap::new(), + type_ids: HashMap::new(), + proto_ids: HashMap::new(), + field_ids: HashMap::new(), + method_ids: HashMap::new(), + class_defs: HashMap::new(), + call_site_ids: vec![], + section_manager: SectionManager::default(), + string_data_list: vec![], + type_ids_list: vec![], + proto_ids_list: vec![], + field_ids_list: vec![], + method_ids_list: vec![], + class_defs_list: vec![], + class_data_list: vec![], + type_lists_index: HashMap::new(), + type_lists_with_offset: vec![], + map_list: MapList::default(), + encoded_array_items: vec![], + method_handles: vec![], + code_items: vec![], + annotations_directory_items: vec![], + annotation_set_items: vec![], + annotation_items: vec![], + annotation_set_lists: vec![], + debug_info_items: vec![], + corrected_class_data_offset: HashMap::new(), + } + } +} + +pub enum DexWritterError { + OutOfSpace(String), +} + +impl DexWriter { + pub fn new() -> Self { + Self::default() + } + + pub fn add_class(&mut self, class: &Class) -> Result<(), DexWritterError> { + debug!("Adding class {} to dex builder", class.descriptor.__str__()); + let new_strings = class.get_all_strings(); + + let new_types = class.get_all_types(); + let new_nb_types = self.type_ids.len() + + new_types + .iter() + .filter(|ty| self.type_ids.get(ty).is_none()) + .count(); + if new_nb_types >= u16::MAX as usize { + return Err(DexWritterError::OutOfSpace( + "To many types for one dex file".into(), + )); + } + + let new_protos = class.get_all_protos(); + let new_nb_protos = self.proto_ids.len() + + new_protos + .iter() + .filter(|proto| self.proto_ids.get(proto).is_none()) + .count(); + if new_nb_protos >= u16::MAX as usize { + return Err(DexWritterError::OutOfSpace( + "To many prototypes for one dex file".into(), + )); + } + + let new_field_ids = class.get_all_field_ids(); + let new_nb_field_ids = self.field_ids.len() + + new_field_ids + .iter() + .filter(|field| self.field_ids.get(field).is_none()) + .count(); + if new_nb_field_ids >= u16::MAX as usize { + return Err(DexWritterError::OutOfSpace( + "To many field ids for one dex file".into(), + )); + } + + let new_method_ids = class.get_all_method_ids(); + let new_nb_method_ids = self.method_ids.len() + + new_method_ids + .iter() + .filter(|meth| self.method_ids.get(meth).is_none()) + .count(); + if new_nb_method_ids >= u16::MAX as usize { + return Err(DexWritterError::OutOfSpace( + "To many method ids for one dex file".into(), + )); + } + + for string in new_strings { + self.strings.insert(string, 0); + } + for ty in new_types { + self.type_ids.insert(ty, 0); + } + for proto in new_protos { + self.proto_ids.insert(proto, 0); + } + for field in new_field_ids { + self.field_ids.insert(field, 0); + } + for method in new_method_ids { + self.method_ids.insert(method, 0); + } + self.class_defs + .insert(class.descriptor.clone(), (class.clone(), 0)); + Ok(()) + } + + pub fn add_string(&mut self, string: DexString) { + self.strings.insert(string, 0); + } + + fn gen_string_data_section(&mut self) -> Result<()> { + debug!("Sort string and generate string_data_item and string_ids sections"); + let mut string_ids_list: Vec = self.strings.keys().cloned().collect(); + string_ids_list.sort(); + for (idx, string) in string_ids_list.iter().enumerate() { + self.strings + .entry(string.clone()) + .and_modify(|val| *val = idx); + self.section_manager.add_elt(Section::StringIdItem, None); + self.section_manager + .add_elt(Section::StringDataItem, Some(string.0.size())); + } + self.string_data_list = string_ids_list + .into_iter() + .map(|string| string.into()) + .collect(); + Ok(()) + } + + fn gen_type_ids_section(&mut self) -> Result<()> { + debug!("Sort types and generate type_id_item section"); + let mut type_ids_list: Vec = self.type_ids.keys().cloned().collect(); + type_ids_list.sort(); + for (idx, ty) in type_ids_list.iter().enumerate() { + self.type_ids.entry(ty.clone()).and_modify(|val| *val = idx); + self.section_manager.add_elt(Section::TypeIdItem, None); + } + let mut type_ids_list_aux = vec![]; + for ty in type_ids_list.into_iter() { + type_ids_list_aux.push(TypeIdItem { + descriptor_idx: *self.strings.get(&ty.0).ok_or(anyhow!( + "String {} (name of type {}) not found in dex builder", + ty.0.__repr__(), + ty.__repr__() + ))? as u32, + }); + } + self.type_ids_list = type_ids_list_aux; + Ok(()) + } + + fn gen_proto_ids_section(&mut self) -> Result<()> { + debug!("Sort prototypes and generate proto_id_item section"); + let mut proto_ids_list: Vec = self.proto_ids.keys().cloned().collect(); + proto_ids_list.sort(); + for (idx, proto) in proto_ids_list.iter().enumerate() { + self.proto_ids + .entry(proto.clone()) + .and_modify(|val| *val = idx); + self.section_manager.add_elt(Section::ProtoIdItem, None); + } + let mut proto_ids_list_aux = vec![]; + for proto in proto_ids_list { + proto_ids_list_aux.push(ProtoIdItem { + shorty_idx: *self.strings.get(&proto.shorty).ok_or(anyhow!( + "String {}, (shorty of prototype {}) not found in dex builder", + proto.shorty.__repr__(), + proto.__repr__() + ))? as u32, + return_type_idx: *self.type_ids.get(&proto.return_type).ok_or(anyhow!( + "Type {}, (return type of prototype {}) not found in dex builder", + proto.shorty.__repr__(), + proto.__repr__() + ))? as u32, + parameters_off: 0, // Will be linked later + }); + } + self.proto_ids_list = proto_ids_list_aux; + Ok(()) + } + + fn gen_field_ids_section(&mut self) -> Result<()> { + debug!("Sort field ids and generate field_ids_item"); + let mut field_ids_list: Vec = self.field_ids.keys().cloned().collect(); + field_ids_list.sort(); + for (idx, field_id) in field_ids_list.iter().enumerate() { + self.field_ids + .entry(field_id.clone()) + .and_modify(|val| *val = idx); + self.section_manager.add_elt(Section::FieldIdItem, None); + } + let mut field_ids_list_aux = vec![]; + for field in field_ids_list.into_iter() { + field_ids_list_aux.push(FieldIdItem { + class_idx: *self.type_ids.get(&field.class_).ok_or(anyhow!( + "Type {} (class of field {}) not found in dex builder", + field.class_.__repr__(), + field.__repr__() + ))? as u16, + type_idx: *self.type_ids.get(&field.type_).ok_or(anyhow!( + "Type {} (type of field {}) not found in dex builder", + field.type_.__repr__(), + field.__repr__() + ))? as u16, + name_idx: *self.strings.get(&field.name).ok_or(anyhow!( + "String {} (name of field {}) not found in dex builder", + field.name.__repr__(), + field.__repr__() + ))? as u32, + }); + } + self.field_ids_list = field_ids_list_aux; + + Ok(()) + } + + fn gen_method_ids_section(&mut self) -> Result<()> { + debug!("Sort method ids and generate method_id_item section"); + let mut method_ids_list: Vec = self.method_ids.keys().cloned().collect(); + method_ids_list.sort(); + for (idx, method_id) in method_ids_list.iter().enumerate() { + self.method_ids + .entry(method_id.clone()) + .and_modify(|val| *val = idx); + self.section_manager.add_elt(Section::MethodIdItem, None); + } + let mut method_ids_list_aux = vec![]; + for method in method_ids_list.into_iter() { + method_ids_list_aux.push(MethodIdItem { + class_idx: *self.type_ids.get(&method.class_).ok_or(anyhow!( + "Type {} (class of method {}) not found in dex builder", + method.class_.__repr__(), + method.__repr__() + ))? as u16, + proto_idx: *self.proto_ids.get(&method.proto).ok_or(anyhow!( + "Prototype {} (signature of method {}) not found in dex builder", + method.proto.__repr__(), + method.__repr__() + ))? as u16, + name_idx: *self.strings.get(&method.name).ok_or(anyhow!( + "String {} (name of method {}) not found in dex builder", + method.name.__repr__(), + method.__repr__() + ))? as u32, + }); + } + self.method_ids_list = method_ids_list_aux; + Ok(()) + } + + /// Insert a code_item. + /// + /// # Warning + /// + /// 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 { + self.class_defs + .get(&method_id.class_) + .unwrap() + .0 + .direct_methods + .get(&method_id) + .unwrap() + .code + .as_ref() + .unwrap() + .clone() + } else { + self.class_defs + .get(&method_id.class_) + .unwrap() + .0 + .virtual_methods + .get(&method_id) + .unwrap() + .code + .as_ref() + .unwrap() + .clone() + }; + // Estimate instructions addresses + let mut min_addr = 0; + let mut max_addr = 0; + let mut label_min_max_addrs: HashMap = HashMap::new(); + + for ins in &code.insns { + match ins { + Instruction::Label(Label { name }) => { + label_min_max_addrs.insert(name.clone(), (min_addr, max_addr)); + min_addr += ins.min_ins_size() / 2; + max_addr += ins.max_ins_size() / 2; + } + Instruction::ConstString(ins) => { + let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( + "String {} (found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let size = ins.get_raw_ins(*string_idx).size() / 2; + min_addr += size; + max_addr += size; + } + _ => { + min_addr += ins.min_ins_size() / 2; + max_addr += ins.max_ins_size() / 2; + } + } + } + // Compute instruction size and precise addresses + let mut addr = 0; + let mut label_addrs = HashMap::new(); + let mut goto_sizes = vec![]; + for ins in &code.insns { + match ins { + Instruction::Label(Label { name }) => { + label_addrs.insert(name.clone(), addr); + addr += ins.max_ins_size() / 2; + } + Instruction::ConstString(ins) => { + let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( + "String {} (found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + addr += ins.get_raw_ins(*string_idx).size() / 2; + } + Instruction::Goto(Goto { label }) => { + let (min_addr, max_addr) = label_min_max_addrs + .get(label) + .ok_or(anyhow!("Label {label} not found in label estimation map"))?; + let size = Goto::size_from_branch_offset_interval(addr, *min_addr, *max_addr)?; + goto_sizes.push(size); + addr += size / 2; + } + _ => addr += ins.ins_size()? / 2, + } + } + // Serialize instructions + let mut tries = vec![]; + let mut handlers = EncodedCatchHandlerList { list: vec![] }; + let mut handler_off = 0; + let mut insns = vec![]; + let mut payloads = vec![]; + let mut goto_idx = 0; + let mut payload_addr = addr; + if payload_addr % 2 != 0 { + payload_addr += 1; // For switch and array table alignment + } + addr = 0; + for ins in &code.insns { + match ins { + Instruction::ConstString(ins) => { + let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( + "String {} (found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*string_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::ConstClass(ins) => { + let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( + "Class {} (type of class found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(class_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::CheckCast(ins) => { + let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( + "Class {} (type of class found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(class_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InstanceOf(ins) => { + let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( + "Class {} (type of class found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(class_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::NewInstance(ins) => { + let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( + "Class {} (type of class found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(class_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::NewArray(ins) => { + let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( + "Type {} (type found in code of {}) not found in dex builder", + ins.lit.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(class_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::FilledNewArray(ins) => { + let class_idx = *self.type_ids.get(&ins.type_).ok_or(anyhow!( + "Type {} (type found in code of {}) not found in dex builder", + ins.type_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(class_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::FillArrayData(ins) => { + let payload = InsFormat::FormatFillArrayDataPayload { + elt_width: ins.elt_width, + data: ins.data.clone(), + }; + if payload_addr % 2 != 0 { + // https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/method_verifier.cc;drc=e8c3e7be783937a340cd4f3280b69962d6f1ea0c;l=1347 + // The ART check if the array data table is 4 bytes aligned (= 2 ins alligned) + // TODO: check how it is donne in android and other dex generation code. + let nop = Instruction::Nop(Nop).get_raw_ins()?; + payload_addr += nop.size() / 2; + payloads.push(nop); + } + let data_offset = payload_addr as i32 - addr as i32; + payload_addr += payload.size() / 2; + payloads.push(payload); + let ins = ins.get_raw_ins(data_offset); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::Goto(ins) => { + let goto_size = goto_sizes[goto_idx]; + goto_idx += 1; + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found goto with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + let ins = ins.get_raw_ins(branch_offset, goto_size); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::Switch(ins) => { + let mut key_targets = vec![]; + for (key, label) in &ins.branches { + let label_addr = label_addrs.get(label).ok_or(anyhow!( + "Label {} not found in code of {}, but found goto with this label", + label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + key_targets.push((*key, branch_offset)); + } + key_targets.sort_by_key(|(key, _)| *key); + let payload = if ins.is_packed() { + let (first_key, _) = *key_targets.first().ok_or(anyhow!( + "Found empty swith in code of {}", + method_id.__repr__() + ))?; + let targets: Vec<_> = + key_targets.into_iter().map(|(_, target)| target).collect(); + InsFormat::FormatPackedSwitchPayload { first_key, targets } + } else { + InsFormat::FormatSparseSwitchPayload { key_targets } + }; + if payload_addr % 2 != 0 { + // https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/method_verifier.cc;drc=e8c3e7be783937a340cd4f3280b69962d6f1ea0c;l=1464 + // The ART check if the switch table is 4 bytes aligned (= 2 ins alligned) + // TODO: check how it is donne in android and other dex generation code. + let nop = Instruction::Nop(Nop).get_raw_ins()?; + payload_addr += nop.size() / 2; + payloads.push(nop); + } + let data_offset = payload_addr as i32 - addr as i32; + payload_addr += payload.size() / 2; + payloads.push(payload); + let ins = ins.get_raw_ins(data_offset); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfEq(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfNe(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfLt(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfGe(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfGt(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfLe(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfEqZ(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfNeZ(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfLtZ(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfGeZ(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfGtZ(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IfLeZ(ins) => { + let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( + "Label {} not found in code of {}, but found if with this label", + ins.label, + method_id.__repr__() + ))?; + let branch_offset = *label_addr as i32 - addr as i32; + if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { + bail!( + "Found an if that jump to far from the instruction in code of {}", + method_id.__repr__() + ); + } + let ins = ins.get_raw_ins(branch_offset as i16); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGet(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGetWide(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGetObject(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGetBoolean(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGetByte(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGetChar(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IGetShort(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPut(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPutWide(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPutObject(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPutBoolean(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPutByte(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPutChar(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::IPutShort(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGet(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGetWide(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGetObject(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGetBoolean(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGetByte(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGetChar(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SGetShort(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPut(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPutWide(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPutObject(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPutBoolean(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPutByte(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPutChar(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::SPutShort(ins) => { + let field = &ins.field; + let field_idx = self.field_ids.get(field).ok_or(anyhow!( + "Field {} (field of class {}, found in code of {}) not found in dex builder", + field.__repr__(), + field.class_.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*field_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokeVirtual(ins) => { + let meth = &ins.method; + let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( + "Method {} (method of class {}, found in code of {}) not found in dex builder", + meth.__repr__(), + meth.class_.__repr__(), + method_id.__repr__() + ))?; + debug_assert!( + *meth_idx <= u16::MAX as usize, + "methode id too big for invoke instruction" + ); + let meth_idx = *meth_idx as u16; + let ins = ins.get_raw_ins(meth_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokeSuper(ins) => { + let meth = &ins.method; + let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( + "Method {} (method of class {}, found in code of {}) not found in dex builder", + meth.__repr__(), + meth.class_.__repr__(), + method_id.__repr__() + ))?; + debug_assert!( + *meth_idx <= u16::MAX as usize, + "methode id too big for invoke instruction" + ); + let meth_idx = *meth_idx as u16; + let ins = ins.get_raw_ins(meth_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokeDirect(ins) => { + let meth = &ins.method; + let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( + "Method {} (method of class {}, found in code of {}) not found in dex builder", + meth.__repr__(), + meth.class_.__repr__(), + method_id.__repr__() + ))?; + debug_assert!( + *meth_idx <= u16::MAX as usize, + "methode id too big for invoke instruction" + ); + let meth_idx = *meth_idx as u16; + let ins = ins.get_raw_ins(meth_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokeStatic(ins) => { + let meth = &ins.method; + let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( + "Method {} (method of class {}, found in code of {}) not found in dex builder", + meth.__repr__(), + meth.class_.__repr__(), + method_id.__repr__() + ))?; + debug_assert!( + *meth_idx <= u16::MAX as usize, + "methode id too big for invoke instruction" + ); + let meth_idx = *meth_idx as u16; + let ins = ins.get_raw_ins(meth_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokeInterface(ins) => { + let meth = &ins.method; + let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( + "Method {} (method of class {}, found in code of {}) not found in dex builder", + meth.__repr__(), + meth.class_.__repr__(), + method_id.__repr__() + ))?; + debug_assert!( + *meth_idx <= u16::MAX as usize, + "methode id too big for invoke instruction" + ); + let meth_idx = *meth_idx as u16; + let ins = ins.get_raw_ins(meth_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokePolymorphic(ins) => { + let meth = &ins.method; + let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( + "Method {} (method of class {}, found in code of {}) not found in dex builder", + meth.__repr__(), + meth.class_.__repr__(), + method_id.__repr__() + ))?; + let proto_idx = self.proto_ids.get(&ins.proto).ok_or(anyhow!( + "Prototype {} (found in code of {}) not found in dex builder", + ins.proto.__repr__(), + method_id.__repr__() + ))?; + debug_assert!( + *meth_idx <= u16::MAX as usize, + "methode id too big for invoke instruction" + ); + debug_assert!( + *proto_idx <= u16::MAX as usize, + "proto id too big for invoke instruction" + ); + let meth_idx = *meth_idx as u16; + let proto_idx = *proto_idx as u16; + let ins = ins.get_raw_ins(meth_idx, proto_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::InvokeCustom(ins) => { + let call_site_idx = self.call_site_ids.len(); + self.insert_call_site_item(&ins.call_site)?; + let ins = ins.get_raw_ins(call_site_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::ConstMethodHandle(ins) => { + let method_handle_idx = self.method_handles.len(); + self.insert_method_handle(&ins.handle)?; + let ins = ins.get_raw_ins(method_handle_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::ConstMethodType(ins) => { + let proto_idx = self.proto_ids.get(&ins.proto).ok_or(anyhow!( + "Prototype {} (found in code of {}) not found in dex builder", + ins.proto.__repr__(), + method_id.__repr__() + ))?; + let ins = ins.get_raw_ins(*proto_idx); + addr += ins.size() / 2; + insns.push(ins); + } + Instruction::Try(try_) => { + let end_block_addr = *label_addrs.get(&try_.end_label).ok_or(anyhow!( + "Label {} not found in code of {}, but found try with this label", + &try_.end_label, + method_id.__repr__() + ))?; + if end_block_addr < addr { + bail!( + "Found end label of a try block before the try instruction in code of {}", + method_id.__repr__() + ) + } + let try_item = TryItem { + start_addr: addr as u32, + insn_count: (end_block_addr - addr) as u16, + handler_off: handler_off as u16, // will be ajusted once the size of the + // handler list object is known + }; + tries.push(try_item); + let mut catches = EncodedCatchHandler { + handlers: vec![], + catch_all_addr: None, + }; + for (ty, label) in &try_.handlers { + let type_idx = Uleb128(*self.type_ids.get(ty).ok_or(anyhow!( + "Could not found type {} captured by a try block in {}\ + in the dex builder", + ty.__repr__(), + method_id.__repr__() + ))? as u32); + let addr = Uleb128(*label_addrs.get(label).ok_or(anyhow!( + "Label {} not found in code of {}, but found try \ + with this label as catch for type {}", + &try_.end_label, + method_id.__repr__(), + ty.__repr__(), + ))? as u32); + catches + .handlers + .push(EncodedTypeAddrPair { type_idx, addr }); + } + if let Some(ref label) = try_.default_handler { + let catch_all_addr = *label_addrs.get(label).ok_or(anyhow!( + "Label {} not found in code of {}, but found try \ + with this label as catch all", + &try_.end_label, + method_id.__repr__() + ))?; + catches.catch_all_addr = Some(Uleb128(catch_all_addr as u32)); + } + handler_off += catches.size(); + handlers.list.push(catches); + } + Instruction::Label(_) => (), + _ => { + let ins = ins.get_raw_ins().with_context(|| { + format!( + "Failed to convert instruction {} (found in code of {}) to raw instruction", + ins.__repr__(), + method_id.__repr__() + ) + })?; + addr += ins.size() / 2; + insns.push(ins); + } + } + } + if addr % 2 != 0 { + // make sure the payload section is 4 bytes aligned + let nop = Instruction::Nop(Nop).get_raw_ins()?; + //addr += nop.size() / 2; + insns.push(nop); + } + insns.extend(payloads); + + for try_ in &mut tries { + try_.handler_off += handlers.size_field().size() as u16; + } + + let debug_info_off = if code.debug_info.1.is_empty() && code.parameter_names.is_none() { + 0 + } else { + let debug_info_off = self + .section_manager + .get_aligned_size(Section::DebugInfoItem); + let mut cursor = Cursor::new(code.debug_info.1); + let mut item = DebugInfoItem { + line_start: Uleb128(code.debug_info.0), + parameter_names: vec![], + bytecode: Vec::::deserialize(&mut cursor, DbgBytecode::EndSequence)?, + }; + if let Some(parameter_names) = code.parameter_names { + for name in ¶meter_names { + if let Some(name) = name { + item.parameter_names + .push(Uleb128p1(*self.strings.get(name).ok_or(anyhow!( + "String {} (name of param of {}) not found", + name.__str__(), + method_id.__repr__() + ))? as u32)); + } else { + item.parameter_names.push(NO_INDEX); + } + } + } + self.section_manager + .add_elt(Section::DebugInfoItem, Some(item.size())); + self.debug_info_items.push(item); + debug_info_off + 1 + }; + let handlers = if handlers.list.is_empty() { + None + } else { + Some(handlers) + }; + 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() + insns, + tries, + handlers, + }; + self.section_manager + .add_elt(Section::CodeItem, Some(item.size())); + self.code_items.push(item); + Ok(()) + } + + /// Insert annotation associated to a class. + /// + + /// Insert a class_data_item in the class_data section (in data). + /// + /// # Note + /// + /// code_item objects are 4 bytes aligns, so their offset cannot be odd. + /// + /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset + /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow + /// to distinguish the offset of the first item (equal to zero before linking) and the value + /// 0 used to indicate an abscence of item. + fn insert_class_data_item(&mut self, class_id: &IdType) -> Result<()> { + let mut data = ClassDataItem::default(); + let (class, _) = self.class_defs.get(class_id).unwrap(); + + let mut static_fields: Vec = class.static_fields.keys().cloned().collect(); + static_fields.sort(); + let mut last_field_id = 0; + for id in &static_fields { + let idx = self.field_ids.get(id).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex builder", + id.__repr__(), + class.__repr__() + ))?; + let field_idx_diff = Uleb128((idx - last_field_id) as u32); + last_field_id = *idx; + let access_flags = Uleb128(class.static_fields.get(id).unwrap().get_raw_access_flags()); + data.static_fields.push(EncodedField { + field_idx_diff, + access_flags, + }); + } + + let mut instance_fields: Vec = class.instance_fields.keys().cloned().collect(); + instance_fields.sort(); + let mut last_field_id = 0; + for id in &instance_fields { + let idx = self.field_ids.get(id).ok_or(anyhow!( + "Field {} (field of class {}) not found in dex builder", + id.__repr__(), + class.__repr__() + ))?; + let field_idx_diff = Uleb128((idx - last_field_id) as u32); + last_field_id = *idx; + let access_flags = Uleb128( + class + .instance_fields + .get(id) + .unwrap() + .get_raw_access_flags(), + ); + data.instance_fields.push(EncodedField { + field_idx_diff, + access_flags, + }); + } + + let mut direct_methods: Vec = class.direct_methods.keys().cloned().collect(); + direct_methods.sort(); + let mut last_method_id = 0; + for id in &direct_methods { + // &mut vs & of self and class make things difficult... + let (class, _) = self.class_defs.get(class_id).unwrap(); + let idx = self.method_ids.get(id).ok_or(anyhow!( + "Method {} (method of class {}) not found in dex builder", + id.__repr__(), + class.__repr__() + ))?; + let method_idx_diff = Uleb128((idx - last_method_id) as u32); + last_method_id = *idx; + let access_flags = + Uleb128(class.direct_methods.get(id).unwrap().get_raw_access_flags()); + // No if let because ownership gunfooterie + let code_off = if class.direct_methods.get(id).unwrap().code.is_some() { + let code_off = self.section_manager.get_aligned_size(Section::CodeItem); + self.insert_code_item(id.clone(), true)?; + Uleb128(code_off + 1) + } else { + Uleb128(0) + }; + data.direct_methods.push(EncodedMethod { + method_idx_diff, + access_flags, + code_off, // Will be relinked once the offset of the code item section is known + }); + } + + let (class, _) = self.class_defs.get(class_id).unwrap(); + let mut virtual_methods: Vec = class.virtual_methods.keys().cloned().collect(); + virtual_methods.sort(); + let mut last_method_id = 0; + for id in &virtual_methods { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let idx = self.method_ids.get(id).ok_or(anyhow!( + "Method {} (method of class {}) not found in dex builder", + id.__repr__(), + class.__repr__() + ))?; + let method_idx_diff = Uleb128((idx - last_method_id) as u32); + last_method_id = *idx; + let access_flags = Uleb128( + class + .virtual_methods + .get(id) + .unwrap() + .get_raw_access_flags(), + ); + // No if let because ownership gunfooterie + let code_off = if class.virtual_methods.get(id).unwrap().code.is_some() { + let code_off = self.section_manager.get_aligned_size(Section::CodeItem); + self.insert_code_item(id.clone(), false)?; + Uleb128(code_off + 1) + } else { + Uleb128(0) + }; + data.virtual_methods.push(EncodedMethod { + method_idx_diff, + access_flags, + code_off, // Will be relinked once the offset of the code item section is known + }); + } + self.section_manager + .add_elt(Section::ClassDataItem, Some(data.size())); + //assert_eq!(data.size(), data.serialize_to_vec().unwrap().len()); + self.class_data_list.push(data); + Ok(()) + } + + /// Insert a [`MethodHandle`]. + pub fn insert_method_handle(&mut self, handle: &MethodHandle) -> Result<()> { + let (field_or_method_id, method_handle_type) = match handle { + MethodHandle::StaticPut(StaticPut(field)) => ( + *self.field_ids.get(field).ok_or(anyhow!( + "Field {} not found in dex writer", + field.__repr__() + ))? as u16, + MethodHandleType::StaticPut, + ), + MethodHandle::StaticGet(StaticGet(field)) => ( + *self.field_ids.get(field).ok_or(anyhow!( + "Field {} not found in dex writer", + field.__repr__() + ))? as u16, + MethodHandleType::StaticGet, + ), + MethodHandle::InstancePut(InstancePut(field)) => ( + *self.field_ids.get(field).ok_or(anyhow!( + "Field {} not found in dex writer", + field.__repr__() + ))? as u16, + MethodHandleType::InstancePut, + ), + MethodHandle::InstanceGet(InstanceGet(field)) => ( + *self.field_ids.get(field).ok_or(anyhow!( + "Field {} not found in dex writer", + field.__repr__() + ))? as u16, + MethodHandleType::InstanceGet, + ), + MethodHandle::InvokeStatic(InvokeStatic(meth)) => ( + *self.method_ids.get(meth).ok_or(anyhow!( + "Method {} not found in dex writer", + meth.__repr__() + ))? as u16, + MethodHandleType::InvokeStatic, + ), + MethodHandle::InvokeInstance(InvokeInstance(meth)) => ( + *self.method_ids.get(meth).ok_or(anyhow!( + "Method {} not found in dex writer", + meth.__repr__() + ))? as u16, + MethodHandleType::InvokeInstance, + ), + MethodHandle::InvokeConstructor(InvokeConstructor(meth)) => ( + *self.method_ids.get(meth).ok_or(anyhow!( + "Method {} not found in dex writer", + meth.__repr__() + ))? as u16, + MethodHandleType::InvokeConstructor, + ), + MethodHandle::InvokeDirect(InvokeDirect(meth)) => ( + *self.method_ids.get(meth).ok_or(anyhow!( + "Method {} not found in dex writer", + meth.__repr__() + ))? as u16, + MethodHandleType::InvokeDirect, + ), + MethodHandle::InvokeInterface(InvokeInterface(meth)) => ( + *self.method_ids.get(meth).ok_or(anyhow!( + "Method {} not found in dex writer", + meth.__repr__() + ))? as u16, + MethodHandleType::InvokeInterface, + ), + }; + self.method_handles.push(MethodHandleItem { + method_handle_type, + field_or_method_id, + unused1: 0, + unused2: 0, + }); + Ok(()) + } + + /// Convert a [`DexValue`] to an [`EncodedValue`]. + /// + /// # Warning + /// + /// This method can insert element in the dex file like method_handles. + pub fn dex_value_to_encoded_value(&mut self, value: &DexValue) -> Result { + match value { + DexValue::Byte(DexByte(val)) => Ok(EncodedValue::Byte(*val)), + DexValue::Short(DexShort(val)) => Ok(EncodedValue::Short(*val)), + DexValue::Char(DexChar(val)) => Ok(EncodedValue::Char(*val)), + DexValue::Int(DexInt(val)) => Ok(EncodedValue::Int(*val)), + DexValue::Long(DexLong(val)) => Ok(EncodedValue::Long(*val)), + DexValue::Float(DexFloat(val)) => Ok(EncodedValue::Float(*val)), + DexValue::Double(DexDouble(val)) => Ok(EncodedValue::Double(*val)), + DexValue::MethodType(val) => Ok(EncodedValue::MethodType( + *self.proto_ids.get(val).ok_or(anyhow!( + "Prototype {} not found in dex writer", + val.__repr__() + ))? as u32, + )), + DexValue::MethodHandle(val) => { + // TODO: move to a method + let idx = self.method_handles.len() as u32; + self.insert_method_handle(val)?; + Ok(EncodedValue::MethodHandle(idx)) + } + DexValue::String(val) => Ok(EncodedValue::String( + *self + .strings + .get(val) + .ok_or(anyhow!("String {} not found in dex writer", val.__repr__()))? + as u32, + )), + DexValue::Type(val) => Ok(EncodedValue::Type( + *self + .type_ids + .get(val) + .ok_or(anyhow!("Type {} not found in dex writer", val.__repr__()))? + as u32, + )), + DexValue::Field(val) => Ok(EncodedValue::Field( + *self + .field_ids + .get(val) + .ok_or(anyhow!("Field {} not found in dex writer", val.__repr__()))? + as u32, + )), + DexValue::Method(val) => Ok(EncodedValue::Method( + *self + .method_ids + .get(val) + .ok_or(anyhow!("Method {} not found in dex writer", val.__repr__()))? + as u32, + )), + DexValue::Enum(IdEnum(val)) => Ok(EncodedValue::Enum( + *self + .field_ids + .get(val) + .ok_or(anyhow!("Field {} not found in dex writer", val.__repr__()))? + as u32, + )), + DexValue::Array(DexArray(arr)) => { + let mut values = vec![]; + for val in arr { + values.push( + self.dex_value_to_encoded_value(val) + .context("Error while serializing a array")?, + ); + } + Ok(EncodedValue::Array(EncodedArray { values })) + } + DexValue::Annotation(val) => Ok(EncodedValue::Annotation( + self.dex_annotation_to_encoded_annotation(val.clone())?, + )), + DexValue::Null(DexNull) => Ok(EncodedValue::Null), + DexValue::Boolean(DexBoolean(val)) => Ok(EncodedValue::Boolean(*val)), + } + } + + /// Insert an encoded_array in the encoded_array_item section. + fn insert_encoded_array_item(&mut self, DexArray(array): DexArray) -> Result<()> { + let mut values = vec![]; + for value in array { + values.push(self.dex_value_to_encoded_value(&value)?); + } + let item = EncodedArrayItem { + value: EncodedArray { values }, + }; + self.section_manager + .add_elt(Section::EncodedArrayItem, Some(item.size())); + self.encoded_array_items.push(item); + Ok(()) + } + + /// Insert a [`CallSite`] to the encoded array items + /// + /// # Warning + /// + /// This method can insert element in the dex file like method_handles. + pub fn insert_call_site_item(&mut self, call_site: &CallSite) -> Result<()> { + let mut values = vec![]; + values.push(DexValue::MethodHandle(call_site.method_handle.clone())); + values.push(DexValue::String(call_site.name.clone())); + values.push(DexValue::MethodType(call_site.type_.clone())); + values.extend(call_site.args.iter().cloned()); + self.call_site_ids.push(CallSiteIdItem { + call_site_off: self + .section_manager + .get_aligned_size(Section::EncodedArrayItem), + }); // linked in link_call_site_ids() + self.section_manager.add_elt(Section::CallSiteIdItem, None); + self.insert_encoded_array_item(DexArray(values)) + } + + /// Insert the encoded_array_item encoding the static_values of a class. + fn insert_class_static_values(&mut self, class_id: &IdType) -> Result<()> { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let mut static_fields: Vec = class.static_fields.keys().cloned().collect(); + static_fields.sort(); + let mut array = vec![]; + let mut last_defined_field_index = 0; + for (idx, f) in static_fields.iter().enumerate() { + if class.static_fields.get(f).unwrap().value.is_some() { + last_defined_field_index = idx; + } + } + for f in &static_fields[..=last_defined_field_index] { + let field = class.static_fields.get(f).unwrap(); + if let Some(val) = field.value.as_ref() { + array.push(val.clone()); + } else { + array.push(field.descriptor.type_.get_default_value().ok_or(anyhow!( + "The type {} (for field {} in class {}) does not have a default value", + field.descriptor.type_.__repr__(), + field.descriptor.__repr__(), + class_id.__repr__() + ))?); + } + } + self.insert_encoded_array_item(DexArray(array)) + .with_context(|| { + format!( + "Failed to serialize static values of class {}", + class_id.__repr__() + ) + }) + } + + fn dex_annotation_to_encoded_annotation( + &mut self, + DexAnnotation { type_, elements }: DexAnnotation, + ) -> Result { + let mut encoded_elements = vec![]; + + let mut elements_names: Vec<_> = elements.keys().collect(); + elements_names.sort(); + + for name in elements_names { + let elt = elements.get(name).unwrap(); + encoded_elements.push(AnnotationElement { + name_idx: Uleb128(*self.strings.get(name).ok_or(anyhow!( + "{} (annotation element name) not found in dex builder", + name.__str__() + ))? as u32), + value: self.dex_value_to_encoded_value(elt)?, + }); + } + Ok(EncodedAnnotation { + type_idx: Uleb128(*self.type_ids.get(&type_).ok_or(anyhow!( + "Annotation type {} not found in dex builder", + type_.__repr__(), + ))? as u32), + elements: encoded_elements, + }) + } + + /// Insert the annnotations set for a class. + fn insert_class_annotation_set(&mut self, class_id: &IdType) -> Result<()> { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let mut annotations = class.annotations.clone(); + + let mut annotation_set = AnnotationSetItem { entries: vec![] }; + annotations.sort_by_key(|annot| annot.annotation.type_.clone()); + for annot in annotations { + annotation_set.entries.push(AnnotationOffItem { + annotation_off: self + .section_manager + .get_aligned_size(Section::AnnotationItem), + }); // linked in link_annotations() + + let item = AnnotationItem { + visibility: match ( + annot.visibility_build, + annot.visibility_runtime, + annot.visibility_system, + ) { + (true, false, false) => AnnotationVisibility::Build, + (false, true, false) => AnnotationVisibility::Runtime, + (false, false, true) => AnnotationVisibility::System, + _ => bail!( + "Annotation need visibility set to one and only one of build, \ + runtime or system" + ), // TODO: check if this is true + }, + annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, + }; + self.section_manager + .add_elt(Section::AnnotationItem, Some(item.size())); + self.annotation_items.push(item); + } + self.section_manager + .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); + self.annotation_set_items.push(annotation_set); + Ok(()) + } + + /// Insert the annnotations set for a class. + fn insert_field_annotation_set( + &mut self, + field_id: &IdField, + is_static_field: bool, + ) -> Result<()> { + let (class, _) = self.class_defs.get(&field_id.class_).unwrap(); + let field = if is_static_field { + class.static_fields.get(field_id).unwrap() + } else { + class.instance_fields.get(field_id).unwrap() + }; + let mut annotations = field.annotations.clone(); + + let mut annotation_set = AnnotationSetItem { entries: vec![] }; + annotations.sort_by_key(|annot| annot.annotation.type_.clone()); + for annot in annotations { + annotation_set.entries.push(AnnotationOffItem { + annotation_off: self + .section_manager + .get_aligned_size(Section::AnnotationItem), + }); // linked in link_annotations() + + let item = AnnotationItem { + visibility: match ( + annot.visibility_build, + annot.visibility_runtime, + annot.visibility_system, + ) { + (true, false, false) => AnnotationVisibility::Build, + (false, true, false) => AnnotationVisibility::Runtime, + (false, false, true) => AnnotationVisibility::System, + _ => bail!( + "Annotation need visibility set to one and only one of build, \ + runtime or system" + ), // TODO: check if this is true + }, + annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, + }; + self.section_manager + .add_elt(Section::AnnotationItem, Some(item.size())); + self.annotation_items.push(item); + } + self.section_manager + .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); + self.annotation_set_items.push(annotation_set); + Ok(()) + } + + /// Insert the annnotations set for a method (but not the parameters annotations). + fn insert_method_annotation_set( + &mut self, + method_id: &IdMethod, + is_direct_method: bool, + ) -> Result<()> { + let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); + let method = if is_direct_method { + class.direct_methods.get(method_id).unwrap() + } else { + class.virtual_methods.get(method_id).unwrap() + }; + let mut annotations = method.annotations.clone(); + + let mut annotation_set = AnnotationSetItem { entries: vec![] }; + annotations.sort_by_key(|annot| annot.annotation.type_.clone()); + for annot in annotations { + annotation_set.entries.push(AnnotationOffItem { + annotation_off: self + .section_manager + .get_aligned_size(Section::AnnotationItem), + }); // linked in link_annotations() + + let item = AnnotationItem { + visibility: match ( + annot.visibility_build, + annot.visibility_runtime, + annot.visibility_system, + ) { + (true, false, false) => AnnotationVisibility::Build, + (false, true, false) => AnnotationVisibility::Runtime, + (false, false, true) => AnnotationVisibility::System, + _ => bail!( + "Annotation need visibility set to one and only one of build, \ + runtime or system" + ), // TODO: check if this is true + }, + annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, + }; + self.section_manager + .add_elt(Section::AnnotationItem, Some(item.size())); + self.annotation_items.push(item); + } + self.section_manager + .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); + self.annotation_set_items.push(annotation_set); + Ok(()) + } + + /// Insert the annotations set for a method parameter. + fn insert_parameters_annotation_set( + &mut self, + method_id: &IdMethod, + is_direct_method: bool, + parameter_idx: usize, + ) -> Result<()> { + let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); + let method = if is_direct_method { + class.direct_methods.get(method_id).unwrap() + } else { + class.virtual_methods.get(method_id).unwrap() + }; + let mut annotations = method.parameters_annotations[parameter_idx].clone(); + + let mut annotation_set = AnnotationSetItem { entries: vec![] }; + annotations.sort_by_key(|annot| annot.annotation.type_.clone()); + for annot in annotations { + annotation_set.entries.push(AnnotationOffItem { + annotation_off: self + .section_manager + .get_aligned_size(Section::AnnotationItem), + }); // linked in link_annotations() + + let item = AnnotationItem { + visibility: match ( + annot.visibility_build, + annot.visibility_runtime, + annot.visibility_system, + ) { + (true, false, false) => AnnotationVisibility::Build, + (false, true, false) => AnnotationVisibility::Runtime, + (false, false, true) => AnnotationVisibility::System, + _ => bail!( + "Annotation need visibility set to one and only one of build, \ + runtime or system" + ), // TODO: check if this is true + }, + annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, + }; + self.section_manager + .add_elt(Section::AnnotationItem, Some(item.size())); + self.annotation_items.push(item); + } + self.section_manager + .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); + self.annotation_set_items.push(annotation_set); + Ok(()) + } + + /// Insert the annotations set list for a method parameters. + fn insert_parameters_annotation_set_list( + &mut self, + method_id: &IdMethod, + is_direct_method: bool, + ) -> Result<()> { + let mut list = AnnotationSetRefList { list: vec![] }; + let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); + let method = if is_direct_method { + class.direct_methods.get(method_id).unwrap() + } else { + class.virtual_methods.get(method_id).unwrap() + }; + let param_has_annotation: Vec<_> = method + .parameters_annotations + .iter() + .map(|annots| !annots.is_empty()) + .collect(); + for (param_idx, has_annotation) in param_has_annotation.into_iter().enumerate() { + list.list.push(AnnotationSetRefItem { + annotations_off: if has_annotation { + let annotation_off = self + .section_manager + .get_aligned_size(Section::AnnotationSetItem); + self.insert_parameters_annotation_set(method_id, is_direct_method, param_idx)?; + annotation_off + 1 + } else { + 0 + }, // linked in link_annotations() + }); + } + + self.section_manager + .add_elt(Section::AnnotationSetRefList, Some(list.size())); + self.annotation_set_lists.push(list); + Ok(()) + } + + /// Insert a class annotations (including field, methods and parameters annotations). + /// + /// # Note + /// + /// annotation_set_item objects are 4 bytes aligns, so their offset cannot be odd. + /// + /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset + /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow + /// to distinguish the offset of the first item (equal to zero before linking) and the value + /// 0 used to indicate an abscence of item. + fn insert_annotations(&mut self, class_id: &IdType) -> Result<()> { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let class_annotations_off = if !class.annotations.is_empty() { + let class_annotations_off = self + .section_manager + .get_aligned_size(Section::AnnotationSetItem); + self.insert_class_annotation_set(class_id) + .with_context(|| { + format!( + "Failed to insert class annotation for class {}", + class_id.__repr__() + ) + })?; + class_annotations_off + 1 + } else { + 0 + }; + + let mut field_ids = vec![]; + let (class, _) = self.class_defs.get(class_id).unwrap(); + field_ids.extend(class.static_fields.keys().cloned()); + field_ids.extend(class.instance_fields.keys().cloned()); + field_ids.sort(); + let mut field_annotations = vec![]; + for field_id in field_ids { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let static_field = class.static_fields.get(&field_id); + let instance_field = class.instance_fields.get(&field_id); + let (is_static, field) = match (static_field, instance_field) { + (Some(field), None) => (true, field), + (None, Some(field)) => (false, field), + _ => bail!( + "Unexpected configuration: field {} is both a static and a instance field in {}", + field_id.__repr__(), class_id.__repr__()), + }; + if !field.annotations.is_empty() { + let annotations_off = self + .section_manager + .get_aligned_size(Section::AnnotationSetItem) + + 1; + self.insert_field_annotation_set(&field_id, is_static)?; + field_annotations.push(FieldAnnotation { + field_idx: *self.field_ids.get(&field_id).ok_or(anyhow!( + "Field {} in {} not found in dex builder", + field_id.__repr__(), + class_id.__repr__(), + ))? as u32, + annotations_off, // linked in link_annotations() + }); + } + } + + let mut method_ids = vec![]; + let (class, _) = self.class_defs.get(class_id).unwrap(); + method_ids.extend(class.direct_methods.keys().cloned()); + method_ids.extend(class.virtual_methods.keys().cloned()); + method_ids.sort(); + let mut method_annotations = vec![]; + for method_id in &method_ids { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let direct_method = class.direct_methods.get(method_id); + let virtual_method = class.virtual_methods.get(method_id); + let (is_direct, method) = match (direct_method, virtual_method) { + (Some(method), None) => (true, method), + (None, Some(method)) => (false, method), + _ => bail!( + "Unexpected configuration: method {} is both a direct and a virtual method in {}", + method_id.__repr__(), class_id.__repr__()), + }; + if !method.annotations.is_empty() { + let annotations_off = self + .section_manager + .get_aligned_size(Section::AnnotationSetItem) + + 1; + self.insert_method_annotation_set(method_id, is_direct)?; + method_annotations.push(MethodAnnotation { + method_idx: *self.method_ids.get(method_id).ok_or(anyhow!( + "Method {} in {} not found in dex builder", + method_id.__repr__(), + class_id.__repr__(), + ))? as u32, + annotations_off, // linked in link_annotations() + }); + } + } + + let mut parameter_annotations = vec![]; + for method_id in method_ids { + let (class, _) = self.class_defs.get(class_id).unwrap(); + let direct_method = class.direct_methods.get(&method_id); + let virtual_method = class.virtual_methods.get(&method_id); + let (is_direct, method) = match (direct_method, virtual_method) { + (Some(method), None) => (true, method), + (None, Some(method)) => (false, method), + _ => bail!( + "Unexpected configuration: method {} is both a direct and a virtual method in {}", + method_id.__repr__(), class_id.__repr__()), + }; + if !method.parameters_annotations.is_empty() { + let annotations_off = self + .section_manager + .get_aligned_size(Section::AnnotationSetRefList) + + 1; + self.insert_parameters_annotation_set_list(&method_id, is_direct)?; + parameter_annotations.push(ParameterAnnotation { + method_idx: *self.method_ids.get(&method_id).ok_or(anyhow!( + "Method {} in {} not found in dex builder", + method_id.__repr__(), + class_id.__repr__(), + ))? as u32, + annotations_off, // linked in link_annotations() + }); + } + } + + let item = AnnotationsDirectoryItem { + class_annotations_off, // linked in link_annotations() + field_annotations, + method_annotations, + parameter_annotations, + }; + self.section_manager + .add_elt(Section::AnnotationsDirectoryItem, Some(item.size())); + self.annotations_directory_items.push(item); + Ok(()) + } + + /// Insert a class_def_item in the class_defs section **and** the other struct that needs to be + /// generated on the fly. + /// + /// # Warning + /// + /// The class_defs section **MUST** be sorted by inheritance dependencies (parents classes and + /// interfaces must appear **before** child classes). Accordingly, this method must be invoked + /// in the right order. + /// + /// # Note + /// + /// annotations_directory_item, encoded_array_item and class_data_item objects are 4 bytes + /// aligns, so their offset cannot be odd. + /// + /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset + /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow + /// to distinguish the offset of the first item (equal to zero before linking) and the value + /// 0 used to indicate an abscence of item. + fn insert_class_def_item(&mut self, class_id: &IdType) -> Result<()> { + let idx = self.class_defs_list.len(); + self.class_defs + .entry(class_id.clone()) + .and_modify(|(_, i)| *i = idx); + let (class, _) = self.class_defs.get(class_id).unwrap(); + let class_data_off = if class.has_data_item() { + let class_data_off = self + .section_manager + .get_aligned_size(Section::ClassDataItem); + self.insert_class_data_item(class_id)?; + class_data_off + 1 + } else { + 0 + }; + // & vs &mut cluster-f, this make rust drop the ref so self hold by `class` before + // mutating self with `insert_class_data_item`, and get a new ref afterward + let (class, _) = self.class_defs.get(class_id).unwrap(); + let static_values_off = if class.has_static_values_array() { + let static_values_off = self + .section_manager + .get_aligned_size(Section::EncodedArrayItem); + self.insert_class_static_values(class_id)?; + static_values_off + 1 + } else { + 0 + }; + let (class, _) = self.class_defs.get(class_id).unwrap(); + let annotations_off = if class.has_annotations() { + let annotations_off = self + .section_manager + .get_aligned_size(Section::AnnotationsDirectoryItem); + self.insert_annotations(class_id)?; + annotations_off + 1 + } else { + 0 + }; + let (class, _) = self.class_defs.get(class_id).unwrap(); + self.class_defs_list.push(ClassDefItem { + class_idx: *self.type_ids.get(class_id).ok_or(anyhow!( + "Type {} (type of class {}) not found in dex builder", + class_id.__repr__(), + class.__repr__() + ))? as u32, + access_flags: class.get_raw_access_flags(), + superclass_idx: if let Some(sup) = &class.superclass { + *self.type_ids.get(sup).ok_or(anyhow!( + "Type {} (superclass of class {}) not found in dex builder", + sup.__repr__(), + class.__repr__() + ))? as u32 + } else { + NO_INDEX.0 + }, + interfaces_off: 0, + source_file_idx: if let Some(file) = &class.source_file { + *self.strings.get(file).ok_or(anyhow!( + "String {} (source file of class {}) not found in dex builder", + file.__repr__(), + class.__repr__() + ))? as u32 + } else { + NO_INDEX.0 + }, + + annotations_off, // need relinking once offset of class_def_item section is known + class_data_off, // need relinking once offset of class_data section is known + static_values_off, // need relinking once offset of encoded_array section is known + }); + self.section_manager.add_elt(Section::ClassDefItem, None); + Ok(()) + } + + fn gen_type_list_section(&mut self) -> Result<()> { + debug!("Generate the type_list section"); + // Collect all type lists + for proto in self.proto_ids.keys() { + if !proto.parameters.is_empty() { + let type_list = self.gen_type_list(&proto.parameters).with_context(|| { + format!("Failed to generate param list for {}", proto.__repr__()) + })?; + self.type_lists_index.insert(type_list, 0); + } + } + for (class, _) in self.class_defs.values() { + if !class.interfaces.is_empty() { + let type_list = self.gen_type_list(&class.interfaces).with_context(|| { + format!("Failed to generate interface list for {}", class.__repr__()) + })?; + self.type_lists_index.insert(type_list, 0); + } + } + + // safe type lists with their offset in the section + let mut offset = 0; + for (i, (list, idx)) in self.type_lists_index.iter_mut().enumerate() { + while offset % 4 != 0 { + // Alignment + self.section_manager.incr_section_size(Section::TypeList, 1); + offset += 1; + } + *idx = i; + self.type_lists_with_offset.push((list.clone(), offset)); + self.section_manager + .add_elt(Section::TypeList, Some(list.size())); + offset += list.size() as u32; + } + // The next section requires alignment to 4 + while offset % 4 != 0 { + // Alignment + self.section_manager.incr_section_size(Section::TypeList, 1); + offset += 1; + } + Ok(()) + } + + /// Generate the map list. + /// + /// # Warning + /// + /// All sections must be generated (but not linked) before generating the map list. + /// + /// This method switch the section manager from edit mode to read only. + fn gen_map_list(&mut self) -> Result<()> { + debug!("Generate the map_list"); + // Get the size of a map item + let map_item_size = 12; /* = MapItem { + type_: MapItemType::HeaderItem, + unused: 0, + size: 0, + offset: 0, + } + .size(); */ + // Empty map has a size 4, then we add the size of a MapItem for each element + // The size of the map_list must be computed before generating the map list, + // as it affect the offset of some sections. + self.section_manager.add_elt(Section::MapList, Some(4)); + for section in Section::VARIANT_LIST { + if !section.is_data() && self.section_manager.get_nb_elt(*section) != 0 { + self.section_manager + .incr_section_size(Section::MapList, map_item_size); + } + } + // All sections are knowns and should not be eddited anymore + self.section_manager.finalize_sections(); + for section in Section::VARIANT_LIST { + if !section.is_data() && self.section_manager.get_nb_elt(*section) != 0 { + /* + match section { + // Alignment + // Until Section::MapList included, the section are naturally alligned to 4 + _ => (), + } + */ + self.map_list.list.push(MapItem { + type_: section.get_map_item_type(), + unused: 0, + size: self.section_manager.get_nb_elt(*section) as u32, + offset: self.section_manager.get_offset(*section), + }); + } + } + Ok(()) + } + + /// Link the offsets in the header. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. + fn link_header(&mut self) { + debug!("Link the header section"); + self.header.map_off = self.section_manager.get_offset(Section::MapList); + self.header.string_ids_size = self.section_manager.get_nb_elt(Section::StringIdItem) as u32; + self.header.string_ids_off = self.section_manager.get_offset(Section::StringIdItem); + self.header.type_ids_size = self.section_manager.get_nb_elt(Section::TypeIdItem) as u32; + self.header.type_ids_off = self.section_manager.get_offset(Section::TypeIdItem); + self.header.proto_ids_size = self.section_manager.get_nb_elt(Section::ProtoIdItem) as u32; + self.header.proto_ids_off = self.section_manager.get_offset(Section::ProtoIdItem); + self.header.field_ids_size = self.section_manager.get_nb_elt(Section::FieldIdItem) as u32; + self.header.field_ids_off = self.section_manager.get_offset(Section::FieldIdItem); + self.header.method_ids_size = self.section_manager.get_nb_elt(Section::MethodIdItem) as u32; + self.header.method_ids_off = self.section_manager.get_offset(Section::MethodIdItem); + self.header.class_defs_size = self.section_manager.get_nb_elt(Section::ClassDefItem) as u32; + self.header.class_defs_off = self.section_manager.get_offset(Section::ClassDefItem); + self.header.data_size = self.section_manager.get_unaligned_size(Section::Data); + self.header.data_off = self.section_manager.get_offset(Section::Data); + } + + /// Link the offsets in the call site id items. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. + fn link_call_site_ids(&mut self) { + debug!("Link call site id items"); + for id in &mut self.call_site_ids { + id.call_site_off += self.section_manager.get_offset(Section::EncodedArrayItem); + } + } + + /// Link the offsets in class_def_items. + /// + /// # Warning + /// + /// This is the only link method called before generating the map list and finilizing the + /// section: + /// + /// Linking can only occur once all sections are entirelly generated, however, + /// `class_data_item.direct|virtual_methods[.].code_off` are Uleb128 encoded, meaning + /// that linking class_data_item modify the size of the class_data_items, hence the position + /// of the class_data_item and all element located after, as well as the size of the data + /// section. This is pretty bothersome and means that the sections **are** modified. + fn link_class_data(&mut self) -> Result<()> { + debug!("Link class data items"); + let mut unlinked_local_offset = 0; + let mut linked_local_offset = 0; + let code_section_off = self.section_manager.get_code_item_offset_prefinalized(); + for data in self.class_data_list.iter_mut() { + let unlinked_size = data.size() as u32; + for method in &mut data.direct_methods { + if method.code_off.0 != 0 { + method.code_off.0 += code_section_off - 1; + } + } + for method in &mut data.virtual_methods { + if method.code_off.0 != 0 { + method.code_off.0 += code_section_off - 1; + } + } + self.corrected_class_data_offset + .insert(unlinked_local_offset, linked_local_offset); + linked_local_offset += data.size() as u32; + unlinked_local_offset += unlinked_size; + } + self.section_manager.incr_section_size( + Section::ClassDataItem, + linked_local_offset as usize - unlinked_local_offset as usize, + ); + Ok(()) + } + + /// Link the offsets in proto_id_items. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. + fn link_proto_id(&mut self) -> Result<()> { + debug!("Link proto id items"); + for (proto, idx) in &self.proto_ids { + if !proto.parameters.is_empty() { + let type_list = self.gen_type_list(&proto.parameters).with_context(|| { + format!("Failed to generate param list for {}", proto.__repr__()) + })?; + let offset = self.section_manager.get_offset(Section::TypeList) + + self.type_lists_with_offset[*self.type_lists_index.get(&type_list).unwrap()] + .1; + self.proto_ids_list[*idx].parameters_off = offset; + } + } + Ok(()) + } + + /// Link the offsets of class_data_items in class_def_items. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. + fn link_class_def(&mut self) -> Result<()> { + debug!("Link class_def_items"); + for class_def in self.class_defs_list.iter_mut() { + // Link the class_data_item entries + // prelink value is set to offset in the section + 1 (to distinguish with 0) + if class_def.class_data_off != 0 { + let unlinked_local_offset = class_def.class_data_off - 1; + let linked_local_offset = *self + .corrected_class_data_offset + .get(&unlinked_local_offset) + .expect( + "Unlinked class_data_item offset not found in corrected_class_data_offset", + ); + class_def.class_data_off = + self.section_manager.get_offset(Section::ClassDataItem) + linked_local_offset; + } + // Link the annotations_directory_item entrie + // prelink value is set to offset in the section + 1 (to distinguish with 0) + if class_def.annotations_off != 0 { + class_def.annotations_off += self + .section_manager + .get_offset(Section::AnnotationsDirectoryItem) + - 1; + } + + // Link the static_values entries + if class_def.static_values_off != 0 { + class_def.static_values_off += + self.section_manager.get_offset(Section::EncodedArrayItem) - 1; + } + } + for (cls, idx) in self.class_defs.values() { + if !cls.interfaces.is_empty() { + let type_list = self.gen_type_list(&cls.interfaces).with_context(|| { + format!("Failed to generate interface list for {}", cls.__repr__()) + })?; + let offset = self.section_manager.get_offset(Section::TypeList) + + self.type_lists_with_offset[*self.type_lists_index.get(&type_list).unwrap()] + .1; + self.class_defs_list[*idx].interfaces_off = offset; + } + } + Ok(()) + } + + /// Link the offset of debug info item in code items. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. + fn link_code(&mut self) { + debug!("Link the debug_info_off entries in code_items"); + for code in self.code_items.iter_mut() { + if code.debug_info_off != 0 { + code.debug_info_off += self.section_manager.get_offset(Section::DebugInfoItem) - 1; + } + } + } + + /// Link all annotations objects. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. + fn link_annotations(&mut self) { + for annotation in self.annotations_directory_items.iter_mut() { + if annotation.class_annotations_off != 0 { + annotation.class_annotations_off += + self.section_manager.get_offset(Section::AnnotationSetItem) - 1; + } + for field_annotation in annotation.field_annotations.iter_mut() { + if field_annotation.annotations_off != 0 { + field_annotation.annotations_off += + self.section_manager.get_offset(Section::AnnotationSetItem) - 1; + } + } + for method_annotation in annotation.method_annotations.iter_mut() { + if method_annotation.annotations_off != 0 { + method_annotation.annotations_off += + self.section_manager.get_offset(Section::AnnotationSetItem) - 1; + } + } + for parameter_annotation in annotation.parameter_annotations.iter_mut() { + if parameter_annotation.annotations_off != 0 { + parameter_annotation.annotations_off += self + .section_manager + .get_offset(Section::AnnotationSetRefList) + - 1; + } + } + } + for annotation_set in self.annotation_set_items.iter_mut() { + for entry in annotation_set.entries.iter_mut() { + entry.annotation_off += self.section_manager.get_offset(Section::AnnotationItem); + // -1 now when linking frag + } + } + for list in self.annotation_set_lists.iter_mut() { + for annotation in list.list.iter_mut() { + if annotation.annotations_off != 0 { + annotation.annotations_off += + self.section_manager.get_offset(Section::AnnotationSetItem) - 1; + } + } + } + } + + fn write_dex_file(&mut self, writer: &mut dyn Write) -> Result<()> { + self.section_manager.reset(); + self.section_manager.add_elt(Section::HeaderItem, None); + + self.gen_string_data_section()?; + self.gen_type_ids_section()?; + self.gen_proto_ids_section()?; + self.gen_field_ids_section()?; + self.gen_method_ids_section()?; + + debug!("Sort classes and generate the class_defs and class_data section"); + for class_id in self.get_sorted_class_def()? { + self.insert_class_def_item(&class_id)?; + } + self.gen_type_list_section()?; + + // start by linking class_data_items to populate self.corrected_class_data_offset + // and update the class_data_item sections size. + // Why before gen_map_list? Because the offsets in class_data_items are F***ing Uleb128 + // encoded, so there size change when linking (see doc of self.corrected_class_data_offset). + let code_offset = self.section_manager.get_code_item_offset_prefinalized(); + self.link_class_data()?; + self.gen_map_list()?; + assert_eq!( + code_offset, + self.section_manager.get_offset(Section::CodeItem), + "Prelinking computed value and post linking value for \ + the offset of the code_item section don't match" + ); + + // From now on, all section are generated and the value in section_manager do not change, + + self.link_header(); + self.link_call_site_ids(); + self.link_proto_id()?; + self.link_class_def()?; + self.link_code(); + self.link_annotations(); + + debug!("Serialize the dex file"); + let mut buffer = Cursor::new(Vec::::new()); + + self.check_section_offset(&buffer, Section::HeaderItem); + Self::fix_section_alignement(&mut buffer, Section::HeaderItem)?; + self.header.serialize(&mut buffer)?; + // StringIdItem section + let mut string_off = self.section_manager.get_offset(Section::StringDataItem); + self.check_section_offset(&buffer, Section::StringIdItem); + for string in self.string_data_list.iter() { + let str_id = StringIdItem { + string_data_off: string_off, + }; + Self::fix_section_alignement(&mut buffer, Section::StringIdItem)?; + str_id.serialize(&mut buffer)?; + string_off += string.size() as u32; + } + // TypeId section + self.check_section_offset(&buffer, Section::TypeIdItem); + for ty in &self.type_ids_list { + Self::fix_section_alignement(&mut buffer, Section::TypeIdItem)?; + ty.serialize(&mut buffer)?; + } + // ProtoId section + self.check_section_offset(&buffer, Section::ProtoIdItem); + for proto in &self.proto_ids_list { + Self::fix_section_alignement(&mut buffer, Section::ProtoIdItem)?; + proto.serialize(&mut buffer)?; + } + // FieldIdItem section + self.check_section_offset(&buffer, Section::FieldIdItem); + for field_id in &self.field_ids_list { + Self::fix_section_alignement(&mut buffer, Section::FieldIdItem)?; + field_id.serialize(&mut buffer)?; + } + // MethodIdItem section + self.check_section_offset(&buffer, Section::MethodIdItem); + for method_id in &self.method_ids_list { + Self::fix_section_alignement(&mut buffer, Section::MethodIdItem)?; + method_id.serialize(&mut buffer)?; + } + // ClassDefItem section + self.check_section_offset(&buffer, Section::ClassDefItem); + for class_def in &self.class_defs_list { + Self::fix_section_alignement(&mut buffer, Section::ClassDefItem)?; + class_def.serialize(&mut buffer)?; + } + // CallSiteIdItem, data are inserted as encoded array item later + self.check_section_offset(&buffer, Section::CallSiteIdItem); + for call_site_id in &self.call_site_ids { + Self::fix_section_alignement(&mut buffer, Section::CallSiteIdItem)?; + call_site_id.serialize(&mut buffer)?; + } + + // MethodHandleItem section + self.check_section_offset(&buffer, Section::MethodHandleItem); + for handle in &self.method_handles { + Self::fix_section_alignement(&mut buffer, Section::MethodHandleItem)?; + handle.serialize(&mut buffer)?; + } + // MapList + self.check_section_offset(&buffer, Section::Data); + self.check_section_offset(&buffer, Section::MapList); + Self::fix_section_alignement(&mut buffer, Section::MapList)?; + self.map_list.serialize(&mut buffer)?; + // TypeList, + self.check_section_offset(&buffer, Section::TypeList); + for (list, _) in &self.type_lists_with_offset { + Self::fix_section_alignement(&mut buffer, Section::TypeList)?; + list.serialize(&mut buffer)?; + } + // AnnotationSetRefList section + self.check_section_offset(&buffer, Section::AnnotationSetRefList); + for list in &self.annotation_set_lists { + Self::fix_section_alignement(&mut buffer, Section::AnnotationSetRefList)?; + list.serialize(&mut buffer)?; + } + // AnnotationSetItem section + self.check_section_offset(&buffer, Section::AnnotationSetItem); + for set in &self.annotation_set_items { + Self::fix_section_alignement(&mut buffer, Section::AnnotationSetItem)?; + set.serialize(&mut buffer)?; + } + // CodeItem section + self.check_section_offset(&buffer, Section::CodeItem); + for code_item in &self.code_items { + Self::fix_section_alignement(&mut buffer, Section::CodeItem)?; + code_item.serialize(&mut buffer)? + } + // StringDataItem section + self.check_section_offset(&buffer, Section::StringDataItem); + for string in &self.string_data_list { + Self::fix_section_alignement(&mut buffer, Section::StringDataItem)?; + string.serialize(&mut buffer)?; + } + // DebugInfoItem section + self.check_section_offset(&buffer, Section::DebugInfoItem); + for debug_info in &self.debug_info_items { + Self::fix_section_alignement(&mut buffer, Section::DebugInfoItem)?; + debug_info.serialize(&mut buffer)?; + } + // AnnotationItem section + self.check_section_offset(&buffer, Section::AnnotationItem); + for annot in &self.annotation_items { + Self::fix_section_alignement(&mut buffer, Section::AnnotationItem)?; + annot.serialize(&mut buffer)?; + } + // EncodedArrayItem section + self.check_section_offset(&buffer, Section::EncodedArrayItem); + for array in &self.encoded_array_items { + Self::fix_section_alignement(&mut buffer, Section::EncodedArrayItem)?; + array.serialize(&mut buffer)?; + } + // AnnotationsDirectoryItem section + self.check_section_offset(&buffer, Section::AnnotationsDirectoryItem); + for dir in &self.annotations_directory_items { + Self::fix_section_alignement(&mut buffer, Section::AnnotationsDirectoryItem)?; + dir.serialize(&mut buffer)?; + } + // ClassDataItem section + self.check_section_offset(&buffer, Section::ClassDataItem); + for data in &self.class_data_list { + Self::fix_section_alignement(&mut buffer, Section::ClassDataItem)?; + data.serialize(&mut buffer)?; + } + // TODO: HiddenapiClassDataItem, + /* + self.check_section_offset(&buffer, Section::HiddenapiClassDataItem); + Self::fix_section_alignement(&mut buffer, Section::HiddenapiClassDataItem)?; + */ + + let end_data = buffer.position(); + assert_eq!( + end_data as u32, + self.header.data_off + self.header.data_size + ); + + // compute signature + buffer.seek(SeekFrom::Start(8 + 4 + 20))?; + let mut hasher = Sha1::new(); + io::copy(&mut buffer, &mut hasher)?; + self.header.signature = hasher.finalize().into(); + let size = buffer.seek(SeekFrom::End(0))? as u32; + self.header.file_size = size; + buffer.rewind()?; + self.header.serialize(&mut buffer)?; + + // Compute checksum + //buffer.seek(SeekFrom::Start(8 + 4))?; + let mut adler = Adler32::new(); + adler.write_slice(&buffer.get_ref()[8 + 4..]); + self.header.checksum = adler.checksum(); + buffer.rewind()?; + self.header.serialize(&mut buffer)?; + + // copy buffer to output + buffer.rewind()?; + io::copy(&mut buffer, writer)?; + + Ok(()) + } + + /// Insert 0 to a buffer until the right alignment is reached for an element of the + /// given section. + fn fix_section_alignement(buffer: &mut Cursor>, section: Section) -> Result<()> { + while buffer.position() % section.get_item_alignment() as u64 != 0 { + Serializable::serialize(&0u8, buffer)?; + } + Ok(()) + } + + /// Check if a section + fn check_section_offset(&self, buffer: &Cursor, section: Section) { + let mut pos = buffer.position(); + while pos % section.get_item_alignment() as u64 != 0 { + pos += 1; + } + let expected = self.section_manager.get_offset(section) as u64; + assert_eq!( + pos, expected, + "Computed section offset and actual section offset do not match for section \ + {section:?}, expected 0x{expected:x}, found 0x{pos:x}" + ); + } + + /// Compute the order of the classes in the section `class_defs`. + /// Class definitions must be sorted so that a class's superclass and interfaces + /// are before the class. + fn get_sorted_class_def(&self) -> Result> { + // Use Kahn's algorithm + let mut graph: HashMap<&IdType, (HashSet<&IdType>, HashSet<&IdType>)> = HashMap::new(); + for (ty, (def, _)) in &self.class_defs { + let mut edges_to = HashSet::new(); + if let Some(sup) = def.superclass.as_ref() { + if self.class_defs.get(sup).is_some() { + edges_to.insert(sup); + } + } + for sup in &def.interfaces { + if self.class_defs.get(sup).is_some() { + edges_to.insert(sup); + } + } + for n_to in &edges_to { + let (from, _) = graph + .entry(n_to) + .or_insert((HashSet::new(), HashSet::new())); + from.insert(ty); + } + let (_, to) = graph.entry(ty).or_insert((HashSet::new(), HashSet::new())); + to.extend(edges_to); + } + + let mut sorted = vec![]; + let mut no_outgoing: VecDeque<&IdType> = VecDeque::new(); + no_outgoing.extend( + graph + .iter() + .filter(|(_, (_, to))| to.is_empty()) + .map(|(ty, _)| ty), + ); + if no_outgoing.is_empty() { + bail!("The class inheritance topoloy is either empty or cyclic"); + } + + while let Some(n) = no_outgoing.pop_front() { + sorted.push(n.clone()); + let (from, _) = graph.get(n).cloned().unwrap(); + for n_from in from { + graph.entry(n_from).and_modify(|(_, to)| _ = to.remove(n)); + let (_, to) = graph.get(n_from).unwrap(); + if to.is_empty() { + no_outgoing.push_back(n_from); + } + } + graph + .entry(n) + .and_modify(|(from, _)| *from = HashSet::new()); + } + for (_, (from, to)) in graph { + if !from.is_empty() || !to.is_empty() { + bail!("The class inheritance topology is cyclic"); + } + } + Ok(sorted) + } + + fn gen_type_list(&self, list: &[IdType]) -> Result { + let mut type_list = TypeList { list: vec![] }; + for ty in list { + type_list.list.push(TypeItem { + type_idx: *self.type_ids.get(ty).ok_or(anyhow!( + "Could not found type {} in dex builder", + ty.__repr__() + ))? as u16, + }); + } + Ok(type_list) + } + + pub fn gen_dex_file_to_vec(&mut self) -> Result> { + let mut output = Cursor::new(Vec::::new()); + self.write_dex_file(&mut output)?; + Ok(output.into_inner()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Section { + HeaderItem, + StringIdItem, + TypeIdItem, + ProtoIdItem, + FieldIdItem, + MethodIdItem, + ClassDefItem, + CallSiteIdItem, + MethodHandleItem, + Data, + MapList, + TypeList, + AnnotationSetRefList, + AnnotationSetItem, + CodeItem, + StringDataItem, + DebugInfoItem, + AnnotationItem, + EncodedArrayItem, + AnnotationsDirectoryItem, + HiddenapiClassDataItem, + ClassDataItem, +} + +impl Section { + const VARIANT_LIST: &'static [Self] = &[ + Self::HeaderItem, + Self::StringIdItem, + Self::TypeIdItem, + Self::ProtoIdItem, + Self::FieldIdItem, + Self::MethodIdItem, + Self::ClassDefItem, + Self::CallSiteIdItem, + Self::MethodHandleItem, + Self::Data, + Self::MapList, + Self::TypeList, + Self::AnnotationSetRefList, + Self::AnnotationSetItem, + Self::CodeItem, + Self::StringDataItem, + Self::DebugInfoItem, + Self::AnnotationItem, + Self::EncodedArrayItem, + Self::AnnotationsDirectoryItem, + Self::ClassDataItem, // must be last because contains offsets in Uleb, + // so size change when linking ! + Self::HiddenapiClassDataItem, + ]; + + fn get_index(&self) -> usize { + match self { + Self::HeaderItem => 0, + Self::StringIdItem => 1, + Self::TypeIdItem => 2, + Self::ProtoIdItem => 3, + Self::FieldIdItem => 4, + Self::MethodIdItem => 5, + Self::ClassDefItem => 6, + Self::CallSiteIdItem => 7, + Self::MethodHandleItem => 8, + Self::Data => 9, + Self::MapList => 10, + Self::TypeList => 11, + Self::AnnotationSetRefList => 12, + Self::AnnotationSetItem => 13, + Self::CodeItem => 14, + Self::StringDataItem => 15, + Self::DebugInfoItem => 16, + Self::AnnotationItem => 17, + Self::EncodedArrayItem => 18, + Self::AnnotationsDirectoryItem => 19, + Self::ClassDataItem => 20, + Self::HiddenapiClassDataItem => 21, + } + } + + fn get_elt_size(&self, default_size: Option) -> usize { + let fixed_size = match self { + Self::HeaderItem => Some(0x70), + Self::StringIdItem => Some(4), + Self::TypeIdItem => Some(4), + Self::ProtoIdItem => Some(0xc), + Self::FieldIdItem => Some(8), + Self::MethodIdItem => Some(8), + Self::ClassDefItem => Some(0x20), + Self::CallSiteIdItem => Some(4), + Self::MethodHandleItem => Some(8), + Self::Data => panic!("Element cannot be inserted in data dirctly"), + Self::MapList => None, + Self::TypeList => None, + Self::AnnotationSetRefList => None, + Self::AnnotationSetItem => None, + Self::ClassDataItem => None, + Self::CodeItem => None, + Self::StringDataItem => None, + Self::DebugInfoItem => None, + Self::AnnotationItem => None, + Self::EncodedArrayItem => None, + Self::AnnotationsDirectoryItem => None, + Self::HiddenapiClassDataItem => None, + }; + if let (Some(fixed_size), Some(default_size)) = (fixed_size, default_size) { + if fixed_size == default_size { + default_size + } else { + panic!( + "Element in {:?} have a size of {}, not {}", + self, fixed_size, default_size + ) + } + } else { + fixed_size.or(default_size).unwrap_or_else(|| { + panic!( + "Element of {:?} don't have a fixed size, you need to provide one", + self + ) + }) + } + } + + /// Return the previous section if it exist. + fn prev(&self) -> Option { + match self { + Self::HeaderItem => None, + Self::StringIdItem => Some(Self::HeaderItem), + Self::TypeIdItem => Some(Self::StringIdItem), + Self::ProtoIdItem => Some(Self::TypeIdItem), + Self::FieldIdItem => Some(Self::ProtoIdItem), + Self::MethodIdItem => Some(Self::FieldIdItem), + Self::ClassDefItem => Some(Self::MethodIdItem), + Self::CallSiteIdItem => Some(Self::ClassDefItem), + Self::MethodHandleItem => Some(Self::CallSiteIdItem), + Self::Data => Some(Self::MethodHandleItem), + Self::MapList => Some(Self::MethodHandleItem), // Data is just an indicator + Self::TypeList => Some(Self::MapList), + Self::AnnotationSetRefList => Some(Self::TypeList), + Self::AnnotationSetItem => Some(Self::AnnotationSetRefList), + Self::CodeItem => Some(Self::AnnotationSetItem), + Self::StringDataItem => Some(Self::CodeItem), + Self::DebugInfoItem => Some(Self::StringDataItem), + Self::AnnotationItem => Some(Self::DebugInfoItem), + Self::EncodedArrayItem => Some(Self::AnnotationItem), + Self::AnnotationsDirectoryItem => Some(Self::EncodedArrayItem), + Self::ClassDataItem => Some(Self::AnnotationsDirectoryItem), + Self::HiddenapiClassDataItem => Some(Self::ClassDataItem), + } + } + + fn get_map_item_type(&self) -> MapItemType { + match self { + Self::HeaderItem => MapItemType::HeaderItem, + Self::StringIdItem => MapItemType::StringIdItem, + Self::TypeIdItem => MapItemType::TypeIdItem, + Self::ProtoIdItem => MapItemType::ProtoIdItem, + Self::FieldIdItem => MapItemType::FieldIdItem, + Self::MethodIdItem => MapItemType::MethodIdItem, + Self::ClassDefItem => MapItemType::ClassDefItem, + Self::CallSiteIdItem => MapItemType::CallSiteIdItem, + Self::MethodHandleItem => MapItemType::MethodHandleItem, + Self::Data => panic!("Data is not a MatItemType"), + Self::MapList => MapItemType::MapList, + Self::TypeList => MapItemType::TypeList, + Self::AnnotationSetRefList => MapItemType::AnnotationSetRefList, + Self::AnnotationSetItem => MapItemType::AnnotationSetItem, + Self::CodeItem => MapItemType::CodeItem, + Self::StringDataItem => MapItemType::StringDataItem, + Self::DebugInfoItem => MapItemType::DebugInfoItem, + Self::AnnotationItem => MapItemType::AnnotationItem, + Self::EncodedArrayItem => MapItemType::EncodedArrayItem, + Self::AnnotationsDirectoryItem => MapItemType::AnnotationsDirectoryItem, + Self::ClassDataItem => MapItemType::ClassDataItem, + Self::HiddenapiClassDataItem => MapItemType::HiddenapiClassDataItem, + } + } + + /// Return the alignment of the item in byte. + fn get_item_alignment(&self) -> u32 { + match self { + Self::HeaderItem => 4, + Self::StringIdItem => 4, + Self::TypeIdItem => 4, + Self::ProtoIdItem => 4, + Self::FieldIdItem => 4, + Self::MethodIdItem => 4, + Self::ClassDefItem => 4, + Self::CallSiteIdItem => 1, + Self::MethodHandleItem => 4, + Self::Data => 1, + Self::MapList => 4, + Self::TypeList => 4, + Self::AnnotationSetRefList => 4, + Self::AnnotationSetItem => 4, + Self::CodeItem => 4, + Self::StringDataItem => 1, + Self::DebugInfoItem => 1, + Self::AnnotationItem => 1, + Self::EncodedArrayItem => 1, + Self::AnnotationsDirectoryItem => 4, + Self::ClassDataItem => 1, + Self::HiddenapiClassDataItem => 1, + } + } + + fn is_data(&self) -> bool { + matches!(self, Self::Data) + } +} + +#[derive(Debug, Default, Clone)] +struct SectionManager { + sizes: [u32; Self::NB_SECTION], + nb_elt: [usize; Self::NB_SECTION], + offsets: [u32; Self::NB_SECTION], + editable: bool, +} + +impl SectionManager { + const NB_SECTION: usize = 22; + + fn reset(&mut self) { + self.sizes = [0; Self::NB_SECTION]; + self.nb_elt = [0; Self::NB_SECTION]; + self.offsets = [0; Self::NB_SECTION]; + self.editable = true; + } + + fn add_elt(&mut self, section: Section, size: Option) { + if !self.editable { + panic!("Try to modify a section when the sections are set to read only"); + } + if section.is_data() { + panic!("Cannot add element directly in section data"); + } + while self.sizes[section.get_index()] % section.get_item_alignment() != 0 { + self.sizes[section.get_index()] += 1; + } + self.sizes[section.get_index()] += section.get_elt_size(size) as u32; + self.nb_elt[section.get_index()] += 1; + } + + fn incr_section_size(&mut self, section: Section, size: usize) { + if !self.editable { + panic!("Try to modify a section when the sections are set to read only"); + } + self.sizes[section.get_index()] += size as u32; + } + + fn get_offset(&self, section: Section) -> u32 { + if self.editable { + panic!("Try to get section offset before sections are finilized"); + } + let size = self.offsets[section.get_index()]; + let alignment = section.get_item_alignment(); + if size % alignment != 0 { + panic!( + "section {section:?} should be aligned on {alignment} bytes, \ + found section offset 0x{size:x}" + ); // avoid by finilized + } + size + } + + fn get_unaligned_size(&self, section: Section) -> u32 { + if section.is_data() { + self.sizes[section.get_index()..].iter().sum() + } else { + self.sizes[section.get_index()] + } + } + + /// The position of a potential new item in the section considering alignment. + fn get_aligned_size(&self, section: Section) -> u32 { + let mut size = self.get_unaligned_size(section); + while size % section.get_item_alignment() != 0 { + size += 1; + } + size + } + + fn get_nb_elt(&self, section: Section) -> usize { + self.nb_elt[section.get_index()] + } + + /// Finialize the sections: switch to read only and fix the section alignment. + fn finalize_sections(&mut self) { + for section in Section::VARIANT_LIST { + while self.sizes[..section.get_index()].iter().sum::() + % section.get_item_alignment() + != 0 + { + self.incr_section_size( + section.prev().expect( + "First section (Header) should alway be aligned but \ + found unaligned section without predecessor", + ), + 1, + ); + } + } + let mut offset = 0; + for section in Section::VARIANT_LIST { + self.offsets[section.get_index()] = offset; + offset += self.sizes[section.get_index()]; + } + + self.editable = false; + } + + /// This method exist for the only purpose of linking the method code offset inside + /// the class data items. This linking needs to be done before finilizing because it change the + /// size of the class data item section. + /// + /// Seriously, avoid using this. + fn get_code_item_offset_prefinalized(&mut self) -> u32 { + if !self.editable || self.get_nb_elt(Section::MapList) != 0 { + panic!("Don't use this method for other purpose than linking class_data_items"); + } + let mut map_list_size = 4; + let map_item_size = 12; /* = MapItem { + type_: MapItemType::HeaderItem, + unused: 0, + size: 0, + offset: 0, + } + .size(); */ + for section in Section::VARIANT_LIST { + if !section.is_data() + && (self.get_nb_elt(*section) != 0 || section == &Section::MapList) + { + map_list_size += map_item_size; + } + } + let mut offset = map_list_size; // This is aligned so it wont affect alignment + for section in &Section::VARIANT_LIST[..Section::CodeItem.get_index()] { + // size Section::Data and size Section::MapList are 0 + while offset % section.get_item_alignment() != 0 { + offset += 1; + } + offset += self.sizes[section.get_index()]; + } + + offset + } + + /// Display the sections informations. + #[allow(dead_code)] + fn show(&self) { + let mut offset = 0; + for section in Section::VARIANT_LIST { + let size = self.get_unaligned_size(*section); + let new_offset = offset + size; + let nb_elt = self.get_nb_elt(*section); + println!( + "{section:?}: 0x{offset:x} -> 0x{new_offset:x} (size: 0x{size:x}, \ + nb elt: {nb_elt})" + ); + if !section.is_data() { + offset = new_offset; + } + } + } +} From e78a67d1d11b3f9dcf58f3614373bb8375878f30 Mon Sep 17 00:00:00 2001 From: Jean-Marie 'Histausse' Mineau Date: Fri, 22 Mar 2024 18:50:43 +0100 Subject: [PATCH 08/14] generate code when linking class data item --- androscalpel/src/dex_fragment.rs | 219 ++++++++++++++++++++----------- androscalpel/src/dex_writer.rs | 133 ++++++++++++++----- 2 files changed, 243 insertions(+), 109 deletions(-) diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs index aaea8db..e369844 100644 --- a/androscalpel/src/dex_fragment.rs +++ b/androscalpel/src/dex_fragment.rs @@ -10,6 +10,7 @@ use crate::Result; use crate::*; use androscalpel_serializer::*; +use crate::dex_writer::DexIndex; use crate::ins::{CallSite, Instruction}; use crate::instructions::*; use androscalpel_serializer::Instruction as InsFormat; @@ -572,12 +573,13 @@ impl DexFragment { Ok(()) } + // TODO: find if there is a way to efficiently link code item. /// Insert a code_item. /// - /// # Warning - /// - /// This is currently a stub that probably serialize invalid references to data. - fn insert_code_item(&mut self, code: &Code, index: &FragIndex) -> Result<()> { + /// This item cannot be cached, because the jump instructions depend on the size of + /// instructions that depend on the size of the descriptor ids that depend on the + /// list of all descriptors in the dex file. + fn insert_code_item(&mut self, code: &Code, index: &DexIndex) -> Result<()> { // Estimate instructions addresses let mut min_addr = 0; let mut max_addr = 0; @@ -1563,11 +1565,14 @@ impl DexFragment { let access_flags = Uleb128(class.direct_methods.get(id).unwrap().get_raw_access_flags()); let code_off = if let Some(code) = &class.direct_methods.get(id).unwrap().code { - let code_off = self.section_manager.get_aligned_size(FragSection::CodeItem); - self.insert_code_item(code, index).with_context(|| { - format!("Failed to convert serialize code of {}", id.__str__()) - })?; - Uleb128(code_off + 1) + // CodeItems depend to much on the complte descriptor list to be generated + // prior to linking + //let code_off = self.section_manager.get_aligned_size(FragSection::CodeItem); + //self.insert_code_item(code, index).with_context(|| { + // format!("Failed to convert serialize code of {}", id.__str__()) + //})?; + //Uleb128(code_off + 1) + Uleb128(1) } else { Uleb128(0) }; @@ -1597,11 +1602,14 @@ impl DexFragment { .get_raw_access_flags(), ); let code_off = if let Some(code) = &class.virtual_methods.get(id).unwrap().code { - let code_off = self.section_manager.get_aligned_size(FragSection::CodeItem); - self.insert_code_item(code, index).with_context(|| { - format!("Failed to convert serialize code of {}", id.__str__()) - })?; - Uleb128(code_off + 1) + // CodeItems depend to much on the complte descriptor list to be generated + // prior to linking + // let code_off = self.section_manager.get_aligned_size(FragSection::CodeItem); + // self.insert_code_item(code, index).with_context(|| { + // format!("Failed to convert serialize code of {}", id.__str__()) + // })?; + // Uleb128(code_off + 1) + Uleb128(1) } else { Uleb128(0) }; @@ -1884,21 +1892,19 @@ impl DexFragment { /// linked before. pub fn link_global_ids( &mut self, - global_strings: &[DexString], - global_type_ids: &[IdType], - global_proto_ids: &[IdMethodType], - global_field_ids: &[IdField], - global_method_ids: &[IdMethod], + class: &Class, + index: &DexIndex, nb_method_handle_before_fragment: usize, ) -> Result<()> { self.link_state.start_linking_idx()?; let string_reindex = Vec::with_capacity(self.strings.len()); + // TODO: considering we have the map, this can be simplified a lot let mut global_idx = 0; for s in self.strings { - while global_idx < global_strings.len() && global_strings[global_idx] != s { + while global_idx < index.strings_list.len() && index.strings_list[global_idx] != s { global_idx += 1; } - if global_idx == global_strings.len() { + if global_idx == index.strings_list.len() { bail!("String {} not found in global index", s.__str__()); } string_reindex.push(global_idx as u32); @@ -1906,10 +1912,10 @@ impl DexFragment { let type_reindex = Vec::with_capacity(self.type_ids.len()); let mut global_idx = 0; for ty in self.type_ids { - while global_idx < global_type_ids.len() && global_type_ids[global_idx] != ty { + while global_idx < index.type_ids_list.len() && index.type_ids_list[global_idx] != ty { global_idx += 1; } - if global_idx == global_type_ids.len() { + if global_idx == index.type_ids_list.len() { bail!("Type {} not found in global index", ty.__str__()); } type_reindex.push(global_idx as u32); @@ -1917,10 +1923,12 @@ impl DexFragment { let proto_reindex = Vec::with_capacity(self.proto_ids.len()); let mut global_idx = 0; for proto in self.proto_ids { - while global_idx < global_proto_ids.len() && global_proto_ids[global_idx] != proto { + while global_idx < index.proto_ids_list.len() + && index.proto_ids_list[global_idx] != proto + { global_idx += 1; } - if global_idx == global_proto_ids.len() { + if global_idx == index.proto_ids_list.len() { bail!("Prototype {} not found in global index", proto.__str__()); } proto_reindex.push(global_idx as u32); @@ -1928,10 +1936,12 @@ impl DexFragment { let field_reindex = Vec::with_capacity(self.field_ids.len()); let mut global_idx = 0; for field in self.field_ids { - while global_idx < global_field_ids.len() && global_field_ids[global_idx] != field { + while global_idx < index.field_ids_list.len() + && index.field_ids_list[global_idx] != field + { global_idx += 1; } - if global_idx == global_field_ids.len() { + if global_idx == index.field_ids_list.len() { bail!("Field {} not found in global index", field.__str__()); } field_reindex.push(global_idx as u16); @@ -1939,18 +1949,23 @@ impl DexFragment { let method_reindex = Vec::with_capacity(self.method_ids.len()); let mut global_idx = 0; for meth in self.method_ids { - while global_idx < global_method_ids.len() && global_method_ids[global_idx] != meth { + while global_idx < index.method_ids_list.len() + && index.method_ids_list[global_idx] != meth + { global_idx += 1; } - if global_idx == global_method_ids.len() { + if global_idx == index.method_ids_list.len() { bail!("Method {} not found in global index", meth.__str__()); } method_reindex.push(global_idx as u16); } + self.link_id_class_data_and_gen_code(class, &field_reindex, &method_reindex, index)?; self.link_id_class_def(&string_reindex, &type_reindex); self.link_id_method_handle(&field_reindex, &method_reindex); + // GEN CODE + todo!() } @@ -1977,51 +1992,6 @@ impl DexFragment { } } - fn link_id_code(&mut self, string_reindex: &[u32], type_reindex: &[u32]) { - let mut total_size = 0; - let mut code_item_relocation = if let FragLinkState::LinkedIdx { - code_item_relocation, - .. - } = self.link_state - { - code_item_relocation - } else { - // link_global_ids() should prevent that - panic!("link_id_code should not be run outside of fn link_global_ids(..)"); - }; - for code in self.code_items { - let current_size = code.size(); - for ins in &mut code.insns { - Self::link_id_ins(ins, string_reindex); - } - // TODO: TryItem recompute handler_off - if let Some(handlers) = code.handlers { - let mut handler_off_reindex = HashMap::new(); - let mut current_offset = handlers.size_field().size(); - let mut old_offset = handlers.size_field().size(); - for handlers in handlers.list { - handler_off_reindex.insert(old_offset as u16, current_offset as u16); - old_offset += handlers.size(); - for handler in handlers.handlers { - handler.type_idx.0 = type_reindex[handler.type_idx.0 as usize]; - } - current_offset += handlers.size(); - } - for try_ in code.tries { - try_.handler_off = *handler_off_reindex - .get(&try_.handler_off) - .expect("Something whent wrong with the handle reindexing"); - } - } - } - } - fn link_id_ins(ins: &mut InsFormat, string_reindex: &[u32]) { - match ins { - InsFormat::Format31C { op: 0x1b, b, .. } => *b = string_reindex[b as usize], - InsFormat::Format21C { op: 0x1a, b, .. } => todo!(), // TODO FUCK this - _ => todo!(), - } - } fn link_id_string_data(&mut self) { todo!() } @@ -2037,8 +2007,103 @@ impl DexFragment { fn link_id_annotation_dir(&mut self) { todo!() } - fn link_id_class_data(&mut self) { - todo!() + + /// Link ids in [`ClassDataItem`] *and* generate the [`CodeItem`]. + fn link_id_class_data_and_gen_code( + &mut self, + class: &Class, + field_reindex: &[u16], + method_reindex: &[u16], + index: &DexIndex, + ) -> Result<()> { + if let Some(data) = self.class_data { + let mut last_local_id = 0; + let mut last_global_id = 0; + for field in data.static_fields { + let new_local_id = last_local_id + field.field_idx_diff.0; + let new_global_id = field_reindex[new_local_id as usize]; + field.field_idx_diff.0 = (new_global_id - last_global_id) as u32; + last_local_id = new_local_id; + last_global_id = new_global_id; + } + let mut last_local_id = 0; + let mut last_global_id = 0; + for field in data.instance_fields { + let new_local_id = last_local_id + field.field_idx_diff.0; + let new_global_id = field_reindex[new_local_id as usize]; + field.field_idx_diff.0 = (new_global_id - last_global_id) as u32; + last_local_id = new_local_id; + last_global_id = new_global_id; + } + + let mut last_local_id = 0; + let mut last_global_id = 0; + for meth in data.direct_methods { + let new_local_id = last_local_id + meth.method_idx_diff.0; + let new_global_id = field_reindex[new_local_id as usize]; + meth.method_idx_diff.0 = (new_global_id - last_global_id) as u32; + if meth.code_off.0 != 0 { + let meth_id = index.method_ids_list[new_global_id as usize]; + let code = class + .direct_methods + .get(&meth_id) + .ok_or(anyhow!( + "direct method {} expected from fragment but not found in {}", + meth_id.__str__(), + class.__str__() + ))? + .code; + if let Some(code) = code { + let code_off = self.section_manager.get_aligned_size(FragSection::CodeItem); + self.insert_code_item(&code, index)?; + meth.code_off.0 = code_off + 1; + } else { + bail!( + "Inconsistant fragment: fragment expect a code item for {}\ + but none was found in {}", + meth_id.__str__(), + class.__str__() + ); + } + } + last_local_id = new_local_id; + last_global_id = new_global_id; + } + let mut last_local_id = 0; + let mut last_global_id = 0; + for meth in data.virtual_methods { + let new_local_id = last_local_id + meth.method_idx_diff.0; + let new_global_id = field_reindex[new_local_id as usize]; + meth.method_idx_diff.0 = (new_global_id - last_global_id) as u32; + if meth.code_off.0 != 0 { + let meth_id = index.method_ids_list[new_global_id as usize]; + let code = class + .virtual_methods + .get(&meth_id) + .ok_or(anyhow!( + "virtual method {} expected from fragment but not found in {}", + meth_id.__str__(), + class.__str__() + ))? + .code; + if let Some(code) = code { + let code_off = self.section_manager.get_aligned_size(FragSection::CodeItem); + self.insert_code_item(&code, index)?; + meth.code_off.0 = code_off + 1; + } else { + bail!( + "Inconsistant fragment: fragment expect a code item for {}\ + but none was found in {}", + meth_id.__str__(), + class.__str__() + ); + } + } + last_local_id = new_local_id; + last_global_id = new_global_id; + } + } + Ok(()) } } @@ -2305,7 +2370,7 @@ impl FragSectionManager { } } -/// Index that associate a type to its local id in a fragment. +/// Index that associate descriptors to their local id in a fragment. #[derive(Debug, Clone)] struct FragIndex { pub strings: HashMap, diff --git a/androscalpel/src/dex_writer.rs b/androscalpel/src/dex_writer.rs index 840872c..854bb73 100644 --- a/androscalpel/src/dex_writer.rs +++ b/androscalpel/src/dex_writer.rs @@ -1,34 +1,38 @@ //! The structure that generate a .dex from classes. -use std::collections::{HashSet, VecDeque}; +use std::collections::{HashMap, HashSet, VecDeque}; use crate::dex_fragment::DexFragment; -use crate::{DexString, IdField, IdMethod, IdMethodType, IdType}; +use crate::{Class, DexString, IdField, IdMethod, IdMethodType, IdType, Result}; #[derive(Debug, Clone)] -pub struct DexWriter { - fragments: VecDeque, +pub struct DexWriter<'a> { + classes: VecDeque<&'a Class>, } -impl Default for DexWriter { +impl<'a> Default for DexWriter<'a> { fn default() -> Self { Self { - fragments: VecDeque::new(), + classes: VecDeque::new(), } } } -impl DexWriter { +impl<'a> DexWriter<'a> { pub fn new() -> Self { Self::default() } pub fn empty(&self) -> bool { - self.fragments.is_empty() + self.classes.is_empty() + } + + pub fn add_class(&mut self, class: &'a Class) { + self.classes.push_back(class) } /// Take as many fragments as possible and convert them to a dex file. - pub fn generate_next_dex_file(&mut self) -> Vec { - let mut fragments = vec![]; + pub fn generate_next_dex_file(&mut self) -> Result> { + let mut fragments_in_file = vec![]; let mut string_set: HashSet = HashSet::new(); let mut type_set: HashSet = HashSet::new(); let mut proto_set: HashSet = HashSet::new(); @@ -37,8 +41,16 @@ impl DexWriter { let mut type_list_set: HashSet> = HashSet::new(); let mut nb_method_handle = 0; let mut nb_method_handle_before = vec![]; + let fragments: VecDeque<(&'a Class, DexFragment)> = self + .classes + .into_iter() + .map(|class| match DexFragment::new(class) { + Ok(frag) => Ok((class, frag)), + Err(err) => Err(err), + }) + .collect()?; loop { - let new_fragment = if let Some(new_fragment) = self.fragments.pop_front() { + let (class, new_fragment) = if let Some(new_fragment) = fragments.pop_front() { new_fragment } else { break; @@ -52,7 +64,7 @@ impl DexWriter { .count() > u16::MAX as usize { - self.fragments.push_front(new_fragment); + fragments.push_front((class, new_fragment)); break; } if proto_set.len() + new_fragment.proto_ids().len() > u16::MAX as usize @@ -64,7 +76,7 @@ impl DexWriter { .count() > u16::MAX as usize { - self.fragments.push_front(new_fragment); + fragments.push_front((class, new_fragment)); break; } if field_set.len() + new_fragment.field_ids().len() > u16::MAX as usize @@ -76,7 +88,7 @@ impl DexWriter { .count() > u16::MAX as usize { - self.fragments.push_front(new_fragment); + fragments.push_front((class, new_fragment)); break; } if method_set.len() + new_fragment.method_ids().len() > u16::MAX as usize @@ -88,7 +100,7 @@ impl DexWriter { .count() > u16::MAX as usize { - self.fragments.push_front(new_fragment); + fragments.push_front((class, new_fragment)); break; } string_set.extend(new_fragment.strings().iter().cloned()); @@ -99,32 +111,89 @@ impl DexWriter { type_list_set.insert(new_fragment.interfaces().to_vec()); nb_method_handle_before.push(nb_method_handle); nb_method_handle += new_fragment.method_handles().len(); - fragments.push(new_fragment); + fragments_in_file.push((class, new_fragment)); } type_list_set.extend(proto_set.iter().map(|proto| proto.parameters.clone())); - let mut strings: Vec = string_set.into_iter().collect(); + let mut strings: Vec = string_set.iter().cloned().collect(); strings.sort(); - let mut type_ids: Vec = type_set.into_iter().collect(); + let mut type_ids: Vec = type_set.iter().cloned().collect(); type_ids.sort(); - let mut proto_ids: Vec = proto_set.into_iter().collect(); + let mut proto_ids: Vec = proto_set.iter().cloned().collect(); proto_ids.sort(); - let mut field_ids: Vec = field_set.into_iter().collect(); + let mut field_ids: Vec = field_set.iter().cloned().collect(); field_ids.sort(); - let mut method_ids: Vec = method_set.into_iter().collect(); + let mut method_ids: Vec = method_set.iter().cloned().collect(); method_ids.sort(); - let mut type_lists: Vec> = type_list_set.into_iter().collect(); + let mut type_lists: Vec> = type_list_set.iter().cloned().collect(); + let index = DexIndex::new(&strings, &type_ids, &proto_ids, &field_ids, &method_ids); - for (i, fragment) in fragments.iter().enumerate() { - fragment.link_global_ids( - &strings, - &type_ids, - &proto_ids, - &field_ids, - &method_ids, - nb_method_handle_before[i], - ); + for (i, (class, fragment)) in fragments_in_file.iter().enumerate() { + fragment.link_global_ids(&index, nb_method_handle_before[i]); } - vec![] + Ok(vec![]) + } +} + +/// Index that associate descriptors to their id in the dex file. +#[derive(Debug, Clone)] +pub(crate) struct DexIndex<'a> { + pub strings: HashMap, + pub types: HashMap, + pub protos: HashMap, + pub fields: HashMap, + pub methods: HashMap, + pub strings_list: &'a [DexString], + pub type_ids_list: &'a [IdType], + pub proto_ids_list: &'a [IdMethodType], + pub field_ids_list: &'a [IdField], + pub method_ids_list: &'a [IdMethod], +} + +impl<'a> DexIndex<'a> { + fn new( + strings_list: &'a [DexString], + type_ids_list: &'a [IdType], + proto_ids_list: &'a [IdMethodType], + field_ids_list: &'a [IdField], + method_ids_list: &'a [IdMethod], + ) -> Self { + Self { + strings_list, + type_ids_list, + proto_ids_list, + field_ids_list, + method_ids_list, + strings: strings_list + .iter() + .cloned() + .enumerate() + .map(|(x, y)| (y, x)) + .collect(), + types: type_ids_list + .iter() + .cloned() + .enumerate() + .map(|(x, y)| (y, x)) + .collect(), + protos: proto_ids_list + .iter() + .cloned() + .enumerate() + .map(|(x, y)| (y, x)) + .collect(), + fields: field_ids_list + .iter() + .cloned() + .enumerate() + .map(|(x, y)| (y, x)) + .collect(), + methods: method_ids_list + .iter() + .cloned() + .enumerate() + .map(|(x, y)| (y, x)) + .collect(), + } } } From 74583b230b74b08c54da468afe06cb6727869d99 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Mon, 25 Mar 2024 16:22:25 +0100 Subject: [PATCH 09/14] wip link id --- androscalpel/src/dex_fragment.rs | 218 ++++++++++++++++++++++++++++--- 1 file changed, 199 insertions(+), 19 deletions(-) diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs index e369844..606a58a 100644 --- a/androscalpel/src/dex_fragment.rs +++ b/androscalpel/src/dex_fragment.rs @@ -49,7 +49,7 @@ pub struct DexFragment { /// The encoded_array_items section. encoded_array_items: Vec, /// The annotations_directory_item. - annotations_directory_items: Option, + annotations_directory_item: Option, /// The annotation_set_item section. annotation_set_items: Vec, /// The annotation item section. @@ -128,7 +128,7 @@ impl DexFragment { encoded_array_items: vec![], method_handles: vec![], code_items: vec![], - annotations_directory_items: None, + annotations_directory_item: None, annotation_set_items: vec![], annotation_items: vec![], annotation_set_lists: vec![], @@ -365,7 +365,7 @@ impl DexFragment { }; self.section_manager .add_elt(FragSection::AnnotationsDirectoryItem, Some(item.size())); - self.annotations_directory_items = Some(item); + self.annotations_directory_item = Some(item); Ok(()) } @@ -1963,8 +1963,23 @@ impl DexFragment { self.link_id_class_data_and_gen_code(class, &field_reindex, &method_reindex, index)?; self.link_id_class_def(&string_reindex, &type_reindex); self.link_id_method_handle(&field_reindex, &method_reindex); - - // GEN CODE + self.link_id_annotation( + &string_reindex, + &type_reindex, + &field_reindex, + &method_reindex, + &proto_reindex, + nb_method_handle_before_fragment, + ); + self.link_id_encoded_array_item( + &string_reindex, + &type_reindex, + &field_reindex, + &method_reindex, + &proto_reindex, + nb_method_handle_before_fragment, + ); + self.link_id_annotation_dir(field_reindex, method_reindex) todo!() } @@ -1992,20 +2007,184 @@ impl DexFragment { } } - fn link_id_string_data(&mut self) { - todo!() + fn link_id_annotation( + &mut self, + string_reindex: &[u32], + type_reindex: &[u32], + field_reindex: &[u16], + method_reindex: &[u16], + proto_reindex: &[u32], + nb_method_handle_before_fragment: usize, + ) { + let mut annotation_item_relocation = if let FragLinkState::LinkedIdx { + annotation_item_relocation, + .. + } = self.link_state + { + annotation_item_relocation + } else { + panic!("link_id_annotation() should only be called by link_global_ids()"); + }; + // TODO: can we update the new value directly in the direstoryies/set? + let mut initial_offset = 0; + let mut new_offset = 0; + for item in self.annotation_items { + let old_size = item.size() as u32; + item.annotation.type_idx.0 = type_reindex[item.annotation.type_idx.0 as usize]; + for elt in item.annotation.elements { + elt.name_idx.0 = string_reindex[elt.name_idx.0 as usize]; + Self::link_id_encoded_value( + &mut elt.value, + string_reindex, + type_reindex, + field_reindex, + method_reindex, + proto_reindex, + nb_method_handle_before_fragment, + ) + } + let new_size = item.size() as u32; + annotation_item_relocation.insert(initial_offset, new_offset); + initial_offset += old_size; + new_offset += new_size; + } } - fn link_id_debug_info(&mut self) { - todo!() + + fn link_id_encoded_annotation( + annotation: &mut EncodedAnnotation, + string_reindex: &[u32], + type_reindex: &[u32], + field_reindex: &[u16], + method_reindex: &[u16], + proto_reindex: &[u32], + nb_method_handle_before_fragment: usize, + ) { + annotation.type_idx.0 = type_reindex[annotation.type_idx.0 as usize]; + for elt in annotation.elements { + elt.name_idx.0 = string_reindex[elt.name_idx.0 as usize]; + Self::link_id_encoded_value( + &mut elt.value, + string_reindex, + type_reindex, + field_reindex, + method_reindex, + proto_reindex, + nb_method_handle_before_fragment, + ) + } } - fn link_id_annotation(&mut self) { - todo!() + + fn link_id_encoded_value( + val: &mut EncodedValue, + string_reindex: &[u32], + type_reindex: &[u32], + field_reindex: &[u16], + method_reindex: &[u16], + proto_reindex: &[u32], + nb_method_handle_before_fragment: usize, + ) { + match val { + EncodedValue::MethodType(idx) => *idx = proto_reindex[*idx as usize], + EncodedValue::MethodHandle(idx) => *idx += nb_method_handle_before_fragment as u32, + EncodedValue::String(idx) => *idx = string_reindex[*idx as usize], + EncodedValue::Type(idx) => *idx = type_reindex[*idx as usize], + EncodedValue::Field(idx) => *idx = field_reindex[*idx as usize] as u32, + EncodedValue::Method(idx) => *idx = method_reindex[*idx as usize] as u32, + EncodedValue::Enum(idx) => *idx = field_reindex[*idx as usize] as u32, + EncodedValue::Array(arr) => Self::link_id_encoded_array( + &mut arr, + string_reindex, + type_reindex, + field_reindex, + method_reindex, + proto_reindex, + nb_method_handle_before_fragment, + ), + EncodedValue::Annotation(annotation) => Self::link_id_encoded_annotation( + &mut annotation, + string_reindex, + type_reindex, + field_reindex, + method_reindex, + proto_reindex, + nb_method_handle_before_fragment, + ), + _ => (), + } } - fn link_id_encoded_array(&mut self) { - todo!() + + fn link_id_encoded_array( + array: &mut EncodedArray, + string_reindex: &[u32], + type_reindex: &[u32], + field_reindex: &[u16], + method_reindex: &[u16], + proto_reindex: &[u32], + nb_method_handle_before_fragment: usize, + ) { + for val in &mut array.values { + Self::link_id_encoded_value( + val, + string_reindex, + type_reindex, + field_reindex, + method_reindex, + proto_reindex, + nb_method_handle_before_fragment, + ) + } } - fn link_id_annotation_dir(&mut self) { - todo!() + + fn link_id_encoded_array_item( + &mut self, + string_reindex: &[u32], + type_reindex: &[u32], + field_reindex: &[u16], + method_reindex: &[u16], + proto_reindex: &[u32], + nb_method_handle_before_fragment: usize, + ) { + let mut encoded_array_relocation = if let FragLinkState::LinkedIdx { + encoded_array_relocation, + .. + } = self.link_state + { + encoded_array_relocation + } else { + panic!("link_id_encoded_arrays() should only be called by link_global_ids()"); + }; + let mut initial_offset = 0; + let mut relocated_offset = 0; + for array in self.encoded_array_items { + let old_size = array.size() as u32; + Self::link_id_encoded_array( + &mut array.value, + string_reindex, + type_reindex, + field_reindex, + method_reindex, + proto_reindex, + nb_method_handle_before_fragment, + ); + let new_size = array.size() as u32; + encoded_array_relocation.insert(old_size, new_size); + initial_offset += old_size; + relocated_offset += new_size; + } + } + + fn link_id_annotation_dir(&mut self, field_reindex: &[u16], method_reindex: &[u16]) { + if let Some(dir) = self.annotations_directory_item { + for annot in dir.field_annotations { + annot.field_idx = field_reindex[annot.field_idx as usize] as u32; + } + for annot in dir.method_annotations { + annot.method_idx = method_reindex[annot.method_idx as usize] as u32; + } + for annot in dir.parameter_annotations { + annot.method_idx = method_reindex[annot.method_idx as usize] as u32; + } + } } /// Link ids in [`ClassDataItem`] *and* generate the [`CodeItem`]. @@ -2389,8 +2568,9 @@ enum FragLinkState { /// The new local addresses of the element whose size depend on the value /// of the index are stored here. LinkedIdx { - code_item_relocation: HashMap, - debug_info_item_relocation: HashMap, + // Not relocation: the code are entirely generated at link id time + //code_item_relocation: HashMap, + //debug_info_item_relocation: HashMap, annotation_item_relocation: HashMap, encoded_array_relocation: HashMap, // TODO only one? }, @@ -2401,8 +2581,8 @@ impl FragLinkState { match self { Self::Unlinked => { *self = Self::LinkedIdx { - code_item_relocation: HashMap::::new(), - debug_info_item_relocation: HashMap::::new(), + // code_item_relocation: HashMap::::new(), + // debug_info_item_relocation: HashMap::::new(), annotation_item_relocation: HashMap::::new(), encoded_array_relocation: HashMap::::new(), }; From 250f85700ebe4a0b57c9023a1a0b56c062aad40a Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Mon, 25 Mar 2024 17:24:42 +0100 Subject: [PATCH 10/14] finish-ish id linking --- TODO.md | 14 +----- androscalpel/src/dex_fragment.rs | 81 +++++++++++++++++++++++++++----- 2 files changed, 71 insertions(+), 24 deletions(-) diff --git a/TODO.md b/TODO.md index 5cb22cb..4806a58 100644 --- a/TODO.md +++ b/TODO.md @@ -3,18 +3,6 @@ - https://source.android.com/docs/core/runtime/dex-format#system-annotation - goto size computation - no nop when no payload +- code items need a "fragment" repr - option to get label at every code addresses - name register / parameters - -# frag meth -- insert_class_static_values - -# relink - -data.static_fields: field_idx_diff -data.direct_methods: method_idx_diff -data.code_off -code_item.try_item -instr - -EncodedValue diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs index 606a58a..fe70f28 100644 --- a/androscalpel/src/dex_fragment.rs +++ b/androscalpel/src/dex_fragment.rs @@ -66,6 +66,14 @@ pub struct DexFragment { interfaces: Vec, /// The current link state of the fragment. link_state: FragLinkState, + + /// This should not be stored, but currently code items + /// need to be generated when linking ids, and to avoid having + /// fragmented data representations, the data used by the code + /// must be generated "pre-linking". Idealy, code items should be + /// precompiled in an intermediate state when creating the fragment, + /// and the `FragIndex` would not be needed anymore. + frag_index: Option, } impl DexFragment { @@ -135,6 +143,7 @@ impl DexFragment { debug_info_items: vec![], interfaces: class.interfaces.clone(), link_state: FragLinkState::Unlinked, + frag_index: None, }; frag.strings = class.get_all_strings().into_iter().collect(); frag.strings.sort(); @@ -234,6 +243,7 @@ impl DexFragment { } else { 0 }; + frag.frag_index = Some(index); Ok(frag) } @@ -579,7 +589,14 @@ impl DexFragment { /// This item cannot be cached, because the jump instructions depend on the size of /// instructions that depend on the size of the descriptor ids that depend on the /// list of all descriptors in the dex file. - fn insert_code_item(&mut self, code: &Code, index: &DexIndex) -> Result<()> { + fn insert_code_item( + &mut self, + code: &Code, + index: &DexIndex, + nb_call_site_before_fragment: usize, + nb_method_handle_before_fragment: usize, + frag_index: &FragIndex, + ) -> Result<()> { // Estimate instructions addresses let mut min_addr = 0; let mut max_addr = 0; @@ -1350,15 +1367,16 @@ impl DexFragment { insns.push(ins); } Instruction::InvokeCustom(ins) => { - let call_site_idx = self.call_site_ids.len(); - self.insert_call_site_item(&ins.call_site, index)?; + let call_site_idx = self.call_site_ids.len() + nb_call_site_before_fragment; + self.insert_call_site_item(&ins.call_site, frag_index)?; let ins = ins.get_raw_ins(call_site_idx); addr += ins.size() / 2; insns.push(ins); } Instruction::ConstMethodHandle(ins) => { - let method_handle_idx = self.method_handles.len(); - self.insert_method_handle(&ins.handle, index)?; + let method_handle_idx = + self.method_handles.len() + nb_method_handle_before_fragment; + self.insert_method_handle(&ins.handle, frag_index)?; let ins = ins.get_raw_ins(method_handle_idx); addr += ins.size() / 2; insns.push(ins); @@ -1890,12 +1908,31 @@ impl DexFragment { /// Some structure change size depending of the value of the index they refere to. /// The new size needs to be known before the offset can be linked, so the idx are /// linked before. + /// + /// `call_site_id_item`s are sorted by offset of the associated array, so they are naturally + /// sorted, and `method_handle_item`s are not sorted. This means linking those id is done + /// simply by adding the number of ids before the fragement. + /// + /// ## Warning + /// + /// The `nb_call_site_before_fragment` and `nb_method_handle_before_fragment` must be computed + /// **after** linking the id of the previous fragment as the code item is generated at this + /// step, doing so generate `call_site_id_item`s and `method_handle_item`s. + /// + /// This is bad as it prevent parallelistation of this step. TODO pub fn link_global_ids( &mut self, class: &Class, index: &DexIndex, + nb_call_site_before_fragment: usize, nb_method_handle_before_fragment: usize, ) -> Result<()> { + let frag_index = if let Some(frag_index) = self.frag_index { + self.frag_index = None; + frag_index + } else { + bail!("Fragment cannot be linked without the frag index (see DexFragment::frag_index doc)") + }; self.link_state.start_linking_idx()?; let string_reindex = Vec::with_capacity(self.strings.len()); // TODO: considering we have the map, this can be simplified a lot @@ -1960,7 +1997,15 @@ impl DexFragment { method_reindex.push(global_idx as u16); } - self.link_id_class_data_and_gen_code(class, &field_reindex, &method_reindex, index)?; + self.link_id_class_data_and_gen_code( + class, + &field_reindex, + &method_reindex, + index, + nb_call_site_before_fragment, + nb_method_handle_before_fragment, + &frag_index, + )?; self.link_id_class_def(&string_reindex, &type_reindex); self.link_id_method_handle(&field_reindex, &method_reindex); self.link_id_annotation( @@ -1979,9 +2024,8 @@ impl DexFragment { &proto_reindex, nb_method_handle_before_fragment, ); - self.link_id_annotation_dir(field_reindex, method_reindex) - - todo!() + self.link_id_annotation_dir(&field_reindex, &method_reindex); + Ok(()) } fn link_id_class_def(&mut self, string_reindex: &[u32], type_reindex: &[u32]) { @@ -2194,6 +2238,9 @@ impl DexFragment { field_reindex: &[u16], method_reindex: &[u16], index: &DexIndex, + nb_call_site_before_fragment: usize, + nb_method_handle_before_fragment: usize, + frag_index: &FragIndex, ) -> Result<()> { if let Some(data) = self.class_data { let mut last_local_id = 0; @@ -2234,7 +2281,13 @@ impl DexFragment { .code; if let Some(code) = code { let code_off = self.section_manager.get_aligned_size(FragSection::CodeItem); - self.insert_code_item(&code, index)?; + self.insert_code_item( + &code, + index, + nb_call_site_before_fragment, + nb_method_handle_before_fragment, + frag_index, + )?; meth.code_off.0 = code_off + 1; } else { bail!( @@ -2267,7 +2320,13 @@ impl DexFragment { .code; if let Some(code) = code { let code_off = self.section_manager.get_aligned_size(FragSection::CodeItem); - self.insert_code_item(&code, index)?; + self.insert_code_item( + &code, + index, + nb_call_site_before_fragment, + nb_method_handle_before_fragment, + frag_index, + )?; meth.code_off.0 = code_off + 1; } else { bail!( From 01c496aaacdcdd88008b75d7fb49dbef35bf1dfb Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Thu, 28 Mar 2024 17:16:56 +0100 Subject: [PATCH 11/14] WIP section manager utils --- androscalpel/src/dex_fragment.rs | 200 ++++++------ androscalpel/src/dex_writer.rs | 509 ++++++++++++++++++++++++++++++- 2 files changed, 598 insertions(+), 111 deletions(-) diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs index fe70f28..4a0b0a9 100644 --- a/androscalpel/src/dex_fragment.rs +++ b/androscalpel/src/dex_fragment.rs @@ -1905,6 +1905,65 @@ impl DexFragment { Ok(()) } + /// Return `(nb_call_site, nb_method_handle)` the numbers of call sites and method handles in + /// the fragment. This is a non-trivial task that because some are generated during + /// [`Self::link_global_ids()`]. + pub fn get_nb_call_site_and_method_handle(&self, class: &Class) -> (usize, usize) { + fn get_nb_call_site_and_method_handle_in_method(method: &Method) -> (usize, usize) { + let mut nb_call_site = 0; + let mut nb_method_handle = 0; + if let Some(Code { insns, .. }) = method.code { + for ins in insns { + match ins { + Instruction::ConstMethodHandle(_) => nb_method_handle += 1, + Instruction::InvokeCustom(ins) => { + nb_call_site += 1; + let mut val_to_process: Vec<&DexValue> = + ins.call_site.args.iter().collect(); + while let Some(val) = val_to_process.pop() { + match val { + DexValue::MethodHandle(_) => nb_method_handle += 1, + DexValue::Array(DexArray(array)) => { + val_to_process.extend(array) + } + DexValue::Annotation(DexAnnotation { elements, .. }) => { + val_to_process.extend(elements.values()) + } + + _ => (), + } + } + } + _ => (), + } + } + }; + (nb_call_site, nb_method_handle) + } + + match self.link_state { + FragLinkState::Unlinked => { + let mut nb_call_site = self.call_site_ids.len(); + let mut nb_method_handle = self.method_handles.len(); + for method in class.direct_methods.values() { + let (nnb_call_site, nnb_method_handle) = + get_nb_call_site_and_method_handle_in_method(method); + nb_call_site += nnb_call_site; + nb_method_handle += nnb_method_handle + } + for method in class.virtual_methods.values() { + let (nnb_call_site, nnb_method_handle) = + get_nb_call_site_and_method_handle_in_method(method); + nb_call_site += nnb_call_site; + nb_method_handle += nnb_method_handle + } + (nb_call_site, nb_method_handle) + } + // after Self::link_global_ids(), the lists are complete + _ => (self.call_site_ids.len(), self.method_handles.len()), + } + } + /// Some structure change size depending of the value of the index they refere to. /// The new size needs to be known before the offset can be linked, so the idx are /// linked before. @@ -1916,10 +1975,9 @@ impl DexFragment { /// ## Warning /// /// The `nb_call_site_before_fragment` and `nb_method_handle_before_fragment` must be computed - /// **after** linking the id of the previous fragment as the code item is generated at this - /// step, doing so generate `call_site_id_item`s and `method_handle_item`s. - /// - /// This is bad as it prevent parallelistation of this step. TODO + /// by [`Self::get_nb_call_site_and_method_handle()`] because some of those values are not + /// generated before linking id (because code items are generated when linking id, not good but + /// all we have right now). pub fn link_global_ids( &mut self, class: &Class, @@ -2070,6 +2128,9 @@ impl DexFragment { panic!("link_id_annotation() should only be called by link_global_ids()"); }; // TODO: can we update the new value directly in the direstoryies/set? + // The size of the elements change, so the section size must be recomputed + self.section_manager + .reset_section(FragSection::AnnotationItem); let mut initial_offset = 0; let mut new_offset = 0; for item in self.annotation_items { @@ -2091,6 +2152,8 @@ impl DexFragment { annotation_item_relocation.insert(initial_offset, new_offset); initial_offset += old_size; new_offset += new_size; + self.section_manager + .add_elt(FragSection::AnnotationItem, Some(new_size as usize)); } } @@ -2197,6 +2260,9 @@ impl DexFragment { } else { panic!("link_id_encoded_arrays() should only be called by link_global_ids()"); }; + // The size of the elements change, so the section size must be recomputed + self.section_manager + .reset_section(FragSection::EncodedArrayItem); let mut initial_offset = 0; let mut relocated_offset = 0; for array in self.encoded_array_items { @@ -2214,6 +2280,8 @@ impl DexFragment { encoded_array_relocation.insert(old_size, new_size); initial_offset += old_size; relocated_offset += new_size; + self.section_manager + .add_elt(FragSection::EncodedArrayItem, Some(new_size as usize)); } } @@ -2343,10 +2411,18 @@ impl DexFragment { } Ok(()) } + + pub fn get_section_manager(&self) -> &FragSectionManager { + &self.section_manager + } + + pub fn get_interfaces(&self) -> &Vec { + &self.interfaces + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum FragSection { +pub(crate) enum FragSection { ClassDefItem, CallSiteIdItem, MethodHandleItem, @@ -2363,7 +2439,7 @@ enum FragSection { } impl FragSection { - const VARIANT_LIST: &'static [Self] = &[ + pub(crate) const VARIANT_LIST: &'static [Self] = &[ Self::ClassDefItem, Self::CallSiteIdItem, Self::MethodHandleItem, @@ -2454,27 +2530,15 @@ impl FragSection { } #[derive(Debug, Default, Clone)] -struct FragSectionManager { +pub(crate) struct FragSectionManager { sizes: [u32; Self::NB_SECTION], nb_elt: [usize; Self::NB_SECTION], - //offsets: [u32; Self::NB_SECTION], - editable: bool, } impl FragSectionManager { const NB_SECTION: usize = 12; - fn reset(&mut self) { - self.sizes = [0; Self::NB_SECTION]; - self.nb_elt = [0; Self::NB_SECTION]; - //self.offsets = [0; Self::NB_SECTION]; - self.editable = true; - } - fn add_elt(&mut self, section: FragSection, size: Option) { - if !self.editable { - panic!("Try to modify a section when the sections are set to read only"); - } if (section == FragSection::ClassDefItem || section == FragSection::ClassDataItem || section == FragSection::AnnotationsDirectoryItem) @@ -2488,35 +2552,18 @@ impl FragSectionManager { self.sizes[section.get_index()] += section.get_elt_size(size) as u32; self.nb_elt[section.get_index()] += 1; } - /* - fn incr_section_size(&mut self, section: FragSection, size: usize) { - if !self.editable { - panic!("Try to modify a section when the sections are set to read only"); - } - self.sizes[section.get_index()] += size as u32; + + fn reset_section(&mut self, section: FragSection) { + self.sizes[section.get_index()] = 0; + self.nb_elt[section.get_index()] = 0; } - fn get_offset(&self, section: FragSection) -> u32 { - if self.editable { - panic!("Try to get section offset before sections are finilized"); - } - let size = self.offsets[section.get_index()]; - let alignment = section.get_item_alignment(); - if size % alignment != 0 { - panic!( - "section {section:?} should be aligned on {alignment} bytes, \ - found section offset 0x{size:x}" - ); // avoid by finilized - } - size - } - */ fn get_unaligned_size(&self, section: FragSection) -> u32 { self.sizes[section.get_index()] } /// The position of a potential new item in the section considering alignment. - fn get_aligned_size(&self, section: FragSection) -> u32 { + pub(crate) fn get_aligned_size(&self, section: FragSection) -> u32 { let mut size = self.get_unaligned_size(section); while size % section.get_item_alignment() != 0 { size += 1; @@ -2524,73 +2571,22 @@ impl FragSectionManager { size } - fn get_nb_elt(&self, section: FragSection) -> usize { + pub(crate) fn get_nb_elt(&self, section: FragSection) -> usize { self.nb_elt[section.get_index()] } - /* - /// Finialize the sections: switch to read only and fix the section alignment. - fn finalize_sections(&mut self) { - for section in Section::VARIANT_LIST { - while self.sizes[..section.get_index()].iter().sum::() - % section.get_item_alignment() - != 0 - { - self.incr_section_size( - section.prev().expect( - "First section (Header) should alway be aligned but \ - found unaligned section without predecessor", - ), - 1, - ); - } + /// Generate a new section manager with the content of an added fragment. + pub(crate) fn merge_fragment(&self, frag_sections: &FragSectionManager) -> Self { + let mut new_section = self.clone(); + for section in FragSection::VARIANT_LIST { + new_section.nb_elt[section.get_index()] += frag_sections.get_nb_elt(*section); + // Noticed the "aligned" + new_section.sizes[section.get_index()] = + new_section.get_aligned_size(*section) + frag_sections.get_aligned_size(*section); } - let mut offset = 0; - for section in Section::VARIANT_LIST { - self.offsets[section.get_index()] = offset; - offset += self.sizes[section.get_index()]; - } - - self.editable = false; + new_section } - /// This method exist for the only purpose of linking the method code offset inside - /// the class data items. This linking needs to be done before finilizing because it change the - /// size of the class data item section. - /// - /// Seriously, avoid using this. - fn get_code_item_offset_prefinalized(&mut self) -> u32 { - if !self.editable || self.get_nb_elt(Section::MapList) != 0 { - panic!("Don't use this method for other purpose than linking class_data_items"); - } - let mut map_list_size = 4; - let map_item_size = 12; /* = MapItem { - type_: MapItemType::HeaderItem, - unused: 0, - size: 0, - offset: 0, - } - .size(); */ - for section in Section::VARIANT_LIST { - if !section.is_data() - && (self.get_nb_elt(*section) != 0 || section == &Section::MapList) - { - map_list_size += map_item_size; - } - } - let mut offset = map_list_size; // This is aligned so it wont affect alignment - for section in &Section::VARIANT_LIST[..Section::CodeItem.get_index()] { - // size Section::Data and size Section::MapList are 0 - while offset % section.get_item_alignment() != 0 { - offset += 1; - } - offset += self.sizes[section.get_index()]; - } - - offset - } - */ - /// Display the sections informations. #[allow(dead_code)] fn show(&self) { diff --git a/androscalpel/src/dex_writer.rs b/androscalpel/src/dex_writer.rs index 854bb73..862a8eb 100644 --- a/androscalpel/src/dex_writer.rs +++ b/androscalpel/src/dex_writer.rs @@ -1,8 +1,10 @@ //! The structure that generate a .dex from classes. use std::collections::{HashMap, HashSet, VecDeque}; -use crate::dex_fragment::DexFragment; +use crate::dex_fragment::{DexFragment, FragSection, FragSectionManager}; use crate::{Class, DexString, IdField, IdMethod, IdMethodType, IdType, Result}; +use androscalpel_serializer::{MapItem, MapItemType, MapList, Serializable, TypeItem, TypeList}; +use anyhow::{anyhow, bail}; #[derive(Debug, Clone)] pub struct DexWriter<'a> { @@ -39,8 +41,6 @@ impl<'a> DexWriter<'a> { let mut field_set: HashSet = HashSet::new(); let mut method_set: HashSet = HashSet::new(); let mut type_list_set: HashSet> = HashSet::new(); - let mut nb_method_handle = 0; - let mut nb_method_handle_before = vec![]; let fragments: VecDeque<(&'a Class, DexFragment)> = self .classes .into_iter() @@ -48,7 +48,7 @@ impl<'a> DexWriter<'a> { Ok(frag) => Ok((class, frag)), Err(err) => Err(err), }) - .collect()?; + .collect::>()?; loop { let (class, new_fragment) = if let Some(new_fragment) = fragments.pop_front() { new_fragment @@ -109,9 +109,9 @@ impl<'a> DexWriter<'a> { field_set.extend(new_fragment.field_ids().iter().cloned()); method_set.extend(new_fragment.method_ids().iter().cloned()); type_list_set.insert(new_fragment.interfaces().to_vec()); - nb_method_handle_before.push(nb_method_handle); - nb_method_handle += new_fragment.method_handles().len(); - fragments_in_file.push((class, new_fragment)); + let (nb_call_site, nb_method_handle) = + new_fragment.get_nb_call_site_and_method_handle(&class); + fragments_in_file.push((class, new_fragment, nb_call_site, nb_method_handle)); } type_list_set.extend(proto_set.iter().map(|proto| proto.parameters.clone())); let mut strings: Vec = string_set.iter().cloned().collect(); @@ -127,10 +127,135 @@ impl<'a> DexWriter<'a> { let mut type_lists: Vec> = type_list_set.iter().cloned().collect(); let index = DexIndex::new(&strings, &type_ids, &proto_ids, &field_ids, &method_ids); - for (i, (class, fragment)) in fragments_in_file.iter().enumerate() { - fragment.link_global_ids(&index, nb_method_handle_before[i]); + // TODO: Sort fragments_in_file in topological order + // fragments_in_file.sort() magic stuff + let mut nb_call_site_total = 0; + let mut nb_method_handle_total = 0; + for (_, _, nb_call_site, nb_method_handle) in &mut fragments_in_file { + let nb_call_site_before_fragment = nb_call_site_total; + let nb_method_handle_before_fragment = nb_method_handle_total; + nb_call_site_total += *nb_call_site; + nb_method_handle_total += *nb_method_handle; + *nb_call_site = nb_call_site_before_fragment; + *nb_method_handle = nb_method_handle_before_fragment; } + // Link descriptor ids (and generate code items) + let fragments_in_file: Vec = fragments_in_file + .into_iter() + .map( + |( + class, + fragment, + nb_call_site_before_fragment, + nb_method_handle_before_fragment, + )| { + match fragment.link_global_ids( + class, + &index, + nb_call_site_before_fragment, + nb_method_handle_before_fragment, + ) { + Err(err) => Err(err), + Ok(()) => Ok(fragment), + } + }, + ) + .collect::>()?; + + let mut type_lists = HashMap::, u32>::new(); + // collect section offsets + // The type are quite uggly but I'm behind on time + let mut fragment_section_manager = FragSectionManager::default(); + let fragments_in_file: Vec<(FragSectionManager, DexFragment)> = fragments_in_file + .into_iter() + .map(|fragment| { + let old_section_manager = fragment_section_manager; + let new_section_manager = + fragment_section_manager.merge_fragment(fragment.get_section_manager()); + fragment_section_manager = new_section_manager; + if !fragment.get_interfaces().is_empty() { + type_lists.insert(fragment.get_interfaces().clone(), 0); + } + (old_section_manager, fragment) + }) + .collect(); + + let mut section_manager: SectionManager = fragment_section_manager.into(); + + section_manager.add_elt(Section::HeaderItem, None); + section_manager.add_multiple_elt(Section::StringIdItem, strings.len()); + section_manager.add_multiple_elt(Section::TypeIdItem, type_ids.len()); + section_manager.add_multiple_elt(Section::ProtoIdItem, proto_ids.len()); + section_manager.add_multiple_elt(Section::FieldIdItem, field_ids.len()); + section_manager.add_multiple_elt(Section::MethodIdItem, method_ids.len()); + for string in &strings { + section_manager.add_elt(Section::StringDataItem, Some(string.0.size())); + } + for proto in &proto_ids { + if !proto.parameters.is_empty() { + type_lists.insert(proto.parameters.clone(), 0); + } + } + let type_list_list: Vec = type_lists + .keys() + .into_iter() + .map(|ty_list| { + if let Some(local_off) = type_lists.get_mut(ty_list) { + *local_off = section_manager.get_aligned_size(Section::TypeList) + } else { + bail!("type list {ty_list:?} not needed but not found in dex_writter") + } + let ty_list = TypeList { + list: ty_list + .into_iter() + .map(|ty| { + index + .types + .get(ty) + .ok_or(anyhow!( + "type {} needed but not found in dex writer", + ty.__str__() + )) + .map(|idx| TypeItem { + type_idx: *idx as u16, + }) + }) + .collect::>()?, + }; + + section_manager.add_elt(Section::TypeList, Some(ty_list.size())); + Ok(ty_list) + }) + .collect::>()?; + section_manager.add_elt(Section::MapList, Some(4)); + let offsets = section_manager.get_offsets(); + let map_item_size = 12; /* = MapItem { + type_: MapItemType::HeaderItem, + unused: 0, + size: 0, + offset: 0, + } + .size(); */ + let mut map_list = MapList::default(); + for section in Section::VARIANT_LIST { + if !section.is_data() && section_manager.get_nb_elt(*section) != 0 { + section_manager.incr_section_size(Section::MapList, map_item_size); + map_list.list.push(MapItem { + type_: section.get_map_item_type(), + unused: 0, + size: section_manager.get_nb_elt(*section) as u32, + offset: offsets.get_offset(*section), + }); + } + } + + // TODO: link en correct data section + + // TODO: link everything else + + // TODO: serialize + Ok(vec![]) } } @@ -197,3 +322,369 @@ impl<'a> DexIndex<'a> { } } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Section { + HeaderItem, + StringIdItem, + TypeIdItem, + ProtoIdItem, + FieldIdItem, + MethodIdItem, + ClassDefItem, + CallSiteIdItem, + MethodHandleItem, + Data, + MapList, + TypeList, + AnnotationSetRefList, + AnnotationSetItem, + CodeItem, + StringDataItem, + DebugInfoItem, + AnnotationItem, + EncodedArrayItem, + AnnotationsDirectoryItem, + HiddenapiClassDataItem, + ClassDataItem, +} + +impl From<&FragSection> for Section { + fn from(section: &FragSection) -> Self { + match section { + FragSection::ClassDefItem => Section::ClassDefItem, + FragSection::CallSiteIdItem => Section::CallSiteIdItem, + FragSection::MethodHandleItem => Section::MethodHandleItem, + FragSection::AnnotationSetRefList => Section::AnnotationSetRefList, + FragSection::AnnotationSetItem => Section::AnnotationSetItem, + FragSection::CodeItem => Section::CodeItem, + FragSection::StringDataItem => Section::StringDataItem, + FragSection::DebugInfoItem => Section::DebugInfoItem, + FragSection::AnnotationItem => Section::AnnotationItem, + FragSection::EncodedArrayItem => Section::EncodedArrayItem, + FragSection::AnnotationsDirectoryItem => Section::AnnotationsDirectoryItem, + FragSection::ClassDataItem => Section::ClassDataItem, + // FragSection::HiddenapiClassDataItem => Section::HiddenapiClassDataItem, + } + } +} + +impl Section { + const VARIANT_LIST: &'static [Self] = &[ + Self::HeaderItem, + Self::StringIdItem, + Self::TypeIdItem, + Self::ProtoIdItem, + Self::FieldIdItem, + Self::MethodIdItem, + Self::ClassDefItem, + Self::CallSiteIdItem, + Self::MethodHandleItem, + Self::Data, + Self::MapList, + Self::TypeList, + Self::AnnotationSetRefList, + Self::AnnotationSetItem, + Self::CodeItem, + Self::StringDataItem, + Self::DebugInfoItem, + Self::AnnotationItem, + Self::EncodedArrayItem, + Self::AnnotationsDirectoryItem, + Self::ClassDataItem, // must be last because contains offsets in Uleb, + // so size change when linking ! + Self::HiddenapiClassDataItem, + ]; + + fn get_index(&self) -> usize { + match self { + Self::HeaderItem => 0, + Self::StringIdItem => 1, + Self::TypeIdItem => 2, + Self::ProtoIdItem => 3, + Self::FieldIdItem => 4, + Self::MethodIdItem => 5, + Self::ClassDefItem => 6, + Self::CallSiteIdItem => 7, + Self::MethodHandleItem => 8, + Self::Data => 9, + Self::MapList => 10, + Self::TypeList => 11, + Self::AnnotationSetRefList => 12, + Self::AnnotationSetItem => 13, + Self::CodeItem => 14, + Self::StringDataItem => 15, + Self::DebugInfoItem => 16, + Self::AnnotationItem => 17, + Self::EncodedArrayItem => 18, + Self::AnnotationsDirectoryItem => 19, + Self::ClassDataItem => 20, + Self::HiddenapiClassDataItem => 21, + } + } + + fn get_default_elt_size(&self) -> Option { + match self { + Self::HeaderItem => Some(0x70), + Self::StringIdItem => Some(4), + Self::TypeIdItem => Some(4), + Self::ProtoIdItem => Some(0xc), + Self::FieldIdItem => Some(8), + Self::MethodIdItem => Some(8), + Self::ClassDefItem => Some(0x20), + Self::CallSiteIdItem => Some(4), + Self::MethodHandleItem => Some(8), + Self::Data => panic!("element cannot be inserted in data dirctly"), + Self::MapList => None, + Self::TypeList => None, + Self::AnnotationSetRefList => None, + Self::AnnotationSetItem => None, + Self::ClassDataItem => None, + Self::CodeItem => None, + Self::StringDataItem => None, + Self::DebugInfoItem => None, + Self::AnnotationItem => None, + Self::EncodedArrayItem => None, + Self::AnnotationsDirectoryItem => None, + Self::HiddenapiClassDataItem => None, + } + } + + fn get_elt_size(&self, default_size: Option) -> usize { + let fixed_size = self.get_default_elt_size(); + if let (Some(fixed_size), Some(default_size)) = (fixed_size, default_size) { + if fixed_size == default_size { + default_size + } else { + panic!( + "Element in {:?} have a size of {}, not {}", + self, fixed_size, default_size + ) + } + } else { + fixed_size.or(default_size).unwrap_or_else(|| { + panic!( + "Element of {:?} don't have a fixed size, you need to provide one", + self + ) + }) + } + } + + /// Return the previous section if it exist. + fn prev(&self) -> Option { + match self { + Self::HeaderItem => None, + Self::StringIdItem => Some(Self::HeaderItem), + Self::TypeIdItem => Some(Self::StringIdItem), + Self::ProtoIdItem => Some(Self::TypeIdItem), + Self::FieldIdItem => Some(Self::ProtoIdItem), + Self::MethodIdItem => Some(Self::FieldIdItem), + Self::ClassDefItem => Some(Self::MethodIdItem), + Self::CallSiteIdItem => Some(Self::ClassDefItem), + Self::MethodHandleItem => Some(Self::CallSiteIdItem), + Self::Data => Some(Self::MethodHandleItem), + Self::MapList => Some(Self::MethodHandleItem), // Data is just an indicator + Self::TypeList => Some(Self::MapList), + Self::AnnotationSetRefList => Some(Self::TypeList), + Self::AnnotationSetItem => Some(Self::AnnotationSetRefList), + Self::CodeItem => Some(Self::AnnotationSetItem), + Self::StringDataItem => Some(Self::CodeItem), + Self::DebugInfoItem => Some(Self::StringDataItem), + Self::AnnotationItem => Some(Self::DebugInfoItem), + Self::EncodedArrayItem => Some(Self::AnnotationItem), + Self::AnnotationsDirectoryItem => Some(Self::EncodedArrayItem), + Self::ClassDataItem => Some(Self::AnnotationsDirectoryItem), + Self::HiddenapiClassDataItem => Some(Self::ClassDataItem), + } + } + + fn get_map_item_type(&self) -> MapItemType { + match self { + Self::HeaderItem => MapItemType::HeaderItem, + Self::StringIdItem => MapItemType::StringIdItem, + Self::TypeIdItem => MapItemType::TypeIdItem, + Self::ProtoIdItem => MapItemType::ProtoIdItem, + Self::FieldIdItem => MapItemType::FieldIdItem, + Self::MethodIdItem => MapItemType::MethodIdItem, + Self::ClassDefItem => MapItemType::ClassDefItem, + Self::CallSiteIdItem => MapItemType::CallSiteIdItem, + Self::MethodHandleItem => MapItemType::MethodHandleItem, + Self::Data => panic!("Data is not a MatItemType"), + Self::MapList => MapItemType::MapList, + Self::TypeList => MapItemType::TypeList, + Self::AnnotationSetRefList => MapItemType::AnnotationSetRefList, + Self::AnnotationSetItem => MapItemType::AnnotationSetItem, + Self::CodeItem => MapItemType::CodeItem, + Self::StringDataItem => MapItemType::StringDataItem, + Self::DebugInfoItem => MapItemType::DebugInfoItem, + Self::AnnotationItem => MapItemType::AnnotationItem, + Self::EncodedArrayItem => MapItemType::EncodedArrayItem, + Self::AnnotationsDirectoryItem => MapItemType::AnnotationsDirectoryItem, + Self::ClassDataItem => MapItemType::ClassDataItem, + Self::HiddenapiClassDataItem => MapItemType::HiddenapiClassDataItem, + } + } + + /// Return the alignment of the item in byte. + fn get_item_alignment(&self) -> u32 { + match self { + Self::HeaderItem => 4, + Self::StringIdItem => 4, + Self::TypeIdItem => 4, + Self::ProtoIdItem => 4, + Self::FieldIdItem => 4, + Self::MethodIdItem => 4, + Self::ClassDefItem => 4, + Self::CallSiteIdItem => 1, + Self::MethodHandleItem => 4, + Self::Data => 1, + Self::MapList => 4, + Self::TypeList => 4, + Self::AnnotationSetRefList => 4, + Self::AnnotationSetItem => 4, + Self::CodeItem => 4, + Self::StringDataItem => 1, + Self::DebugInfoItem => 1, + Self::AnnotationItem => 1, + Self::EncodedArrayItem => 1, + Self::AnnotationsDirectoryItem => 4, + Self::ClassDataItem => 1, + Self::HiddenapiClassDataItem => 1, + } + } + + fn is_data(&self) -> bool { + matches!(self, Self::Data) + } +} + +#[derive(Debug, Default, Clone)] +struct SectionManager { + sizes: [u32; Self::NB_SECTION], + nb_elt: [usize; Self::NB_SECTION], +} + +impl From for SectionManager { + fn from(frag_sections: FragSectionManager) -> Self { + let mut section_manager = Self::default(); + for frag_section in FragSection::VARIANT_LIST { + let section: Section = frag_section.into(); + section_manager.nb_elt[section.get_index()] += frag_sections.get_nb_elt(*frag_section); + // Noticed the "aligned" + section_manager.sizes[section.get_index()] = section_manager.get_aligned_size(section) + + frag_sections.get_aligned_size(*frag_section); + } + section_manager + } +} + +impl SectionManager { + const NB_SECTION: usize = 22; + + fn add_elt(&mut self, section: Section, size: Option) { + if section.is_data() { + panic!("Cannot add element directly in section data"); + } + while self.sizes[section.get_index()] % section.get_item_alignment() != 0 { + self.sizes[section.get_index()] += 1; + } + self.sizes[section.get_index()] += section.get_elt_size(size) as u32; + self.nb_elt[section.get_index()] += 1; + } + + /// Add `nb` element in `section`. The element must be of fixed size (the layout of the file + /// ensure the element of fixed size are always aligned). + fn add_multiple_elt(&mut self, section: Section, nb: usize) { + if section.is_data() { + panic!("Cannot add element directly in section data"); + } + let size = if let Some(size) = section.get_default_elt_size() { + size + } else { + panic!( + "Cannot add multiple elements at the same time for element that don't \ + have a fixed size." + ); + }; + self.sizes[section.get_index()] += (size * nb) as u32; + self.nb_elt[section.get_index()] += nb; + } + + fn incr_section_size(&mut self, section: Section, size: usize) { + self.sizes[section.get_index()] += size as u32; + } + + fn get_unaligned_size(&self, section: Section) -> u32 { + if section.is_data() { + self.sizes[section.get_index()..].iter().sum() + } else { + self.sizes[section.get_index()] + } + } + + /// The position of a potential new item in the section considering alignment. + fn get_aligned_size(&self, section: Section) -> u32 { + let mut size = self.get_unaligned_size(section); + while size % section.get_item_alignment() != 0 { + size += 1; + } + size + } + + fn get_nb_elt(&self, section: Section) -> usize { + self.nb_elt[section.get_index()] + } + + /// Display the sections informations. + #[allow(dead_code)] + fn show(&self) { + for section in Section::VARIANT_LIST { + let size = self.get_unaligned_size(*section); + let nb_elt = self.get_nb_elt(*section); + println!("{section:?}: size: 0x{size:x}, nb elt: {nb_elt})"); + } + } + + /// Return the offset of the sections with the sections defined in by this manager. + fn get_offsets(&self) -> SectionOffsets { + let mut offsets = SectionOffsets::default(); + let mut offset = 0; + for section in Section::VARIANT_LIST { + while offset % section.get_item_alignment() != 0 { + offset += 1; + } + offsets.offsets[section.get_index()]; + offset += self.sizes[section.get_index()]; + } + offsets + } +} + +#[derive(Debug, Default, Clone)] +struct SectionOffsets { + offsets: [u32; SectionManager::NB_SECTION], +} + +impl SectionOffsets { + fn get_offset(&self, section: Section) -> u32 { + self.offsets[section.get_index()] + } + + /// Return the offset of the sections **of a fragment**, from the **start of the dex file**. + /// `previous_fragment_sections` is are the merged `FragSectionManager` of the previous + /// fragment. + fn get_fragment_offset( + &self, + previous_fragment_sections: &FragSectionManager, + ) -> SectionOffsets { + let mut offsets = self.clone(); + for frag_section in FragSection::VARIANT_LIST { + let section: Section = frag_section.into(); + // Noticed the "aligned" + offsets.offsets[section.get_index()] += + previous_fragment_sections.get_aligned_size(*frag_section); + } + offsets + } +} From 82c1140eae51ec3ee12688c3452b7c1ad206f4c1 Mon Sep 17 00:00:00 2001 From: Jean-Marie 'Histausse' Mineau Date: Fri, 29 Mar 2024 11:21:31 +0100 Subject: [PATCH 12/14] WIP link offset --- androscalpel/src/dex_fragment.rs | 43 +++++++++++++++++++++++++++----- androscalpel/src/dex_writer.rs | 30 ++++++++++++++++++---- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs index 4a0b0a9..ae29e17 100644 --- a/androscalpel/src/dex_fragment.rs +++ b/androscalpel/src/dex_fragment.rs @@ -1991,7 +1991,7 @@ impl DexFragment { } else { bail!("Fragment cannot be linked without the frag index (see DexFragment::frag_index doc)") }; - self.link_state.start_linking_idx()?; + self.link_state = self.link_state.start_linking_idx()?; let string_reindex = Vec::with_capacity(self.strings.len()); // TODO: considering we have the map, this can be simplified a lot let mut global_idx = 0; @@ -2419,6 +2419,11 @@ impl DexFragment { pub fn get_interfaces(&self) -> &Vec { &self.interfaces } + + pub fn link_offsets_but_class_data(&mut self, offsets: &SectionOffsets) -> Result<()> { + self.link_state = self.link_state.start_linking_offset()?; + Ok(()) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -2627,25 +2632,51 @@ enum FragLinkState { //code_item_relocation: HashMap, //debug_info_item_relocation: HashMap, annotation_item_relocation: HashMap, - encoded_array_relocation: HashMap, // TODO only one? + encoded_array_relocation: HashMap, + }, + /// The index used in the fragment are linked and offset are in the process of beeing linked. + LinkingOffset { + // Not relocation: the code are entirely generated at link id time + //code_item_relocation: HashMap, + //debug_info_item_relocation: HashMap, + annotation_item_relocation: HashMap, + encoded_array_relocation: HashMap, }, } impl FragLinkState { - fn start_linking_idx(&mut self) -> Result<()> { + fn start_linking_idx(self) -> Result { match self { Self::Unlinked => { - *self = Self::LinkedIdx { + Ok(Self::LinkedIdx { // code_item_relocation: HashMap::::new(), // debug_info_item_relocation: HashMap::::new(), annotation_item_relocation: HashMap::::new(), encoded_array_relocation: HashMap::::new(), - }; - Ok(()) + }) } _ => { bail!("Cannot link the idx in a fragment that is already linked"); } } } + + fn start_linking_offset(self) -> Result { + match self { + Self::LinkedIdx { + annotation_item_relocation, + encoded_array_relocation, + } => { + Ok(Self::LinkingOffset { + // code_item_relocation: HashMap::::new(), + // debug_info_item_relocation: HashMap::::new(), + annotation_item_relocation, + encoded_array_relocation, + }) + } + _ => { + bail!("Cannot link the offsets in a fragment that have already linked offset of not linked ids"); + } + } + } } diff --git a/androscalpel/src/dex_writer.rs b/androscalpel/src/dex_writer.rs index 862a8eb..fbae656 100644 --- a/androscalpel/src/dex_writer.rs +++ b/androscalpel/src/dex_writer.rs @@ -167,6 +167,8 @@ impl<'a> DexWriter<'a> { // collect section offsets // The type are quite uggly but I'm behind on time let mut fragment_section_manager = FragSectionManager::default(); + // DexFragment and the merged fragment section managers of the previous fragments + // (excluding the current fragment) let fragments_in_file: Vec<(FragSectionManager, DexFragment)> = fragments_in_file .into_iter() .map(|fragment| { @@ -228,8 +230,8 @@ impl<'a> DexWriter<'a> { Ok(ty_list) }) .collect::>()?; + section_manager.add_elt(Section::MapList, Some(4)); - let offsets = section_manager.get_offsets(); let map_item_size = 12; /* = MapItem { type_: MapItemType::HeaderItem, unused: 0, @@ -237,6 +239,15 @@ impl<'a> DexWriter<'a> { offset: 0, } .size(); */ + for section in Section::VARIANT_LIST { + if !section.is_data() && section_manager.get_nb_elt(*section) != 0 { + section_manager.incr_section_size(Section::MapList, map_item_size); + } + } + let offsets = section_manager.get_offsets(); + // Sections are computed (except for class_data_item sizes, but those are at the end of the + // file so it does not affect the map_list) + let mut map_list = MapList::default(); for section in Section::VARIANT_LIST { if !section.is_data() && section_manager.get_nb_elt(*section) != 0 { @@ -250,9 +261,18 @@ impl<'a> DexWriter<'a> { } } - // TODO: link en correct data section + // link everything but class_def_item.class_data_off and collect the offsets of the + // sections of the fragments + let fragments_in_file: Vec<(SectionOffsets, DexFragment)> = fragments_in_file + .into_iter() + .map(|(frag_section_manager, fragment)| { + let fragment_offsets = offsets.get_fragment_offset(&frag_section_manager); + fragment.link_offsets_but_class_data(&fragment_offsets); + todo!() + }) + .collect(); - // TODO: link everything else + // TODO: class_def_item.class_data_off // TODO: serialize @@ -324,7 +344,7 @@ impl<'a> DexIndex<'a> { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum Section { +pub(crate) enum Section { HeaderItem, StringIdItem, TypeIdItem, @@ -662,7 +682,7 @@ impl SectionManager { } #[derive(Debug, Default, Clone)] -struct SectionOffsets { +pub(crate) struct SectionOffsets { offsets: [u32; SectionManager::NB_SECTION], } From a6cff225922b01a4e3fca1b5e9c97e88429bc9b0 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Fri, 29 Mar 2024 16:37:30 +0100 Subject: [PATCH 13/14] WIP link offset class def --- androscalpel/src/dex_fragment.rs | 48 ++++++++++++++++++++++++++++++-- androscalpel/src/dex_writer.rs | 2 +- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs index ae29e17..e35d5b5 100644 --- a/androscalpel/src/dex_fragment.rs +++ b/androscalpel/src/dex_fragment.rs @@ -2088,8 +2088,13 @@ impl DexFragment { fn link_id_class_def(&mut self, string_reindex: &[u32], type_reindex: &[u32]) { self.class_def.class_idx = type_reindex[self.class_def.class_idx as usize]; - self.class_def.superclass_idx = type_reindex[self.class_def.superclass_idx as usize]; - self.class_def.source_file_idx = string_reindex[self.class_def.source_file_idx as usize]; + if self.class_def.superclass_idx != NO_INDEX.0 { + self.class_def.superclass_idx = type_reindex[self.class_def.superclass_idx as usize]; + } + if self.class_def.source_file_idx != NO_INDEX.0 { + self.class_def.source_file_idx = + string_reindex[self.class_def.source_file_idx as usize]; + } } fn link_id_method_handle(&mut self, field_reindex: &[u16], method_reindex: &[u16]) { @@ -2420,8 +2425,45 @@ impl DexFragment { &self.interfaces } - pub fn link_offsets_but_class_data(&mut self, offsets: &SectionOffsets) -> Result<()> { + /// Link the offsets in the fragment except for the class_def_item.class_data_off + /// that needs the size of the linked class_data_items of the previous fragments. + pub fn link_offsets_but_class_data( + &mut self, + offsets: &SectionOffsets, + type_lists: &HashMap, u32>, + ) -> Result<()> { self.link_state = self.link_state.start_linking_offset()?; + self.link_off_call_site_ids(offsets); + self.link_off_class_def(offsets, type_lists)?; + Ok(()) + } + + fn link_off_call_site_ids(&mut self, offsets: &SectionOffsets) { + for id in &mut self.call_site_ids { + id.call_site_off += offsets.get_offset(Section::EncodedArrayItem); + } + } + + fn link_off_class_def( + &mut self, + offsets: &SectionOffsets, + type_lists: &HashMap, u32>, + ) -> Result<()> { + if self.class_def.annotations_off != 0 { + self.class_def.annotations_off += + offsets.get_offset(Section::AnnotationsDirectoryItem) - 1; + } + if self.class_def.static_values_off != 0 { + self.class_def.static_values_off += offsets.get_offset(Section::EncodedArrayItem) - 1; + } + if !self.interfaces.is_empty() { + self.class_def.interfaces_off = offsets.get_offset(Section::TypeList) + + type_lists.get(&self.interfaces).ok_or(anyhow!( + "Interface list {:?} not found in type list", + self.interfaces + ))?; + } + // class data offset not set here Ok(()) } } diff --git a/androscalpel/src/dex_writer.rs b/androscalpel/src/dex_writer.rs index fbae656..58a6737 100644 --- a/androscalpel/src/dex_writer.rs +++ b/androscalpel/src/dex_writer.rs @@ -687,7 +687,7 @@ pub(crate) struct SectionOffsets { } impl SectionOffsets { - fn get_offset(&self, section: Section) -> u32 { + pub fn get_offset(&self, section: Section) -> u32 { self.offsets[section.get_index()] } From 68c6e3cccf7e99f2f4c94b5d28e1ac64327788d2 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Fri, 29 Mar 2024 17:27:58 +0100 Subject: [PATCH 14/14] WIP link annotation offset --- androscalpel/src/dex_fragment.rs | 102 ++++++++++++++++++++++++++--- androscalpel/src/dex_writer_old.rs | 1 - 2 files changed, 93 insertions(+), 10 deletions(-) diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs index e35d5b5..fa24ff6 100644 --- a/androscalpel/src/dex_fragment.rs +++ b/androscalpel/src/dex_fragment.rs @@ -389,8 +389,7 @@ impl DexFragment { annotation_set.entries.push(AnnotationOffItem { annotation_off: self .section_manager - .get_aligned_size(FragSection::AnnotationItem) - + 1, + .get_aligned_size(FragSection::AnnotationItem), }); let item = AnnotationItem { @@ -468,8 +467,7 @@ impl DexFragment { annotation_set.entries.push(AnnotationOffItem { annotation_off: self .section_manager - .get_aligned_size(FragSection::AnnotationItem) - + 1, + .get_aligned_size(FragSection::AnnotationItem), }); let item = AnnotationItem { @@ -1843,8 +1841,7 @@ impl DexFragment { annotation_set.entries.push(AnnotationOffItem { annotation_off: self .section_manager - .get_aligned_size(FragSection::AnnotationItem) - + 1, + .get_aligned_size(FragSection::AnnotationItem), }); let item = AnnotationItem { @@ -2433,15 +2430,36 @@ impl DexFragment { type_lists: &HashMap, u32>, ) -> Result<()> { self.link_state = self.link_state.start_linking_offset()?; - self.link_off_call_site_ids(offsets); + self.link_off_call_site_ids(offsets)?; self.link_off_class_def(offsets, type_lists)?; + self.link_off_code(offsets); + self.link_off_annotations(offsets)?; Ok(()) } - fn link_off_call_site_ids(&mut self, offsets: &SectionOffsets) { + fn link_off_call_site_ids(&mut self, offsets: &SectionOffsets) -> Result<()> { + let mut encoded_array_relocation = if let FragLinkState::LinkingOffset { + encoded_array_relocation, + .. + } = self.link_state + { + encoded_array_relocation + } else { + panic!( + "link_off_call_site_ids() should only be called by link_offsets_but_class_data()" + ); + }; for id in &mut self.call_site_ids { - id.call_site_off += offsets.get_offset(Section::EncodedArrayItem); + id.call_site_off = offsets.get_offset(Section::EncodedArrayItem) + + encoded_array_relocation + .get(&id.call_site_off) + .ok_or(anyhow!( + "Could not found the new local offset of array \ + previously located at local offset 0x{:x}", + id.call_site_off + ))?; } + Ok(()) } fn link_off_class_def( @@ -2466,6 +2484,72 @@ impl DexFragment { // class data offset not set here Ok(()) } + + fn link_off_code(&mut self, offsets: &SectionOffsets) { + for code in &mut self.code_items { + if code.debug_info_off != 0 { + code.debug_info_off += offsets.get_offset(Section::DebugInfoItem) - 1; + } + } + } + + fn link_off_annotations(&mut self, offsets: &SectionOffsets) -> Result<()> { + let mut annotation_item_relocation = if let FragLinkState::LinkingOffset { + annotation_item_relocation, + .. + } = self.link_state + { + annotation_item_relocation + } else { + panic!("link_off_annotations() should only be called by link_offsets_but_class_data()"); + }; + if let Some(annotation) = self.annotations_directory_item { + if annotation.class_annotations_off != 0 { + annotation.class_annotations_off += + offsets.get_offset(Section::AnnotationSetItem) - 1; + } + for field_annotation in annotation.field_annotations.iter_mut() { + if field_annotation.annotations_off != 0 { + field_annotation.annotations_off += + offsets.get_offset(Section::AnnotationSetItem) - 1; + } + } + for method_annotation in annotation.method_annotations.iter_mut() { + if method_annotation.annotations_off != 0 { + method_annotation.annotations_off += + offsets.get_offset(Section::AnnotationSetItem) - 1; + } + } + for parameter_annotation in annotation.parameter_annotations.iter_mut() { + if parameter_annotation.annotations_off != 0 { + parameter_annotation.annotations_off += + offsets.get_offset(Section::AnnotationSetRefList) - 1; + } + } + } + for annotation_set in self.annotation_set_items.iter_mut() { + for entry in annotation_set.entries.iter_mut() { + entry.annotation_off = annotation_item_relocation + .get(&entry.annotation_off) + .ok_or(anyhow!( + "Could not found new local offset for annotation with previous\ + local offset 0x{:x}", + entry.annotation_off + ))? + + offsets.get_offset(Section::AnnotationItem); + } + } + for list in self.annotation_set_lists.iter_mut() { + for annotation in list.list.iter_mut() { + if annotation.annotations_off != 0 { + annotation.annotations_off += + offsets.get_offset(Section::AnnotationSetItem) - 1; + } + } + } + + Ok(()) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/androscalpel/src/dex_writer_old.rs b/androscalpel/src/dex_writer_old.rs index f967970..a54c3f9 100644 --- a/androscalpel/src/dex_writer_old.rs +++ b/androscalpel/src/dex_writer_old.rs @@ -2568,7 +2568,6 @@ impl DexWriter { for annotation_set in self.annotation_set_items.iter_mut() { for entry in annotation_set.entries.iter_mut() { entry.annotation_off += self.section_manager.get_offset(Section::AnnotationItem); - // -1 now when linking frag } } for list in self.annotation_set_lists.iter_mut() {