add support for read hiddenapi

This commit is contained in:
Jean-Marie Mineau 2024-06-10 18:20:45 +02:00
parent df9d258780
commit 437ecbeecc
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
17 changed files with 764 additions and 92 deletions

View file

@ -1,13 +1,12 @@
//! Hidden api items.
use crate as androscalpel_serializer;
use crate::{Error, ReadSeek, Result, Serializable, Uleb128};
use std::io::{Cursor, Write};
use std::io::{Cursor, Seek, SeekFrom, Write};
/// <https://source.android.com/docs/core/runtime/dex-format#hiddenapi-class-data-item>
/// Hard to serialize/deserialize without additional data like the number of classes
/// Hard to serialize/deserialize without additional data like the number of classes defs
/// or the method/field of the classes.
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct HiddenapiClassDataItem {
//pub size: u32,
pub data: Vec<u8>,
@ -18,7 +17,7 @@ impl HiddenapiClassDataItem {
self.data.len() as u32
}
/// Return `hiddenapi_class_data_item.offsets[class_idx]`.
/// Return `hiddenapi_class_data_item.offsets[class_def_item_idx]`.
///
/// If `0`: Either no data for this class or all API flags are zero.
/// Else: offset from the begining of the [`HiddenapiClassDataItem`]
@ -26,13 +25,17 @@ impl HiddenapiClassDataItem {
///
/// # Warning
///
/// They are no check weither the `class_idx` is valid one or not.
/// Giving an invalid idx (like an idx >= nb class) is UB.
pub fn get_offset(&self, class_idx: u32) -> Result<u32> {
let index = (class_idx as usize) * 4;
if self.data.len() < index - 4 {
/// `class_def_item_idx` is **NOT** `class_idx` (Contrary to what
/// <https://source.android.com/docs/core/runtime/dex-format#hiddenapi-class-data-item> says).
/// `class_def_item_idx` is the index of the `class_def_item`.
///
/// They are no check weither the `class_def_item_idx` is valid one or not.
/// Giving an invalid idx (like an idx >= nb class def) is UB.
pub fn get_offset(&self, class_def_item_idx: usize) -> Result<u32> {
let index = class_def_item_idx * 4;
if self.data.len() <= index {
Err(Error::InconsistantStruct(format!(
"class index 0x{class_idx:x} out of bound of HiddenapiClassDataItem data"
"class index 0x{class_def_item_idx:x} out of bound of HiddenapiClassDataItem data"
)))
} else {
u32::deserialize_from_slice(&self.data[index..])
@ -44,7 +47,7 @@ impl HiddenapiClassDataItem {
/// # Warning
///
/// They are no check weither the `nb_class` is valid one or not.
/// Giving an invalid `nb_class`.
/// Giving an invalid `nb_class` is UB.
pub fn get_offsets(&self, nb_class: u32) -> Result<Vec<u32>> {
let mut offsets = vec![];
let mut buffer = Cursor::new(self.data.as_slice());
@ -60,19 +63,37 @@ impl HiddenapiClassDataItem {
///
/// # Warning
///
/// They are no check weither the `nb_flags` or `offset`
/// `class_def_item_idx` is **NOT** `class_idx` (Contrary to what
/// <https://source.android.com/docs/core/runtime/dex-format#hiddenapi-class-data-item> says).
/// `class_def_item_idx` is the index of the `class_def_item`.
///
/// They are no check weither the `nb_flags` or `class_def_item_idx`
/// are valid. Providing invalid values is UB.
pub fn get_flags(&self, nb_flags: usize, offset: u32) -> Result<Vec<Uleb128>> {
pub fn get_flags(
&self,
nb_flags: usize,
class_def_item_idx: usize,
) -> Result<Vec<HiddenApiFlags>> {
let offset = self.get_offset(class_def_item_idx)?;
if offset == 0 {
Ok(vec![Uleb128(0); nb_flags])
Ok(vec![HiddenApiFlags::DEFAULT; nb_flags])
} else if offset < 4 {
// < 8 is almost certainly false
panic!()
} else {
let mut buffer = Cursor::new(self.data.as_slice());
buffer
.seek(SeekFrom::Start(offset as u64 - 4))
.map_err(|_| {
Error::InconsistantStruct(format!(
"{offset} if out of data bound for HiddenApiClassData"
))
})?;
let mut flags = vec![];
for _ in 0..nb_flags {
flags.push(Uleb128::deserialize(&mut buffer)?);
flags.push(HiddenApiFlags::from_uleb128(Uleb128::deserialize(
&mut buffer,
)?));
}
Ok(flags)
}
@ -102,40 +123,139 @@ impl Serializable for HiddenapiClassDataItem {
}
}
/// Flags for hidden api
#[derive(Serializable, Debug, PartialEq, Eq, Copy, Clone)]
#[prefix_type(Uleb128)]
pub enum HiddenApiFlag {
#[derive(Clone, PartialEq, Eq)]
pub struct HiddenApiFlags {
pub value: HiddenApiValue,
pub domain_api: HiddenApiDomain,
}
impl HiddenApiFlags {
pub const DEFAULT: Self = Self {
value: HiddenApiValue::Whitelist,
domain_api: HiddenApiDomain {
is_test_api: false,
is_core_platform_api: false,
unknown_flags: 0,
},
};
pub fn from_uleb128(Uleb128(val): Uleb128) -> Self {
Self {
value: HiddenApiValue::from_u32(val),
domain_api: HiddenApiDomain::from_u32(val),
}
}
pub fn to_uleb128(&self) -> Uleb128 {
Uleb128(self.value.to_u32() | self.domain_api.to_u32())
}
}
/// Flags for hidden api flag value
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum HiddenApiValue {
/// Interfaces that can be freely used and are supported as
/// part of the officially documented Android framework Package Index
#[prefix(Uleb128(0x00))]
Whitelist,
/// Non-SDK interfaces that can be used regardless of the
/// application's target API level
#[prefix(Uleb128(0x01))]
Greylist,
/// Non-SDK interfaces that cannot be used regardless of the
/// application's target API level. Accessing one of these
/// interfaces causes a runtime error.
#[prefix(Uleb128(0x02))]
Blacklist,
/// Non-SDK interfaces that can be used for Android 8.x and
/// below unless they are restricted.
#[prefix(Uleb128(0x03))]
/// below unless they are restricted (targetSdkVersion <= 27 (O_MR1)).
GreylistMaxO,
/// Non-SDK interfaces that can be used for Android 9.x unless
/// they are restricted.
#[prefix(Uleb128(0x04))]
GreylistMaxP,
/// Non-SDK interfaces that can be used for Android 10.x unless
/// they are restricted.
#[prefix(Uleb128(0x05))]
GreylistMaxQ,
/// Non-SDK interfaces that can be used for Android 11.x unless
/// they are restricted.
#[prefix(Uleb128(0x06))]
GreylistMaxR,
GreylistMaxS,
/// Unknown flag, either an error or this crate is out of date.
#[default_variant]
Unknwon(Uleb128),
Unknwon(u8),
}
impl HiddenApiValue {
pub const MASK: u32 = 0b111;
pub fn from_u32(flags: u32) -> Self {
match flags & Self::MASK {
0x00 => Self::Whitelist,
0x01 => Self::Greylist,
0x02 => Self::Blacklist,
0x03 => Self::GreylistMaxO,
0x04 => Self::GreylistMaxP,
0x05 => Self::GreylistMaxQ,
0x06 => Self::GreylistMaxR,
0x07 => Self::GreylistMaxS,
other => Self::Unknwon(other as u8),
}
}
pub fn to_u32(&self) -> u32 {
match self {
Self::Whitelist => 0x00,
Self::Greylist => 0x01,
Self::Blacklist => 0x02,
Self::GreylistMaxO => 0x03,
Self::GreylistMaxP => 0x04,
Self::GreylistMaxQ => 0x05,
Self::GreylistMaxR => 0x06,
Self::GreylistMaxS => 0x07,
Self::Unknwon(other) => {
if (0b1111_1000 & *other) != 0 {
panic!("HiddenApiValue is encoded on 3 bits but found value {other}");
}
*other as u32
}
}
}
}
/// Flags for hidden api flag domain
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct HiddenApiDomain {
pub is_core_platform_api: bool,
pub is_test_api: bool,
pub unknown_flags: u32,
}
impl HiddenApiDomain {
pub const CORE_PLATFORM_API_FLAG: u32 = 0x08;
pub const TEST_API_FLAG: u32 = 0x10;
pub fn from_u32(flags: u32) -> Self {
let flags = flags & !HiddenApiValue::MASK;
Self {
is_core_platform_api: (flags & Self::CORE_PLATFORM_API_FLAG) != 0,
is_test_api: (flags & Self::TEST_API_FLAG) != 0,
unknown_flags: flags & (!Self::CORE_PLATFORM_API_FLAG) & (!Self::TEST_API_FLAG),
}
}
pub fn to_u32(&self) -> u32 {
if ((0b111 | Self::CORE_PLATFORM_API_FLAG | Self::CORE_PLATFORM_API_FLAG)
& self.unknown_flags)
!= 0
{
panic!(
"The first 5 bits of HiddenApiDomain are reserved and \
shoud be set to 0, but value 0x{:x} was found",
self.unknown_flags
);
}
self.unknown_flags
| if self.is_core_platform_api {
Self::CORE_PLATFORM_API_FLAG
} else {
0
}
| if self.is_test_api {
Self::TEST_API_FLAG
} else {
0
}
}
}