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),
|
SerializationError(String),
|
||||||
DeserializationError(String),
|
DeserializationError(String),
|
||||||
InvalidStringEncoding(String),
|
InvalidStringEncoding(String),
|
||||||
|
InconsistantStruct(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = core::result::Result<T, Error>;
|
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::SerializationError(msg) => write!(f, "Error: {}", msg),
|
||||||
Self::DeserializationError(msg) => write!(f, "Error: {}", msg),
|
Self::DeserializationError(msg) => write!(f, "Error: {}", msg),
|
||||||
Self::InvalidStringEncoding(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};
|
use crate::{EncodedArray, Serializable};
|
||||||
|
|
||||||
pub mod class;
|
pub mod class;
|
||||||
|
pub mod code;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
|
|
||||||
pub use class::*;
|
pub use class::*;
|
||||||
|
pub use code::*;
|
||||||
pub use header::*;
|
pub use header::*;
|
||||||
pub use map::*;
|
pub use map::*;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue