From bd22b7990baedb2674debe9d83865371ab5ee3d0 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Tue, 12 Dec 2023 11:25:33 +0100 Subject: [PATCH] add annotations to generated dex --- TODO.md | 89 +++- androscalpel/src/apk.rs | 2 +- androscalpel/src/class.rs | 21 + androscalpel/src/dex_writer.rs | 498 ++++++++++++++++-- androscalpel/src/field.rs | 5 + androscalpel/src/method.rs | 9 + androscalpel_serializer/src/annotation/mod.rs | 6 +- androscalpel_serializer_derive/src/lib.rs | 7 +- 8 files changed, 599 insertions(+), 38 deletions(-) diff --git a/TODO.md b/TODO.md index 8b91b9e..f081fdf 100644 --- a/TODO.md +++ b/TODO.md @@ -2,7 +2,94 @@ - generate .dex - code - edditable code format +- json serialize with serde - sanity checks - tests -- DexValues will become a problem ? (eg need to clone the vector for the array) +- PyRef - https://source.android.com/docs/core/runtime/dex-format#system-annotation + + + +``` +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::hash::Hash; + +pub mod vectorize { + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use std::iter::FromIterator; + pub fn serialize<'a, T, K, V, S>(target: T, ser: S) -> Result + where + S: Serializer, + T: IntoIterator, + K: Serialize + 'a, + V: Serialize + 'a, + { + let container: Vec<_> = target.into_iter().collect(); + serde::Serialize::serialize(&container, ser) + } + + pub fn deserialize<'de, T, K, V, D>(des: D) -> Result + where + D: Deserializer<'de>, + T: FromIterator<(K, V)>, + K: Deserialize<'de>, + V: Deserialize<'de>, + { + let container: Vec<_> = serde::Deserialize::deserialize(des)?; + Ok(T::from_iter(container.into_iter())) + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] +struct KeyItem { + name: String, + len: u32, +} + +impl KeyItem { + fn new(s: &str) -> Self { + Self { + name: s.into(), + len: s.len() as u32, + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] +enum ValueItem { + String1(String), + String2(String), + Int(u32), +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +struct ToSerialize { + #[serde(with = "vectorize")] + map1: HashMap, + #[serde(with = "vectorize")] + map2: HashMap, +} + +fn main() { + let value = ToSerialize { + map1: HashMap::from([ + (KeyItem::new("plop"), ValueItem::String1("Plop".into())), + (KeyItem::new("plip"), ValueItem::String2("Plip".into())), + (KeyItem::new("lop"), ValueItem::Int(109)), + ]), + map2: HashMap::from([( + KeyItem::new("run"), + ValueItem::String1("Demons run when a good man goes to war".into()), + )]), + }; + let serialized = serde_json::to_string(&value).unwrap(); + println!("{serialized}"); + assert_eq!( + serde_json::from_str::(&serialized).unwrap(), + value + ); +} +``` + +`cargo add serde -F derive && cargo add serde_json` diff --git a/androscalpel/src/apk.rs b/androscalpel/src/apk.rs index 94338c8..7421f2a 100644 --- a/androscalpel/src/apk.rs +++ b/androscalpel/src/apk.rs @@ -85,7 +85,7 @@ impl Apk { let annotations_directory = if class_item.annotations_off == 0 { None } else { - Some(dex.get_struct_at_offset::(class_item.annotations_off)?) + Some(dex.get_struct_at_offset::(class_item.annotations_off)?) }; let mut annotations = vec![]; if let Some(annotations_directory) = &annotations_directory { diff --git a/androscalpel/src/class.rs b/androscalpel/src/class.rs index 3e4adb6..844d151 100644 --- a/androscalpel/src/class.rs +++ b/androscalpel/src/class.rs @@ -306,6 +306,27 @@ impl Class { .any(|field| field.value.is_some()) } + /// If the class or its fields/methods have annotations + pub fn has_annotations(&self) -> bool { + !self.annotations.is_empty() + || self + .static_fields + .values() + .any(|field| field.has_annotations()) + || self + .instance_fields + .values() + .any(|field| field.has_annotations()) + || self + .direct_methods + .values() + .any(|field| field.has_annotations()) + || self + .virtual_methods + .values() + .any(|field| field.has_annotations()) + } + /// Return the binary representation of access flags. pub fn get_raw_access_flags(&self) -> u32 { let mut flags = 0u32; diff --git a/androscalpel/src/dex_writer.rs b/androscalpel/src/dex_writer.rs index 71492c2..0124691 100644 --- a/androscalpel/src/dex_writer.rs +++ b/androscalpel/src/dex_writer.rs @@ -39,12 +39,6 @@ pub struct DexWriter { /// index in the `class_defs` section. class_defs: HashMap, // call_site_ids: // TODO: parsing code insns - // method_handles are not deduplicated nor ordered, so they must be serialized on the fly. - // TODO: other structs in data: - // **map_list**, prbl generate on write - // values - // annotations - // /// 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. @@ -72,6 +66,14 @@ pub struct DexWriter { 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. @@ -130,6 +132,10 @@ impl Default for DexWriter { 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![], } } } @@ -418,16 +424,19 @@ impl DexWriter { Ok(()) } + /// Insert annotation associated to a class. + /// + /// Insert a class_data_item in the class_data section (in data). /// /// # Note /// - /// code_item object are 4 bytes aligns, so code_item_off cannot be odd. + /// 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 code item (equal to zero before linking) and the value - /// 0 used to indicate an abscence of code. + /// 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(); @@ -685,7 +694,9 @@ impl DexWriter { } Ok(EncodedValue::Array(EncodedArray { values })) } - DexValue::Annotation(_) => todo!(), // Ok(EncodedValue::Annotation(todo!())), + 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)), } @@ -740,6 +751,349 @@ impl DexWriter { }) } + 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_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_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_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_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_size(Section::AnnotationSetItem); + self.insert_parameters_annotation_set(method_id, is_direct_method, param_idx)?; + annotation_off + 1 + } else { + 0 + }, // TODO: link + }); + } + + 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_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_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_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_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. /// @@ -748,6 +1102,16 @@ impl DexWriter { /// 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 @@ -757,7 +1121,7 @@ impl DexWriter { let class_data_off = if class.has_data_item() { let class_data_off = self.section_manager.get_size(Section::ClassDataItem); self.insert_class_data_item(class_id)?; - class_data_off + class_data_off + 1 } else { 0 }; @@ -767,7 +1131,17 @@ impl DexWriter { let static_values_off = if class.has_static_values_array() { let static_values_off = self.section_manager.get_size(Section::EncodedArrayItem); self.insert_class_static_values(class_id)?; - static_values_off + 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_size(Section::AnnotationsDirectoryItem); + self.insert_annotations(class_id)?; + annotations_off + 1 } else { 0 }; @@ -799,9 +1173,9 @@ impl DexWriter { NO_INDEX.0 }, - annotations_off: 0, // TODO - 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 + 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(()) @@ -960,10 +1334,11 @@ impl DexWriter { /// Linking can only occur once all sections are entirelly generated. fn link_class_data_occurences(&mut self) -> Result<()> { debug!("Link the class_data_item entries in class_def_items"); - for (class, idx) in self.class_defs.values() { - if class.has_data_item() { - self.class_defs_list[*idx].class_data_off += - self.section_manager.get_offset(Section::ClassDataItem); + for class_def in self.class_defs_list.iter_mut() { + // prelink value is set to offset in the section + 1 (to distinguish with 0) + if class_def.class_data_off != 0 { + class_def.class_data_off += + self.section_manager.get_offset(Section::ClassDataItem) - 1; } } Ok(()) @@ -976,16 +1351,20 @@ impl DexWriter { /// Linking can only occur once all sections are entirelly generated. fn link_static_values(&mut self) -> Result<()> { debug!("Link the static_values entries in class_def_items"); - for (class, idx) in self.class_defs.values() { - if class.has_static_values_array() { - self.class_defs_list[*idx].class_data_off += - self.section_manager.get_offset(Section::EncodedArrayItem); + for class_def in self.class_defs_list.iter_mut() { + if class_def.static_values_off != 0 { + class_def.static_values_off += + self.section_manager.get_offset(Section::EncodedArrayItem) - 1; } } Ok(()) } /// Link the offsets of code item in class_data_items. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. fn link_code_item(&mut self) -> Result<()> { debug!("Link the code_item entries in class_data_items"); for data in &mut self.class_data_list { @@ -1003,8 +1382,55 @@ impl DexWriter { Ok(()) } + /// Link all annotations objects. + /// + /// # Warning + /// + /// Linking can only occur once all sections are entirelly generated. + fn link_annotations(&mut self) -> Result<()> { + 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; + } + } + } + Ok(()) + } + fn write_dex_file(&mut self, writer: &mut dyn Write) -> Result<()> { - // TODO: SPLIT THIS IN METHODS !!! self.section_manager.reset(); self.section_manager.add_elt(Section::HeaderItem, None); @@ -1027,6 +1453,7 @@ impl DexWriter { self.link_class_data_occurences()?; self.link_static_values()?; self.link_code_item()?; + self.link_annotations()?; debug!("Serialize the dex file"); // TODO: compute checksum, hash, ect @@ -1083,29 +1510,38 @@ impl DexWriter { self.section_manager.incr_section_size(Section::TypeList, 1); offset += 1; } - // TODO: AnnotationSetRefList, - // TODO: AnnotationSetItem, + // AnnotationSetRefList section + for list in &self.annotation_set_lists { + list.serialize(writer)?; + } + // AnnotationSetItem section + for set in &self.annotation_set_items { + set.serialize(writer)?; + } // ClassDataItem section for data in &self.class_data_list { data.serialize(writer)?; } // CodeItem section for code_item in &self.code_items { - // TODO: !!! fix the handler stub issue with handler offset that change when the - // type_idx change code_item.serialize(writer)? } for string in &self.string_data_list { string.serialize(writer)?; } - // TODO: DebugInfoItem, - // TODO: AnnotationItem, + // AnnotationItem section + for annot in &self.annotation_items { + annot.serialize(writer)?; + } // TODO: EncodedArrayItem: partialy done for array in &self.encoded_array_items { array.serialize(writer)?; } - // TODO: AnnotationsDirectoryItem, + // AnnotationsDirectoryItem section + for dir in &self.annotations_directory_items { + dir.serialize(writer)?; + } // TODO: HiddenapiClassDataItem, Ok(()) diff --git a/androscalpel/src/field.rs b/androscalpel/src/field.rs index f9fd04a..b7aa377 100644 --- a/androscalpel/src/field.rs +++ b/androscalpel/src/field.rs @@ -232,4 +232,9 @@ impl Field { } flags } + + /// If the fields has annotations + pub fn has_annotations(&self) -> bool { + !self.annotations.is_empty() + } } diff --git a/androscalpel/src/method.rs b/androscalpel/src/method.rs index e575f1a..ac0f45d 100644 --- a/androscalpel/src/method.rs +++ b/androscalpel/src/method.rs @@ -268,4 +268,13 @@ impl Method { flags } + + /// If the fields has annotations + pub fn has_annotations(&self) -> bool { + !self.annotations.is_empty() + || !self + .parameters_annotations + .iter() + .any(|list| !list.is_empty()) + } } diff --git a/androscalpel_serializer/src/annotation/mod.rs b/androscalpel_serializer/src/annotation/mod.rs index 83d8be2..feeeaa0 100644 --- a/androscalpel_serializer/src/annotation/mod.rs +++ b/androscalpel_serializer/src/annotation/mod.rs @@ -10,7 +10,7 @@ use std::io::Write; /// /// alignment: 4 bytes #[derive(Debug, Clone, PartialEq, Eq)] -pub struct AnnotationDirectoryItem { +pub struct AnnotationsDirectoryItem { /// 0 if they are no annotation, else offset of an [`crate::AnnotationSetItem`]. pub class_annotations_off: u32, // pub fields_size: u32, @@ -27,7 +27,7 @@ pub struct AnnotationDirectoryItem { pub parameter_annotations: Vec, } -impl AnnotationDirectoryItem { +impl AnnotationsDirectoryItem { pub fn fields_size_field(&self) -> u32 { self.field_annotations.len() as u32 } @@ -39,7 +39,7 @@ impl AnnotationDirectoryItem { } } -impl Serializable for AnnotationDirectoryItem { +impl Serializable for AnnotationsDirectoryItem { fn serialize(&self, output: &mut dyn Write) -> Result<()> { self.class_annotations_off.serialize(output)?; self.fields_size_field().serialize(output)?; diff --git a/androscalpel_serializer_derive/src/lib.rs b/androscalpel_serializer_derive/src/lib.rs index c700a73..401c612 100644 --- a/androscalpel_serializer_derive/src/lib.rs +++ b/androscalpel_serializer_derive/src/lib.rs @@ -116,7 +116,10 @@ use syn::{ /// ``` /// /// -#[proc_macro_derive(Serializable, attributes(until, prefix, prefix_type, suffix, default_variant))] +#[proc_macro_derive( + Serializable, + attributes(until, prefix, prefix_type, suffix, default_variant) +)] pub fn derive_serializable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); let name = input.ident; @@ -723,7 +726,7 @@ fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream { }, } }); - let mut default_variant = None; + let mut default_variant = None; for v in &data.variants { let v_params = ParamsVariant::parse(&v.attrs); if v_params.default_variant.is_some() && v_params.prefix.is_some() {