add code item and related
This commit is contained in:
parent
9ed99594cc
commit
ae1c3e20ac
3 changed files with 327 additions and 0 deletions
|
|
@ -16,6 +16,7 @@ pub enum Error {
|
|||
SerializationError(String),
|
||||
DeserializationError(String),
|
||||
InvalidStringEncoding(String),
|
||||
InconsistantStruct(String),
|
||||
}
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
|
@ -30,6 +31,7 @@ impl std::fmt::Display for Error {
|
|||
Self::SerializationError(msg) => write!(f, "Error: {}", msg),
|
||||
Self::DeserializationError(msg) => write!(f, "Error: {}", msg),
|
||||
Self::InvalidStringEncoding(msg) => write!(f, "Error: {}", msg),
|
||||
Self::InconsistantStruct(msg) => write!(f, "Error: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
323
androscalpel_serializer/src/items/code.rs
Normal file
323
androscalpel_serializer/src/items/code.rs
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
//! Code items
|
||||
|
||||
use crate as androscalpel_serializer;
|
||||
use crate::{Error, ReadSeek, Result, Serializable, Sleb128, Uleb128};
|
||||
use std::io::Write;
|
||||
|
||||
/// https://source.android.com/docs/core/runtime/dex-format#code-item
|
||||
/// alignment: 4 bytes
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CodeItem {
|
||||
pub registers_size: u16,
|
||||
pub ins_size: u16,
|
||||
pub outs_size: u16,
|
||||
// pub tries_size: u16,
|
||||
/// 0 if no debug info, else offset to a [`DebugInfoItem`].
|
||||
pub debug_info_off: u32,
|
||||
// pub insns_size: u32,
|
||||
pub insns: Vec<u16>,
|
||||
// pub padding: Vec<u8>,
|
||||
/// try items must refere to non overlapping range an order from low to hight addresses.
|
||||
pub tries: Vec<TryItem>,
|
||||
pub handlers: Option<EncodedCatchHandlerList>,
|
||||
}
|
||||
|
||||
impl CodeItem {
|
||||
pub fn tries_size_field(&self) -> u16 {
|
||||
self.tries.len() as u16
|
||||
}
|
||||
|
||||
pub fn insns_size_field(&self) -> u32 {
|
||||
self.insns.len() as u32
|
||||
}
|
||||
|
||||
pub fn sanity_check(&self) -> Result<()> {
|
||||
if self.tries.is_empty() && self.handlers.is_some() {
|
||||
return Err(Error::InconsistantStruct(
|
||||
"CodeItem cannot have a `handlers` value if `tries_size` is 0 (CodeItem.tries is empty)".into()
|
||||
));
|
||||
}
|
||||
if !self.tries.is_empty() && self.handlers.is_none() {
|
||||
return Err(Error::InconsistantStruct(
|
||||
"CodeItem must have a `handlers` value if `tries_size` is not 0 (CodeItem.tries not empty)".into()
|
||||
));
|
||||
}
|
||||
let mut max_addr = 0;
|
||||
for item in &self.tries {
|
||||
let addr = item.start_addr;
|
||||
if addr < max_addr {
|
||||
return Err(Error::InconsistantStruct(
|
||||
"try_item in a code_item must be non overlapping and sorted from low to high address".into()
|
||||
));
|
||||
}
|
||||
max_addr += addr + (item.insn_count as u32);
|
||||
}
|
||||
// Necessary? no in spec
|
||||
if max_addr > self.insns.len() as u32 {
|
||||
return Err(Error::InconsistantStruct(
|
||||
"found try_item whose block span outside of the insns array".into(),
|
||||
));
|
||||
}
|
||||
|
||||
for try_ in &self.tries {
|
||||
let handler = self
|
||||
.handlers
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get_handler_at_offset(try_.handler_off)?;
|
||||
handler.sanity_check()?;
|
||||
for handler in &handler.handlers {
|
||||
// Necessary? no in spec
|
||||
if handler.addr.0 > self.insns.len() as u32 {
|
||||
return Err(Error::InconsistantStruct(
|
||||
"Found an handler whose address is outside of the insns array".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(Uleb128(addr)) = handler.catch_all_addr {
|
||||
// Necessary? no in spec
|
||||
if addr > self.insns.len() as u32 {
|
||||
return Err(Error::InconsistantStruct(
|
||||
"Found a catch all handler whose address is outside of the insns array"
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serializable for CodeItem {
|
||||
fn serialize(&self, output: &mut dyn Write) -> Result<()> {
|
||||
self.sanity_check().map_err(|err| match err {
|
||||
Error::InconsistantStruct(msg) => {
|
||||
Error::SerializationError(format!("Inconsistant CodeItem: {msg}"))
|
||||
}
|
||||
err => err,
|
||||
})?;
|
||||
self.registers_size.serialize(output)?;
|
||||
self.ins_size.serialize(output)?;
|
||||
self.outs_size.serialize(output)?;
|
||||
self.tries_size_field().serialize(output)?;
|
||||
self.debug_info_off.serialize(output)?;
|
||||
for insn in &self.insns {
|
||||
insn.serialize(output)?;
|
||||
}
|
||||
if !self.tries.is_empty() && self.insns.len() % 2 == 1 {
|
||||
0u16.serialize(output)?;
|
||||
}
|
||||
for item in &self.tries {
|
||||
item.serialize(output)?;
|
||||
}
|
||||
if let Some(ref handlers) = self.handlers {
|
||||
handlers.serialize(output)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
|
||||
let registers_size = u16::deserialize(input)?;
|
||||
let ins_size = u16::deserialize(input)?;
|
||||
let outs_size = u16::deserialize(input)?;
|
||||
let tries_size = u16::deserialize(input)?;
|
||||
let debug_info_off = u32::deserialize(input)?;
|
||||
let insns_size = u32::deserialize(input)?;
|
||||
let mut insns = vec![];
|
||||
for _ in 0..insns_size {
|
||||
insns.push(u16::deserialize(input)?);
|
||||
}
|
||||
if tries_size != 0 && insns_size % 2 == 1 {
|
||||
let _ = u16::deserialize(input)?;
|
||||
}
|
||||
let mut tries = vec![];
|
||||
for _ in 0..tries_size {
|
||||
tries.push(TryItem::deserialize(input)?);
|
||||
}
|
||||
let handlers = if tries_size != 0 {
|
||||
Some(EncodedCatchHandlerList::deserialize(input)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Self {
|
||||
registers_size,
|
||||
ins_size,
|
||||
outs_size,
|
||||
debug_info_off,
|
||||
insns,
|
||||
tries,
|
||||
handlers,
|
||||
})
|
||||
}
|
||||
fn size(&self) -> usize {
|
||||
self.registers_size.size()
|
||||
+ self.ins_size.size()
|
||||
+ self.outs_size.size()
|
||||
+ self.tries_size_field().size()
|
||||
+ self.debug_info_off.size()
|
||||
+ self.insns.iter().map(|val| val.size()).sum::<usize>()
|
||||
+ if !self.tries.is_empty() && self.insns.len() % 2 == 1 {
|
||||
2
|
||||
} else {
|
||||
0
|
||||
}
|
||||
+ self.tries.iter().map(|val| val.size()).sum::<usize>()
|
||||
+ self.handlers.as_ref().map(|val| val.size()).unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// https://source.android.com/docs/core/runtime/dex-format#type-item
|
||||
#[derive(Serializable, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct TryItem {
|
||||
/// Start address of the block of code covered. It's a count of 16-bit code unit to the
|
||||
/// start of the first covered instruction of the block.
|
||||
pub start_addr: u32,
|
||||
/// Number of 16-bit code unit covered by the entry.
|
||||
pub insn_count: u16,
|
||||
/// **Offset in bytes** from the start of the `EncodedCatchHandlerList` to the
|
||||
/// `EncodedCatchHandler` associated.
|
||||
pub handler_off: u16,
|
||||
}
|
||||
|
||||
/// https://source.android.com/docs/core/runtime/dex-format#encoded-catch-handlerlist
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct EncodedCatchHandlerList {
|
||||
// pub size: Uleb128,
|
||||
pub list: Vec<EncodedCatchHandler>,
|
||||
}
|
||||
|
||||
impl EncodedCatchHandlerList {
|
||||
pub fn size_field(&self) -> Uleb128 {
|
||||
Uleb128(self.list.len() as u32)
|
||||
}
|
||||
|
||||
/// Return a reference to the [`EncodedCatchHandler`] located at `offset` bytes after
|
||||
/// the begining of the [`EncodedCatchHandlerList`]. Expected to be used to lookup
|
||||
/// the value refered to by [`TryItem.handler_off`].
|
||||
pub fn get_handler_at_offset(&self, offset: u16) -> Result<&EncodedCatchHandler> {
|
||||
let offset = offset as usize;
|
||||
let mut current_offset = 0;
|
||||
for handler in &self.list {
|
||||
if current_offset == offset {
|
||||
return Ok(handler);
|
||||
}
|
||||
current_offset += handler.size();
|
||||
if current_offset > offset {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(Error::InconsistantStruct(format!(
|
||||
"Offset 0x{offset:x} does not match with the begining of a EncodedCatchHandler in this EncodedCatchHandlerList"
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serializable for EncodedCatchHandlerList {
|
||||
fn serialize(&self, output: &mut dyn Write) -> Result<()> {
|
||||
self.size_field().serialize(output)?;
|
||||
for item in &self.list {
|
||||
item.serialize(output)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
|
||||
let size = i32::deserialize(input)?;
|
||||
let mut list = vec![];
|
||||
for _ in 0..size {
|
||||
list.push(EncodedCatchHandler::deserialize(input)?);
|
||||
}
|
||||
Ok(Self {
|
||||
// size,
|
||||
list,
|
||||
})
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.size_field().size() + self.list.iter().map(|val| val.size()).sum::<usize>()
|
||||
}
|
||||
}
|
||||
|
||||
/// https://source.android.com/docs/core/runtime/dex-format#encoded-catch-handler
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct EncodedCatchHandler {
|
||||
// pub size: Sleb128,
|
||||
/// List of handler, one by type caught, in the order of the type tests
|
||||
pub handlers: Vec<EncodedTypeAddrPair>,
|
||||
/// Bytecode address of the catchall handler.
|
||||
pub catch_all_addr: Option<Uleb128>,
|
||||
}
|
||||
|
||||
impl EncodedCatchHandler {
|
||||
pub fn size_field(&self) -> Sleb128 {
|
||||
let sign = if self.catch_all_addr.is_some() { -1 } else { 1 };
|
||||
let len = self.handlers.len() as i32;
|
||||
//if len == 0 && self.catch_all_addr.is_none() {
|
||||
// Not good. Panic? Error?
|
||||
//}
|
||||
Sleb128(len * sign)
|
||||
}
|
||||
|
||||
pub fn sanity_check(&self) -> Result<()> {
|
||||
if self.catch_all_addr.is_none() && self.handlers.is_empty() {
|
||||
return Err(Error::InconsistantStruct(
|
||||
"EncodedCatchHandler must have at least one handler or catch_all_addr defined"
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serializable for EncodedCatchHandler {
|
||||
fn serialize(&self, output: &mut dyn Write) -> Result<()> {
|
||||
self.sanity_check().map_err(|err| match err {
|
||||
Error::InconsistantStruct(msg) => {
|
||||
Error::SerializationError(format!("Inconsistant EncodedCatchHandler: {msg}"))
|
||||
}
|
||||
err => err,
|
||||
})?;
|
||||
self.size_field().serialize(output)?;
|
||||
for handler in &self.handlers {
|
||||
handler.serialize(output)?;
|
||||
}
|
||||
if let Some(catch_all_addr) = self.catch_all_addr {
|
||||
catch_all_addr.serialize(output)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
|
||||
let Sleb128(size) = Sleb128::deserialize(input)?;
|
||||
let mut handlers = vec![];
|
||||
for _ in 0..size.abs() {
|
||||
handlers.push(EncodedTypeAddrPair::deserialize(input)?);
|
||||
}
|
||||
let catch_all_addr = if size <= 0 {
|
||||
Some(Uleb128::deserialize(input)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Self {
|
||||
handlers,
|
||||
catch_all_addr,
|
||||
})
|
||||
}
|
||||
fn size(&self) -> usize {
|
||||
self.size_field().size()
|
||||
+ self.handlers.iter().map(|val| val.size()).sum::<usize>()
|
||||
+ self
|
||||
.catch_all_addr
|
||||
.as_ref()
|
||||
.map(|val| val.size())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// https://source.android.com/docs/core/runtime/dex-format#encoded-type-addr-pair
|
||||
#[derive(Serializable, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct EncodedTypeAddrPair {
|
||||
/// Index of the [`TypeId`] in `type_ids`
|
||||
pub type_idx: Uleb128,
|
||||
/// Bytecode address of the exception handler
|
||||
pub addr: Uleb128,
|
||||
}
|
||||
|
|
@ -4,10 +4,12 @@ use crate as androscalpel_serializer;
|
|||
use crate::{EncodedArray, Serializable};
|
||||
|
||||
pub mod class;
|
||||
pub mod code;
|
||||
pub mod header;
|
||||
pub mod map;
|
||||
|
||||
pub use class::*;
|
||||
pub use code::*;
|
||||
pub use header::*;
|
||||
pub use map::*;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue