add field ids to generated dex

This commit is contained in:
Jean-Marie Mineau 2023-12-04 11:38:08 +01:00
parent 57d6b38746
commit da47521993
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
10 changed files with 258 additions and 12 deletions

View file

@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet};
use pyo3::prelude::*;
use crate::{dex_id::IdType, value::DexValue, DexString, IdMethodType};
use crate::{dex_id::IdType, value::DexValue, DexString, IdField, IdMethodType};
/// Annotation with a visibility
#[pyclass]
@ -76,6 +76,11 @@ impl DexAnnotationItem {
pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
self.annotation.get_all_protos()
}
/// Return all field ids referenced in the annotation.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
self.annotation.get_all_field_ids()
}
}
/// An annotation.
@ -142,4 +147,13 @@ impl DexAnnotation {
}
protos
}
/// Return all field ids referenced in the annotation.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
let mut fields = HashSet::new();
for value in self.elements.values() {
fields.extend(value.get_all_field_ids());
}
fields
}
}

View file

@ -215,4 +215,30 @@ impl Class {
}
protos
}
/// Return all fields referenced in the class.
/// This **not** the concatenation of the static and instances fields variable:
/// this also contains reference to fields in other classes used by methods/values
/// in this class.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
let mut fields = HashSet::new();
for (id, field) in &self.static_fields {
fields.insert(id.clone());
fields.extend(field.get_all_field_ids());
}
for (id, field) in &self.instance_fields {
fields.insert(id.clone());
fields.extend(field.get_all_field_ids());
}
for method in self.direct_methods.values() {
fields.extend(method.get_all_field_ids());
}
for method in self.virtual_methods.values() {
fields.extend(method.get_all_field_ids());
}
for annot in &self.annotations {
fields.extend(annot.get_all_field_ids());
}
fields
}
}

View file

@ -4,7 +4,7 @@ use std::collections::HashSet;
use pyo3::prelude::*;
use crate::{DexString, IdMethodType, IdType};
use crate::{DexString, IdField, IdMethodType, IdType};
// TODO: make this easy to edit/manipulate, maybe move to Method
@ -90,4 +90,10 @@ impl Code {
// TODO
HashSet::new()
}
/// Return all field ids referenced in the codes.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
// TODO
HashSet::new()
}
}

View file

