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

@ -7,6 +7,7 @@ edition = "2021"
[dependencies]
androscalpel_serializer = { version = "0.1.0", path = "../androscalpel_serializer" }
anyhow = "1.0.98"
flate2 = { version = "1.0.28", features = ["rust_backend"] }
log = "0.4.25"
rand = "0.8.5"

View file

@ -1,4 +1,5 @@
use androscalpel_serializer::Serializable;
use anyhow::{Context, Result};
use std::collections::HashMap;
use std::env;
use std::fs;
@ -162,7 +163,7 @@ pub fn replace_dex(
apksigner: Option<impl AsRef<Path>>,
keypassword: Option<&str>,
additionnal_files: Option<HashMap<String, Option<impl Read + Seek>>>,
) {
) -> Result<()> {
let zipalign = if let Some(path) = &zipalign {
path.as_ref().as_os_str()
} else {
@ -176,17 +177,22 @@ pub fn replace_dex(
let tmp_dir = env::temp_dir().join(format!("apk_frauder_{:x}", rand::random::<u128>()));
let unaligned_path = tmp_dir.join("stripped.apk");
let aligned_path = tmp_dir.join("aligned.apk");
fs::create_dir_all(&tmp_dir).expect("Failed to create temporary directory");
fs::create_dir_all(&tmp_dir).context("Failed to create temporary directory")?;
let file = File::open(apk).expect("failed to open file");
let mut apk = ZipFileReader::new(file);
let file = File::create(&unaligned_path).expect("failed to create file");
let file = File::open(&apk).with_context(|| {
format!(
"failed to open file {}",
apk.as_ref().to_str().unwrap_or("")
)
})?;
let mut apk = ZipFileReader::new(file)?;
let file = File::create(&unaligned_path).context("failed to create file")?;
let mut apk_out = ZipFileWriter::new(file, apk.zip64_end_of_central_directory.clone());
let mut file_info_ref = (*apk
.get_classes_file_info()
.first()
.expect("No dex file found in apk"))
.context("No dex file found in apk")?)
.clone();
apk.unlink_signature_files();
@ -233,7 +239,7 @@ pub fn replace_dex(
.arg(unaligned_path.as_os_str())
.arg(aligned_path.as_os_str())
.status()
.unwrap();
.context("Failed to run zipalign")?;
let mut cmd = Command::new(apksigner);
let cmd = cmd
@ -249,6 +255,7 @@ pub fn replace_dex(
} else {
cmd
};
cmd.status().unwrap();
fs::remove_dir_all(tmp_dir).expect("Failled to remove tmp dir");
cmd.status().context("Failled to run apksigne")?;
fs::remove_dir_all(tmp_dir).context("Failled to remove tmp dir")?;
Ok(())
}

View file

@ -22,6 +22,6 @@ fn main() {
None::<HashMap<String, Option<Cursor<&[u8]>>>>,
);*/
let file = File::open("zagruski.apk").unwrap();
let reader = ZipFileReader::new(file);
let reader = ZipFileReader::new(file).unwrap();
println!("{:#?}", &reader.files[..2]);
}

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> {