start adding error handling to apk_frauder

This commit is contained in:
Jean-Marie Mineau 2025-04-17 18:01:27 +02:00
parent 615f7a8f52
commit 5940434694
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
6 changed files with 106 additions and 65 deletions

View file

@ -1,7 +1,3 @@
use log::{info, warn};
use std::collections::HashMap;
use std::io::{Read, Seek, SeekFrom};
use crate::{
apk_signing_block::ApkSigningBlock,
apk_signing_block::Magic,
@ -13,7 +9,11 @@ use crate::{
general_purpose_flags, FileHeader, FileInfo, LocalFileHeader, Signature,
};
use androscalpel_serializer::Serializable;
use anyhow::{bail, Context, Result};
use flate2::read::DeflateDecoder;
use log::{info, warn};
use std::collections::HashMap;
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, PartialEq, Eq)]
pub struct ZipFileReader<T: Read + Seek> {
@ -25,29 +25,37 @@ pub struct ZipFileReader<T: Read + Seek> {
}
impl<T: Read + Seek> ZipFileReader<T> {
pub fn new(mut reader: T) -> Self {
pub fn new(mut reader: T) -> Result<Self> {
let end_of_central_directory_off =
Self::get_end_of_central_directory_offset(&mut reader).unwrap();
Self::get_end_of_central_directory_offset(&mut reader)
.context("end of centrall directory not found, probably not a zip")?;
reader
.seek(SeekFrom::Start(end_of_central_directory_off))
.unwrap();
let end_of_central_directory = EndCentralDirectory::deserialize(&mut reader).unwrap();
reader
.context("Failed to seek to end of central directory")?;
let end_of_central_directory = EndCentralDirectory::deserialize(&mut reader)
.context("Failed to deserialize end of central directory")?;
let zip64_end_of_central_directory = if reader
.seek(SeekFrom::Start(
end_of_central_directory_off - Zip64EndCentralDirectoryLocator::SIZE as u64,
))
.unwrap();
let zip64_ecd_locator = Zip64EndCentralDirectoryLocator::deserialize(&mut reader).ok();
let zip64_end_of_central_directory = if let Some(zip64_ecd_locator) = zip64_ecd_locator {
assert_eq!(
zip64_ecd_locator.disk_number_of_zip64_end_central_directory_start,
0
);
assert!(zip64_ecd_locator.total_number_of_disks <= 1);
let zip64_edc_record_off =
zip64_ecd_locator.offset_zip64_end_of_central_directory_record;
reader.seek(SeekFrom::Start(zip64_edc_record_off)).unwrap();
Zip64EndCentralDirectory::deserialize(&mut reader).ok()
.is_ok()
{
let zip64_ecd_locator = Zip64EndCentralDirectoryLocator::deserialize(&mut reader).ok();
if let Some(zip64_ecd_locator) = zip64_ecd_locator {
assert_eq!(
zip64_ecd_locator.disk_number_of_zip64_end_central_directory_start,
0
);
assert!(zip64_ecd_locator.total_number_of_disks <= 1);
let zip64_edc_record_off =
zip64_ecd_locator.offset_zip64_end_of_central_directory_record;
reader
.seek(SeekFrom::Start(zip64_edc_record_off))
.context("Failed to seek to end of zip64 central directory")?;
Zip64EndCentralDirectory::deserialize(&mut reader).ok()
} else {
None
}
} else {
None
};
@ -66,24 +74,29 @@ impl<T: Read + Seek> ZipFileReader<T> {
zip_file
.data
.seek(SeekFrom::Start(zip_file.get_cd_offset()))
.unwrap();
.context("Failed to seek to central directory")?;
let mut size_read = 0;
let cd_size = zip_file.get_cd_size();
while size_read < cd_size {
let header = FileHeader::deserialize(&mut zip_file.data).unwrap();
let header = FileHeader::deserialize(&mut zip_file.data)
.context("Failed to deserialize file header")?;
//println!("{:#?}", header);
size_read += header.size() as u64;
let pos_in_dir = zip_file.data.stream_position().unwrap();
let pos_in_dir = zip_file
.data
.stream_position()
.context("Failed to get stream position")?;
if header.general_purpose_flags & general_purpose_flags::MASK_ENCRYPTED_CENTRAL_DIR != 0
{
panic!("Central directory encryption not supported");
bail!("Central directory encryption not supported");
}
zip_file
.data
.seek(SeekFrom::Start(header.get_offset_local_header()))
.unwrap();
let local_header = LocalFileHeader::deserialize(&mut zip_file.data).unwrap();
.context("Failled to seek to local header")?;
let local_header = LocalFileHeader::deserialize(&mut zip_file.data)
.context("Failed to deserialize local file header")?;
let data_descriptor = if (local_header.general_purpose_flags
& general_purpose_flags::MASK_USE_DATA_DESCRIPTOR
!= 0)
@ -94,20 +107,25 @@ impl<T: Read + Seek> ZipFileReader<T> {
zip_file
.data
.seek(SeekFrom::Current(header.compressed_size as i64))
.unwrap();
.context("failed to seek to after the file data")?;
if zip_file.zip64_end_of_central_directory.is_some() {
Some(DataDescriptor::Zip64(
DataDescriptor64::deserialize(&mut zip_file.data).unwrap(),
DataDescriptor64::deserialize(&mut zip_file.data)
.context("Failed to deserialize data descriptor 64")?,
))
} else {
Some(DataDescriptor::Zip32(
DataDescriptor32::deserialize(&mut zip_file.data).unwrap(),
DataDescriptor32::deserialize(&mut zip_file.data)
.context("Failed to deserialize data descriptor")?,
))
}
} else {
None
};
zip_file.data.seek(SeekFrom::Start(pos_in_dir)).unwrap();
zip_file
.data
.seek(SeekFrom::Start(pos_in_dir))
.context("Failed to seek to position in directory")?;
zip_file.files.push(FileInfo {
local_header,
header,
@ -119,24 +137,26 @@ impl<T: Read + Seek> ZipFileReader<T> {
zip_file
.data
.seek(SeekFrom::Start(zip_file.get_cd_offset() - 16))
.unwrap();
let magic = Magic::deserialize(&mut zip_file.data).unwrap();
.context("Failed to seek to central directory")?;
let magic =
Magic::deserialize(&mut zip_file.data).context("Failed to deserialize Magic")?;
if magic == ApkSigningBlock::MAGIC {
zip_file
.data
.seek(SeekFrom::Start(zip_file.get_cd_offset() - 16 - 8))
.unwrap();
let block_size = u64::deserialize(&mut zip_file.data).unwrap();
.context("Failed to seek to central directory")?;
let block_size = u64::deserialize(&mut zip_file.data)
.context("Failed to deserialize block size")?;
zip_file
.data
.seek(SeekFrom::Start(zip_file.get_cd_offset() - block_size - 8))
.unwrap();
.context("Failed to seek to central directory")?;
zip_file.apk_sign_block = ApkSigningBlock::deserialize(&mut zip_file.data).ok();
}
}
zip_file
Ok(zip_file)
}
pub fn is_zip64(&self) -> bool {
@ -195,7 +215,7 @@ impl<T: Read + Seek> ZipFileReader<T> {
}
pub fn get_end_of_central_directory_offset(reader: &mut T) -> Option<u64> {
let file_size = reader.seek(SeekFrom::End(0)).unwrap();
let file_size = reader.seek(SeekFrom::End(0)).ok()?;
let mut sig = Signature::default();
let mut comment_size = 0;
while sig != EndCentralDirectory::SIGNATURE {
@ -203,8 +223,8 @@ impl<T: Read + Seek> ZipFileReader<T> {
.seek(SeekFrom::End(
-(EndCentralDirectory::MIN_SIZE as i64) - comment_size,
))
.unwrap();
sig = Signature::deserialize(reader).unwrap();
.ok()?;
sig = Signature::deserialize(reader).ok()?;
comment_size += 1;
if comment_size > 65536
|| comment_size as usize + EndCentralDirectory::MIN_SIZE > file_size as usize
@ -305,38 +325,50 @@ impl<T: Read + Seek> ZipFileReader<T> {
}
}
pub fn get_bin(&mut self, offset: u64, size: usize) -> Vec<u8> {
self.data.seek(SeekFrom::Start(offset)).unwrap();
pub fn get_bin(&mut self, offset: u64, size: usize) -> Result<Vec<u8>> {
self.data
.seek(SeekFrom::Start(offset))
.context("Failed to seek to data")?;
let mut data = vec![0u8; size];
self.data.read_exact(&mut data).unwrap();
self.data
.read_exact(&mut data)
.context("failed to read data")?;
/*
for _ in 0..size {
data.push(u8::deserialize(&mut self.data).unwrap());
}
*/
data
Ok(data)
}
pub fn read_file_as_vec(&mut self, name: &str) -> Vec<u8> {
let file = self.get_file_info(name).unwrap();
pub fn read_file_as_vec(&mut self, name: &str) -> Result<Vec<u8>> {
let file = self
.get_file_info(name)
.with_context(|| format!("Failed to get info for {name}"))?;
let offset = file.get_file_offset();
let size_c = file.header.compressed_size as usize;
let size = file.header.uncompressed_size as usize;
let compression_method = file.header.compression_method;
let mut data = vec![0u8; size_c];
self.data.seek(SeekFrom::Start(offset)).unwrap();
self.data.read_exact(&mut data).unwrap();
self.data
.seek(SeekFrom::Start(offset))
.with_context(|| format!("Failed to seek to start of file {name} (at 0x{offset:x})"))?;
self.data
.read_exact(&mut data)
.with_context(|| format!("Failed to read data for file {name}"))?;
match compression_method {
CompressionMethod::Stored => {}
CompressionMethod::Deflated => {
let mut decomp_data = vec![0u8; size];
let mut deflater = DeflateDecoder::new(&data[..]);
deflater.read_exact(&mut decomp_data).unwrap();
deflater
.read_exact(&mut decomp_data)
.with_context(|| format!("Failed to decompress data for file {name}"))?;
data = decomp_data
}
_ => unimplemented!(),
}
data
Ok(data)
}
pub fn get_file_info(&self, name: &str) -> Option<&FileInfo> {