@ -468,6 +468,28 @@ impl IdField {
types.insert(self.class_.clone());
types
}
/// Return all field ids referenced in the Id.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
let mut fields = HashSet::new();
fields.insert(self.clone());
fields
}
}
impl Ord for IdField {
fn cmp(&self, other: &Self) -> Ordering {
self.class_
.cmp(&other.class_)
.then(self.name.cmp(&other.name))
.then(self.type_.cmp(&other.type_))
}
}
impl PartialOrd for IdField {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
/// The Id of a method.

View file

@ -3,7 +3,7 @@
use std::collections::HashMap;
use std::io::{Cursor, Write};
use anyhow::{anyhow, Context};
use anyhow::{anyhow, bail, Context};
use crate::Result;
use crate::*;
@ -15,7 +15,7 @@ pub struct DexWriter {
strings: HashMap<DexString, usize>,
type_ids: HashMap<IdType, usize>,
proto_ids: HashMap<IdMethodType, usize>,
_field_ids: HashMap<IdField, usize>,
field_ids: HashMap<IdField, usize>,
_method_ids: HashMap<IdMethod, usize>,
// TODO: composite classes need a struct for storing link data
// class_defs: HashMap<IdMethod, (Class, u32)>,
@ -61,7 +61,7 @@ impl Default for DexWriter {
strings: HashMap::new(),
type_ids: HashMap::new(),
proto_ids: HashMap::new(),
_field_ids: HashMap::new(),
field_ids: HashMap::new(),
_method_ids: HashMap::new(),
}
}
@ -86,16 +86,24 @@ impl DexWriter {
let new_types = class.get_all_types();
/* this means more types than bytes in the file, prbl no gonna append
* and len(type) <= len(string) anyway
let mut future_type_set = new_types.clone();
future_type_set.extend(self.type_ids.keys().cloned());
// TODO: they are ref to type as u16, check
if future_type_set.len() >= u32::MAX as usize {
let mut futur_type_set = new_types.clone();
futur_type_set.extend(self.type_ids.keys().cloned());
// TODO: they are ref to type as u16, so checks?
if futur_type_set.len() >= u32::MAX as usize {
// TODO return structured error to handle this case by generating multiple dex files
bail!("To many types for one dex file");
}
*/
let new_protos = class.get_all_protos();
let mut futur_proto_set = new_protos.clone();
futur_proto_set.extend(self.proto_ids.keys().cloned());
if futur_proto_set.len() >= u16::MAX as usize {
// TODO return structured error to handle this case by generating multiple dex files
bail!("To many prototypes for one dex file");
}
let new_field_ids = class.get_all_field_ids();
for string in new_strings {
self.strings.insert(string, 0);
@ -106,6 +114,9 @@ impl DexWriter {
for proto in new_protos {
self.proto_ids.insert(proto, 0);
}
for field in new_field_ids {
self.field_ids.insert(field, 0);
}
Ok(())
}
@ -209,6 +220,39 @@ impl DexWriter {
offset += list.size();
}
// Sort and generate FieldIdItem
let mut field_ids_list: Vec<IdField> = 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);
section_manager.add_elt(Section::FieldIdItem, None);
}
let field_ids_list: Vec<FieldIdItem> = {
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,
});
}
field_ids_list_aux
};
// Populate map_list
let map_item_size = MapItem {
type_: MapItemType::HeaderItem,
@ -292,7 +336,10 @@ impl DexWriter {
for proto in proto_ids_list {
proto.serialize(writer)?;
}
// TODO: FieldIdItem,
// FieldIdItem section
for field_id in field_ids_list {
field_id.serialize(writer)?;
}
// TODO: MethodIdItem,
// TODO: ClassDefItem,
// TODO: CallSiteIdItem,

View file

@ -157,4 +157,17 @@ impl Field {
}
protos
}
/// Return all field ids referenced in the field.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
let mut fields = HashSet::new();
fields.insert(self.descriptor.clone());
if let Some(value) = &self.value {
fields.extend(value.get_all_field_ids());
}
for annot in &self.annotations {
fields.extend(annot.get_all_field_ids());
}
fields
}
}

View file

@ -4,7 +4,7 @@ use std::collections::HashSet;
use pyo3::prelude::*;
use crate::{Code, DexAnnotationItem, DexString, IdMethod, IdMethodType, IdType};
use crate::{Code, DexAnnotationItem, DexString, IdField, IdMethod, IdMethodType, IdType};
/// Represent a method.
#[pyclass]
@ -160,4 +160,21 @@ impl Method {
}
protos
}
/// Return all field ids referenced in the method.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
let mut fields = HashSet::new();
for annot in &self.annotations {
fields.extend(annot.get_all_field_ids());
}
for param_annots in &self.parameters_annotations {
for annot in param_annots {
fields.extend(annot.get_all_field_ids());
}
}
if let Some(code) = &self.code {
fields.extend(code.get_all_field_ids());
}
fields
}
}

View file

