add annotations to generated dex
This commit is contained in:
parent
53d321c7fe
commit
bd22b7990b
8 changed files with 599 additions and 38 deletions
89
TODO.md
89
TODO.md
|
|
@ -2,7 +2,94 @@
|
||||||
- generate .dex
|
- generate .dex
|
||||||
- code
|
- code
|
||||||
- edditable code format
|
- edditable code format
|
||||||
|
- json serialize with serde
|
||||||
- sanity checks
|
- sanity checks
|
||||||
- tests
|
- 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
|
- 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<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
T: IntoIterator<Item = (&'a K, &'a V)>,
|
||||||
|
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<T, D::Error>
|
||||||
|
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<KeyItem, ValueItem>,
|
||||||
|
#[serde(with = "vectorize")]
|
||||||
|
map2: HashMap<KeyItem, ValueItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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::<ToSerialize>(&serialized).unwrap(),
|
||||||
|
value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`cargo add serde -F derive && cargo add serde_json`
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ impl Apk {
|
||||||
let annotations_directory = if class_item.annotations_off == 0 {
|
let annotations_directory = if class_item.annotations_off == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(dex.get_struct_at_offset::<AnnotationDirectoryItem>(class_item.annotations_off)?)
|
Some(dex.get_struct_at_offset::<AnnotationsDirectoryItem>(class_item.annotations_off)?)
|
||||||
};
|
};
|
||||||
let mut annotations = vec![];
|
let mut annotations = vec![];
|
||||||
if let Some(annotations_directory) = &annotations_directory {
|
if let Some(annotations_directory) = &annotations_directory {
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,27 @@ impl Class {
|
||||||
.any(|field| field.value.is_some())
|
.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.
|
/// Return the binary representation of access flags.
|
||||||
pub fn get_raw_access_flags(&self) -> u32 {
|
pub fn get_raw_access_flags(&self) -> u32 {
|
||||||
let mut flags = 0u32;
|
let mut flags = 0u32;
|
||||||
|
|
|
||||||
|
|
@ -39,12 +39,6 @@ pub struct DexWriter {
|
||||||
/// index in the `class_defs` section.
|
/// index in the `class_defs` section.
|
||||||
class_defs: HashMap<IdType, (Class, usize)>,
|
class_defs: HashMap<IdType, (Class, usize)>,
|
||||||
// call_site_ids: // TODO: parsing code insns
|
// 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.
|
/// A struct that keep track of sections size, nb elements and offsets.
|
||||||
section_manager: SectionManager,
|
section_manager: SectionManager,
|
||||||
/// The string_data section. Once populated, the elements must be sorted according to the spec.
|
/// 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)>,
|
type_lists_with_offset: Vec<(TypeList, u32)>,
|
||||||
/// The encoded_array_items section.
|
/// The encoded_array_items section.
|
||||||
encoded_array_items: Vec<EncodedArrayItem>,
|
encoded_array_items: Vec<EncodedArrayItem>,
|
||||||
|
/// The annotations_directory_item section.
|
||||||
|
annotations_directory_items: Vec<AnnotationsDirectoryItem>,
|
||||||
|
/// The annotation_set_item section.
|
||||||
|
annotation_set_items: Vec<AnnotationSetItem>,
|
||||||
|
/// The annotation item section.
|
||||||
|
annotation_items: Vec<AnnotationItem>,
|
||||||
|
/// The annotation_set_ref_list section.
|
||||||
|
annotation_set_lists: Vec<AnnotationSetRefList>,
|
||||||
/// The method_handles section.
|
/// The method_handles section.
|
||||||
method_handles: Vec<MethodHandleItem>,
|
method_handles: Vec<MethodHandleItem>,
|
||||||
/// The code_items sections.
|
/// The code_items sections.
|
||||||
|
|
@ -130,6 +132,10 @@ impl Default for DexWriter {
|
||||||
encoded_array_items: vec![],
|
encoded_array_items: vec![],
|
||||||
method_handles: vec![],
|
method_handles: vec![],
|
||||||
code_items: 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert annotation associated to a class.
|
||||||
|
///
|
||||||
|
|
||||||
/// Insert a class_data_item in the class_data section (in data).
|
/// Insert a class_data_item in the class_data section (in data).
|
||||||
///
|
///
|
||||||
/// # Note
|
/// # 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
|
/// 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
|
/// 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
|
/// to distinguish the offset of the first item (equal to zero before linking) and the value
|
||||||
/// 0 used to indicate an abscence of code.
|
/// 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_id: &IdType) -> Result<()> {
|
||||||
let mut data = ClassDataItem::default();
|
let mut data = ClassDataItem::default();
|
||||||
let (class, _) = self.class_defs.get(class_id).unwrap();
|
let (class, _) = self.class_defs.get(class_id).unwrap();
|
||||||
|
|
@ -685,7 +694,9 @@ impl DexWriter {
|
||||||
}
|
}
|
||||||
Ok(EncodedValue::Array(EncodedArray { values }))
|
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::Null(DexNull) => Ok(EncodedValue::Null),
|
||||||
DexValue::Boolean(DexBoolean(val)) => Ok(EncodedValue::Boolean(*val)),
|
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<EncodedAnnotation> {
|
||||||
|
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
|
/// Insert a class_def_item in the class_defs section **and** the other struct that needs to be
|
||||||
/// generated on the fly.
|
/// generated on the fly.
|
||||||
///
|
///
|
||||||
|
|
@ -748,6 +1102,16 @@ impl DexWriter {
|
||||||
/// The class_defs section **MUST** be sorted by inheritance dependencies (parents classes and
|
/// 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
|
/// interfaces must appear **before** child classes). Accordingly, this method must be invoked
|
||||||
/// in the right order.
|
/// 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<()> {
|
fn insert_class_def_item(&mut self, class_id: &IdType) -> Result<()> {
|
||||||
let idx = self.class_defs_list.len();
|
let idx = self.class_defs_list.len();
|
||||||
self.class_defs
|
self.class_defs
|
||||||
|
|
@ -757,7 +1121,7 @@ impl DexWriter {
|
||||||
let class_data_off = if class.has_data_item() {
|
let class_data_off = if class.has_data_item() {
|
||||||
let class_data_off = self.section_manager.get_size(Section::ClassDataItem);
|
let class_data_off = self.section_manager.get_size(Section::ClassDataItem);
|
||||||
self.insert_class_data_item(class_id)?;
|
self.insert_class_data_item(class_id)?;
|
||||||
class_data_off
|
class_data_off + 1
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
@ -767,7 +1131,17 @@ impl DexWriter {
|
||||||
let static_values_off = if class.has_static_values_array() {
|
let static_values_off = if class.has_static_values_array() {
|
||||||
let static_values_off = self.section_manager.get_size(Section::EncodedArrayItem);
|
let static_values_off = self.section_manager.get_size(Section::EncodedArrayItem);
|
||||||
self.insert_class_static_values(class_id)?;
|
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 {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
@ -799,7 +1173,7 @@ impl DexWriter {
|
||||||
NO_INDEX.0
|
NO_INDEX.0
|
||||||
},
|
},
|
||||||
|
|
||||||
annotations_off: 0, // TODO
|
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
|
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
|
static_values_off, // need relinking once offset of encoded_array section is known
|
||||||
});
|
});
|
||||||
|
|
@ -960,10 +1334,11 @@ impl DexWriter {
|
||||||
/// Linking can only occur once all sections are entirelly generated.
|
/// Linking can only occur once all sections are entirelly generated.
|
||||||
fn link_class_data_occurences(&mut self) -> Result<()> {
|
fn link_class_data_occurences(&mut self) -> Result<()> {
|
||||||
debug!("Link the class_data_item entries in class_def_items");
|
debug!("Link the class_data_item entries in class_def_items");
|
||||||
for (class, idx) in self.class_defs.values() {
|
for class_def in self.class_defs_list.iter_mut() {
|
||||||
if class.has_data_item() {
|
// prelink value is set to offset in the section + 1 (to distinguish with 0)
|
||||||
self.class_defs_list[*idx].class_data_off +=
|
if class_def.class_data_off != 0 {
|
||||||
self.section_manager.get_offset(Section::ClassDataItem);
|
class_def.class_data_off +=
|
||||||
|
self.section_manager.get_offset(Section::ClassDataItem) - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -976,16 +1351,20 @@ impl DexWriter {
|
||||||
/// Linking can only occur once all sections are entirelly generated.
|
/// Linking can only occur once all sections are entirelly generated.
|
||||||
fn link_static_values(&mut self) -> Result<()> {
|
fn link_static_values(&mut self) -> Result<()> {
|
||||||
debug!("Link the static_values entries in class_def_items");
|
debug!("Link the static_values entries in class_def_items");
|
||||||
for (class, idx) in self.class_defs.values() {
|
for class_def in self.class_defs_list.iter_mut() {
|
||||||
if class.has_static_values_array() {
|
if class_def.static_values_off != 0 {
|
||||||
self.class_defs_list[*idx].class_data_off +=
|
class_def.static_values_off +=
|
||||||
self.section_manager.get_offset(Section::EncodedArrayItem);
|
self.section_manager.get_offset(Section::EncodedArrayItem) - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Link the offsets of code item in class_data_items.
|
/// 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<()> {
|
fn link_code_item(&mut self) -> Result<()> {
|
||||||
debug!("Link the code_item entries in class_data_items");
|
debug!("Link the code_item entries in class_data_items");
|
||||||
for data in &mut self.class_data_list {
|
for data in &mut self.class_data_list {
|
||||||
|
|
@ -1003,8 +1382,55 @@ impl DexWriter {
|
||||||
Ok(())
|
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<()> {
|
fn write_dex_file(&mut self, writer: &mut dyn Write) -> Result<()> {
|
||||||
// TODO: SPLIT THIS IN METHODS !!!
|
|
||||||
self.section_manager.reset();
|
self.section_manager.reset();
|
||||||
self.section_manager.add_elt(Section::HeaderItem, None);
|
self.section_manager.add_elt(Section::HeaderItem, None);
|
||||||
|
|
||||||
|
|
@ -1027,6 +1453,7 @@ impl DexWriter {
|
||||||
self.link_class_data_occurences()?;
|
self.link_class_data_occurences()?;
|
||||||
self.link_static_values()?;
|
self.link_static_values()?;
|
||||||
self.link_code_item()?;
|
self.link_code_item()?;
|
||||||
|
self.link_annotations()?;
|
||||||
|
|
||||||
debug!("Serialize the dex file");
|
debug!("Serialize the dex file");
|
||||||
// TODO: compute checksum, hash, ect
|
// TODO: compute checksum, hash, ect
|
||||||
|
|
@ -1083,29 +1510,38 @@ impl DexWriter {
|
||||||
self.section_manager.incr_section_size(Section::TypeList, 1);
|
self.section_manager.incr_section_size(Section::TypeList, 1);
|
||||||
offset += 1;
|
offset += 1;
|
||||||
}
|
}
|
||||||
// TODO: AnnotationSetRefList,
|
// AnnotationSetRefList section
|
||||||
// TODO: AnnotationSetItem,
|
for list in &self.annotation_set_lists {
|
||||||
|
list.serialize(writer)?;
|
||||||
|
}
|
||||||
|
// AnnotationSetItem section
|
||||||
|
for set in &self.annotation_set_items {
|
||||||
|
set.serialize(writer)?;
|
||||||
|
}
|
||||||
// ClassDataItem section
|
// ClassDataItem section
|
||||||
for data in &self.class_data_list {
|
for data in &self.class_data_list {
|
||||||
data.serialize(writer)?;
|
data.serialize(writer)?;
|
||||||
}
|
}
|
||||||
// CodeItem section
|
// CodeItem section
|
||||||
for code_item in &self.code_items {
|
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)?
|
code_item.serialize(writer)?
|
||||||
}
|
}
|
||||||
for string in &self.string_data_list {
|
for string in &self.string_data_list {
|
||||||
string.serialize(writer)?;
|
string.serialize(writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: DebugInfoItem,
|
// TODO: DebugInfoItem,
|
||||||
// TODO: AnnotationItem,
|
// AnnotationItem section
|
||||||
|
for annot in &self.annotation_items {
|
||||||
|
annot.serialize(writer)?;
|
||||||
|
}
|
||||||
// TODO: EncodedArrayItem: partialy done
|
// TODO: EncodedArrayItem: partialy done
|
||||||
for array in &self.encoded_array_items {
|
for array in &self.encoded_array_items {
|
||||||
array.serialize(writer)?;
|
array.serialize(writer)?;
|
||||||
}
|
}
|
||||||
// TODO: AnnotationsDirectoryItem,
|
// AnnotationsDirectoryItem section
|
||||||
|
for dir in &self.annotations_directory_items {
|
||||||
|
dir.serialize(writer)?;
|
||||||
|
}
|
||||||
// TODO: HiddenapiClassDataItem,
|
// TODO: HiddenapiClassDataItem,
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -232,4 +232,9 @@ impl Field {
|
||||||
}
|
}
|
||||||
flags
|
flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If the fields has annotations
|
||||||
|
pub fn has_annotations(&self) -> bool {
|
||||||
|
!self.annotations.is_empty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -268,4 +268,13 @@ impl Method {
|
||||||
|
|
||||||
flags
|
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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use std::io::Write;
|
||||||
/// <https://source.android.com/docs/core/runtime/dex-format#referenced-from-class_def_item_1>
|
/// <https://source.android.com/docs/core/runtime/dex-format#referenced-from-class_def_item_1>
|
||||||
/// alignment: 4 bytes
|
/// alignment: 4 bytes
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct AnnotationDirectoryItem {
|
pub struct AnnotationsDirectoryItem {
|
||||||
/// 0 if they are no annotation, else offset of an [`crate::AnnotationSetItem`].
|
/// 0 if they are no annotation, else offset of an [`crate::AnnotationSetItem`].
|
||||||
pub class_annotations_off: u32,
|
pub class_annotations_off: u32,
|
||||||
// pub fields_size: u32,
|
// pub fields_size: u32,
|
||||||
|
|
@ -27,7 +27,7 @@ pub struct AnnotationDirectoryItem {
|
||||||
pub parameter_annotations: Vec<ParameterAnnotation>,
|
pub parameter_annotations: Vec<ParameterAnnotation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnnotationDirectoryItem {
|
impl AnnotationsDirectoryItem {
|
||||||
pub fn fields_size_field(&self) -> u32 {
|
pub fn fields_size_field(&self) -> u32 {
|
||||||
self.field_annotations.len() as 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<()> {
|
fn serialize(&self, output: &mut dyn Write) -> Result<()> {
|
||||||
self.class_annotations_off.serialize(output)?;
|
self.class_annotations_off.serialize(output)?;
|
||||||
self.fields_size_field().serialize(output)?;
|
self.fields_size_field().serialize(output)?;
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
pub fn derive_serializable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
let name = input.ident;
|
let name = input.ident;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue