add coded item to generated dex

This commit is contained in:
Jean-Marie Mineau 2023-12-06 10:58:07 +01:00
parent 3468bc0463
commit 53d321c7fe
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
5 changed files with 797 additions and 35 deletions

View file

@ -693,28 +693,27 @@ impl Apk {
handler_off,
} in code_item.tries
{
tries.push((start_addr, insn_count, handler_off));
}
let mut handlers_aux = vec![];
if let Some(EncodedCatchHandlerList { list }) = code_item.handlers {
for EncodedCatchHandler {
let EncodedCatchHandler {
handlers,
catch_all_addr,
} in list
} = code_item
.handlers
.as_ref()
.ok_or(anyhow!(
"Inconsistant code_item: found try blocks but no handler list"
))?
.get_handler_at_offset(handler_off)?;
let catch_all_addr = catch_all_addr.map(|Uleb128(val)| val);
let mut handlers_ = vec![];
for EncodedTypeAddrPair {
type_idx: Uleb128(type_idx),
addr: Uleb128(addr),
} in handlers
{
let mut handlers_ = vec![];
let catch_all_addr = catch_all_addr.map(|Uleb128(val)| val);
for EncodedTypeAddrPair {
type_idx: Uleb128(type_idx),
addr: Uleb128(addr),
} in handlers
{
handlers_.push((Self::get_id_type_from_idx(type_idx as usize, dex)?, addr))
}
handlers_aux.push((handlers_, catch_all_addr));
handlers_.push((Self::get_id_type_from_idx(*type_idx as usize, dex)?, *addr))
}
tries.push((start_addr, insn_count, (handlers_, catch_all_addr)));
}
let handlers = handlers_aux;
Ok(Code {
registers_size: code_item.registers_size,
ins_size: code_item.ins_size,
@ -722,7 +721,6 @@ impl Apk {
debug_info,
insns: code_item.insns,
tries,
handlers,
})
}

View file

@ -38,12 +38,9 @@ pub struct Code {
// TODO: maybe implement as custom OPcode to make me easy to modify?
/// Try blocks
#[pyo3(get, set)]
pub tries: Vec<(u32, u16, u16)>,
pub tries: Vec<(u32, u16, TmpHandlerType)>,
// TODO: currently unusable, juste a mapping ty TryItem
// TODO: maybe implement as custom OPcode to make me easy to modify?
/// The handlers associated to the tries blocks.
#[pyo3(get, set)]
pub handlers: Vec<TmpHandlerType>,
}
#[pymethods]
@ -66,7 +63,7 @@ impl Code {
/// Return all strings referenced in the codes.
pub fn get_all_strings(&self) -> HashSet<DexString> {
let mut strings = HashSet::new();
for (list, _) in &self.handlers {
for (_, _, (list, _)) in &self.tries {
for (ty, _) in list {
strings.extend(ty.get_all_strings());
}
@ -77,7 +74,7 @@ impl Code {
/// Return all types referenced in the codes.
pub fn get_all_types(&self) -> HashSet<IdType> {
let mut types = HashSet::new();
for (list, _) in &self.handlers {
for (_, _, (list, _)) in &self.tries {
for (ty, _) in list {
types.insert(ty.clone());
}

View file

@ -74,6 +74,8 @@ pub struct DexWriter {
encoded_array_items: Vec<EncodedArrayItem>,
/// The method_handles section.
method_handles: Vec<MethodHandleItem>,
/// The code_items sections.
code_items: Vec<CodeItem>,
/// The map_list
map_list: MapList,
}
@ -127,6 +129,7 @@ impl Default for DexWriter {
map_list: MapList::default(),
encoded_array_items: vec![],
method_handles: vec![],
code_items: vec![],
}
}
}
@ -328,7 +331,103 @@ impl DexWriter {
Ok(())
}
/// Insert a code_item.
///
/// # Warning
///
/// This is currently a stub that probably serialize invalid references to data.
fn insert_code_item(&mut self, method_id: IdMethod, direct_methods: bool) -> Result<()> {
let code = if direct_methods {
self.class_defs
.get(&method_id.class_)
.unwrap()
.0
.direct_methods
.get(&method_id)
.unwrap()
.code
.as_ref()
.unwrap()
} else {
self.class_defs
.get(&method_id.class_)
.unwrap()
.0
.virtual_methods
.get(&method_id)
.unwrap()
.code
.as_ref()
.unwrap()
};
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)
};
let item = CodeItem {
registers_size: code.registers_size,
ins_size: code.ins_size,
outs_size: code.outs_size,
debug_info_off: 0, // TODO: code.debug_info
insns: code.insns.clone(),
tries,
handlers,
};
self.section_manager
.add_elt(Section::CodeItem, Some(item.size()));
self.code_items.push(item);
Ok(())
}
/// 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.
///
/// 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.
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();
@ -379,6 +478,8 @@ impl DexWriter {
direct_methods.sort();
let mut last_method_id = 0;
for id in &direct_methods {
// &mut vs & of self and class make things difficult...
let (class, _) = self.class_defs.get(class_id).unwrap();
let idx = self.method_ids.get(id).ok_or(anyhow!(
"Method {} (method of class {}) not found in dex builder",
id.__repr__(),
@ -388,17 +489,27 @@ impl DexWriter {
last_method_id = *idx;
let access_flags =
Uleb128(class.direct_methods.get(id).unwrap().get_raw_access_flags());
// No if let because ownership gunfooterie
let code_off = if class.direct_methods.get(id).unwrap().code.is_some() {
let code_off = self.section_manager.get_size(Section::CodeItem);
self.insert_code_item(id.clone(), true)?;
Uleb128(code_off + 1)
} else {
Uleb128(0)
};
data.direct_methods.push(EncodedMethod {
method_idx_diff,
access_flags,
code_off: Uleb128(0), // TODO
code_off, // Will be relinked once the offset of the code item section is known
});
}
let (class, _) = self.class_defs.get(class_id).unwrap();
let mut virtual_methods: Vec<IdMethod> = class.virtual_methods.keys().cloned().collect();
virtual_methods.sort();
let mut last_method_id = 0;
for id in &virtual_methods {
let (class, _) = self.class_defs.get(class_id).unwrap();
let idx = self.method_ids.get(id).ok_or(anyhow!(
"Method {} (method of class {}) not found in dex builder",
id.__repr__(),
@ -413,10 +524,18 @@ impl DexWriter {
.unwrap()
.get_raw_access_flags(),
);
// No if let because ownership gunfooterie
let code_off = if class.virtual_methods.get(id).unwrap().code.is_some() {
let code_off = self.section_manager.get_size(Section::CodeItem);
self.insert_code_item(id.clone(), false)?;
Uleb128(code_off + 1)
} else {
Uleb128(0)
};
data.virtual_methods.push(EncodedMethod {
method_idx_diff,
access_flags,
code_off: Uleb128(0), // TODO
code_off, // Will be relinked once the offset of the code item section is known
});
}
self.section_manager
@ -866,6 +985,24 @@ impl DexWriter {
Ok(())
}
/// Link the offsets of code item in class_data_items.
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 {
for method in &mut data.direct_methods {
if method.code_off.0 != 0 {
method.code_off.0 += self.section_manager.get_offset(Section::CodeItem) - 1;
}
}
for method in &mut data.virtual_methods {
if method.code_off.0 != 0 {
method.code_off.0 += self.section_manager.get_offset(Section::CodeItem) - 1;
}
}
}
Ok(())
}
fn write_dex_file(&mut self, writer: &mut dyn Write) -> Result<()> {
// TODO: SPLIT THIS IN METHODS !!!
self.section_manager.reset();
@ -889,6 +1026,7 @@ impl DexWriter {
self.link_type_list_occurences()?;
self.link_class_data_occurences()?;
self.link_static_values()?;
self.link_code_item()?;
debug!("Serialize the dex file");
// TODO: compute checksum, hash, ect
@ -951,7 +1089,12 @@ impl DexWriter {
for data in &self.class_data_list {
data.serialize(writer)?;
}
// TODO: CodeItem,
// 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)?;
}