diff --git a/androscalpel/src/dex_writer.rs b/androscalpel/src/dex_writer.rs index f52df29..6ad4000 100644 --- a/androscalpel/src/dex_writer.rs +++ b/androscalpel/src/dex_writer.rs @@ -43,7 +43,7 @@ pub struct DexWriter { /// index in the `class_defs` section. class_defs: HashMap, /// The call sites refered to in the bytecode. - call_sites: Vec, + 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. @@ -125,7 +125,7 @@ impl Default for DexWriter { field_ids: HashMap::new(), method_ids: HashMap::new(), class_defs: HashMap::new(), - call_sites: vec![], + call_site_ids: vec![], section_manager: SectionManager::default(), string_data_list: vec![], type_ids_list: vec![], @@ -378,6 +378,7 @@ impl DexWriter { .clone() }; + // Estimate instructions addresses let mut min_addr = 0; let mut max_addr = 0; let mut label_min_max_addrs: HashMap = HashMap::new(); @@ -404,6 +405,7 @@ impl DexWriter { } } } + // Compute instruction size and precise addresses let mut addr = 0; let mut label_addrs = HashMap::new(); let mut goto_sizes = vec![]; @@ -432,6 +434,10 @@ impl DexWriter { _ => 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; @@ -1182,8 +1188,8 @@ impl DexWriter { insns.push(ins); } Instruction::InvokeCustom(ins) => { - let call_site_idx = self.call_sites.len(); - self.call_sites.push(ins.call_site.clone()); + 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); @@ -1205,8 +1211,57 @@ impl DexWriter { addr += ins.size() / 2; insns.push(ins); } - Instruction::Try(_ins) => { - // TODO + 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, + }; + 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(_) => (), _ => { @@ -1224,10 +1279,6 @@ impl DexWriter { } insns.extend(payloads); - // TODO - let mut tries = vec![]; - let mut handlers = None; - let debug_info_off = if code.debug_info.is_empty() { 0 } else { @@ -1238,6 +1289,11 @@ impl DexWriter { 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, @@ -1250,51 +1306,6 @@ impl DexWriter { self.section_manager .add_elt(Section::CodeItem, Some(item.size())); self.code_items.push(item); - /* - let mut tries = vec![]; - let mut local_off = 0; - let mut handler_offsets = HashMap::new(); - let mut handler_list = EncodedCatchHandlerList { list: vec![] }; - for (start_addr, insn_count, (list, catch_all_addr)) in code.tries.iter().cloned() { - let mut handlers = vec![]; - for (ty, addr) in list { - 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(addr); - handlers.push(EncodedTypeAddrPair { type_idx, addr }); - } - let handler = EncodedCatchHandler { - handlers, - catch_all_addr: catch_all_addr.map(Uleb128), - }; - let handler_off = if let Some(handler_off) = handler_offsets.get(&handler).cloned() { - handler_off - } else { - let old_local_off = local_off; - local_off += handler.size() as u16; - handler_list.list.push(handler.clone()); - handler_offsets.insert(handler, old_local_off); - old_local_off - }; - - tries.push(TryItem { - start_addr, - insn_count, - handler_off, - }); - } - for try_ in tries.iter_mut() { - try_.handler_off += handler_list.size_field().size() as u16; - } - let handlers = if tries.is_empty() { - None - } else { - Some(handler_list) - }; - */ Ok(()) } @@ -1597,6 +1608,24 @@ impl DexWriter { 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_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(); @@ -1672,11 +1701,18 @@ impl DexWriter { }); // 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 + 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)?, }; @@ -1712,11 +1748,18 @@ impl DexWriter { }); // 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 + 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)?, }; @@ -1752,11 +1795,18 @@ impl DexWriter { }); // 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 + 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)?, }; @@ -1793,11 +1843,18 @@ impl DexWriter { }); // 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 + 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)?, }; @@ -2173,6 +2230,18 @@ impl DexWriter { 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 of type_lists items in proto_id_items and class_def_items. /// /// # Warning @@ -2338,7 +2407,9 @@ impl DexWriter { self.get_map_list()?; + // 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_type_list_occurences()?; self.link_class_data_occurences(); self.link_static_values(); @@ -2378,7 +2449,11 @@ impl DexWriter { for class_def in &self.class_defs_list { class_def.serialize(writer)?; } - // TODO: CallSiteIdItem, data must be inserted as encoded array item later + // CallSiteIdItem, data are inserted as encoded array item later + for call_site_id in &self.call_site_ids { + call_site_id.serialize(writer)?; + } + // MethodHandleItem section for handle in &self.method_handles { handle.serialize(writer)?; @@ -2428,7 +2503,7 @@ impl DexWriter { for annot in &self.annotation_items { annot.serialize(writer)?; } - // TODO: EncodedArrayItem: partialy done + // EncodedArrayItem section for array in &self.encoded_array_items { array.serialize(writer)?; } diff --git a/androscalpel/src/instructions.rs b/androscalpel/src/instructions.rs index 85cf297..b58afc8 100644 --- a/androscalpel/src/instructions.rs +++ b/androscalpel/src/instructions.rs @@ -6027,7 +6027,7 @@ impl IfLeZ { } } -/// Put the value at arr[idx] in register dest (all values are in registers) +/// Put the value at `arr[idx]` in register dest (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AGet { @@ -6099,7 +6099,7 @@ impl AGet { } } -/// Put the value at arr[idx] in register pair dest (all values are in registers) +/// Put the value at `arr[idx]` in register pair dest (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AGetWide { @@ -6171,7 +6171,7 @@ impl AGetWide { } } -/// Put the reference at arr[idx] in register (all values are in registers) +/// Put the reference at `arr[idx]` in register (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AGetObject { @@ -6243,7 +6243,7 @@ impl AGetObject { } } -/// Put the boolean at arr[idx] in register dest (all values are in registers) +/// Put the boolean at `arr[idx]` in register dest (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AGetBoolean { @@ -6315,7 +6315,7 @@ impl AGetBoolean { } } -/// Put the byte at arr[idx] in register dest (all values are in registers) +/// Put the byte at `arr[idx]` in register dest (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AGetByte { @@ -6387,7 +6387,7 @@ impl AGetByte { } } -/// Put the char at arr[idx] in register dest (all values are in registers) +/// Put the char at `arr[idx]` in register dest (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AGetChar { @@ -6459,7 +6459,7 @@ impl AGetChar { } } -/// Put the short at arr[idx] in register dest (all values are in registers) +/// Put the short at `arr[idx]` in register dest (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AGetShort { @@ -6531,7 +6531,7 @@ impl AGetShort { } } -/// Put the value of register 'from' in arr[idx] (all values are in registers) +/// Put the value of register 'from' in `arr[idx]` (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct APut { @@ -6603,7 +6603,7 @@ impl APut { } } -/// Put the value of the register pair 'from' in arr[idx] (all values are in registers) +/// Put the value of the register pair 'from' in `arr[idx]` (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct APutWide { @@ -6675,7 +6675,7 @@ impl APutWide { } } -/// Put the object reference in 'from' in arr[idx] (all values are in registers) +/// Put the object reference in 'from' in `arr[idx]` (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct APutObject { @@ -6747,7 +6747,7 @@ impl APutObject { } } -/// Put the boolean in 'from' in arr[idx] (all values are in registers) +/// Put the boolean in 'from' in `arr[idx]` (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct APutBoolean { @@ -6819,7 +6819,7 @@ impl APutBoolean { } } -/// Put the byte in 'from' in arr[idx] (all values are in registers) +/// Put the byte in 'from' in `arr[idx]` (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct APutByte { @@ -6891,7 +6891,7 @@ impl APutByte { } } -/// Put the char in 'from' in arr[idx] (all values are in registers) +/// Put the char in 'from' in `arr[idx]` (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct APutChar { @@ -6963,7 +6963,7 @@ impl APutChar { } } -/// Put the short in 'from' in arr[idx] (all values are in registers) +/// Put the short in 'from' in `arr[idx]` (all values are in registers) #[pyclass] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct APutShort { diff --git a/androscalpel_serializer/src/items/class.rs b/androscalpel_serializer/src/items/class.rs index b586284..141f5ca 100644 --- a/androscalpel_serializer/src/items/class.rs +++ b/androscalpel_serializer/src/items/class.rs @@ -21,7 +21,7 @@ pub struct ClassDefItem { pub interfaces_off: u32, /// Index of a [`crate::StringIdItem`] in `string_ids` or [`crate::NO_INDEX`]. pub source_file_idx: u32, - /// 0 if no annotation, else offset to a [`crate::AnnotationDirectoryItem`]. + /// 0 if no annotation, else offset to a [`crate::AnnotationsDirectoryItem`]. pub annotations_off: u32, /// 0 if no data for this class, else offset to a [`crate::ClassDataItem`]. pub class_data_off: u32,