@ -59,6 +59,13 @@ impl StaticPut {
pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
HashSet::new()
}
/// Return all field ids referenced in the handle.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
let mut fields = HashSet::new();
fields.insert(self.0.clone());
fields
}
}
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq)]
@ -97,6 +104,13 @@ impl StaticGet {
pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
HashSet::new()
}
/// Return all field ids referenced in the handle.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
let mut fields = HashSet::new();
fields.insert(self.0.clone());
fields
}
}
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq)]
@ -135,6 +149,13 @@ impl InstancePut {
pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
HashSet::new()
}
/// Return all field ids referenced in the handle.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
let mut fields = HashSet::new();
fields.insert(self.0.clone());
fields
}
}
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq)]
@ -173,6 +194,13 @@ impl InstanceGet {
pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
HashSet::new()
}
/// Return all field ids referenced in the handle.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
let mut fields = HashSet::new();
fields.insert(self.0.clone());
fields
}
}
#[pyclass]
@ -212,6 +240,11 @@ impl InvokeStatic {
pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
self.0.get_all_protos()
}
/// Return all field ids referenced in the handle.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
HashSet::new()
}
}
#[pyclass]
@ -251,6 +284,11 @@ impl InvokeInstance {
pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
self.0.get_all_protos()
}
/// Return all field ids referenced in the handle.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
HashSet::new()
}
}
#[pyclass]
@ -290,6 +328,11 @@ impl InvokeConstructor {
pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
self.0.get_all_protos()
}
/// Return all field ids referenced in the handle.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
HashSet::new()
}
}
#[pyclass]
@ -329,6 +372,11 @@ impl InvokeDirect {
pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
self.0.get_all_protos()
}
/// Return all field ids referenced in the handle.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
HashSet::new()
}
}
#[pyclass]
@ -368,6 +416,11 @@ impl InvokeInterface {
pub fn get_all_protos(&self) -> HashSet<IdMethodType> {
self.0.get_all_protos()
}
/// Return all field ids referenced in the handle.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
HashSet::new()
}
}
impl<'source> FromPyObject<'source> for MethodHandle {
@ -475,4 +528,19 @@ impl MethodHandle {
Self::InvokeInterface(val) => val.get_all_protos(),
}
}
/// Return all field ids referenced in the handle.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
match self {
Self::StaticPut(val) => val.get_all_field_ids(),
Self::StaticGet(val) => val.get_all_field_ids(),
Self::InstancePut(val) => val.get_all_field_ids(),
Self::InstanceGet(val) => val.get_all_field_ids(),
Self::InvokeStatic(val) => val.get_all_field_ids(),
Self::InvokeInstance(val) => val.get_all_field_ids(),
Self::InvokeConstructor(val) => val.get_all_field_ids(),
Self::InvokeDirect(val) => val.get_all_field_ids(),
Self::InvokeInterface(val) => val.get_all_field_ids(),
}
}
}

View file

@ -2,7 +2,7 @@
use std::collections::HashSet;
use crate::{DexString, DexValue, IdMethodType, IdType};
use crate::{DexString, DexValue, IdField, IdMethodType, IdType};
use pyo3::prelude::*;
#[pyclass]
@ -288,4 +288,13 @@ impl DexArray {
}
protos
}
/// Return all field ids referenced in the value.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
let mut fields = HashSet::new();
for val in &self.0 {
fields.extend(val.get_all_field_ids());
}
fields
}
}

View file

@ -172,6 +172,30 @@ impl DexValue {
DexValue::Boolean(_) => HashSet::new(),
}
}
/// Return all field ids referenced in the value.
pub fn get_all_field_ids(&self) -> HashSet<IdField> {
match self {
DexValue::Byte(_) => HashSet::new(),
DexValue::Short(_) => HashSet::new(),
DexValue::Char(_) => HashSet::new(),
DexValue::Int(_) => HashSet::new(),
DexValue::Long(_) => HashSet::new(),
DexValue::Float(_) => HashSet::new(),
DexValue::Double(_) => HashSet::new(),
DexValue::MethodType(_) => HashSet::new(),
DexValue::MethodHandle(val) => val.get_all_field_ids(),
DexValue::String(_) => HashSet::new(),
DexValue::Type(_) => HashSet::new(),
DexValue::Field(val) => val.get_all_field_ids(),
DexValue::Method(_) => HashSet::new(),
DexValue::Enum(_) => HashSet::new(),
DexValue::Array(val) => val.get_all_field_ids(),
DexValue::Annotation(val) => val.get_all_field_ids(),
DexValue::Null(_) => HashSet::new(),
DexValue::Boolean(_) => HashSet::new(),
}
}
}
impl IntoPy<PyObject> for DexValue {