diff --git a/Cargo.lock b/Cargo.lock index 8610ed3..ac708b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -67,9 +67,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" dependencies = [ "backtrace", ] @@ -79,6 +79,7 @@ name = "apk_frauder" version = "0.1.0" dependencies = [ "androscalpel_serializer", + "anyhow", "flate2", "log", "rand", diff --git a/androscalpel/src/apk.rs b/androscalpel/src/apk.rs index 40ea179..8abfbbd 100644 --- a/androscalpel/src/apk.rs +++ b/androscalpel/src/apk.rs @@ -3012,7 +3012,7 @@ impl Apk { + Send + Sync, { - let mut apk_z = ZipFileReader::new(apk); + let mut apk_z = ZipFileReader::new(apk)?; let mut apk = Self::default(); let dex_names = apk_z .get_classes_file_info() @@ -3020,7 +3020,7 @@ impl Apk { .map(|info| info.get_name()) .collect::>(); for name in dex_names { - let data = apk_z.read_file_as_vec(&name); + let data = apk_z.read_file_as_vec(&name)?; // TODO recover? apk.add_dex_file(&name, &data, label_ins.clone(), cache)?; } Ok(apk) diff --git a/apk_frauder/Cargo.toml b/apk_frauder/Cargo.toml index d51c48b..b10ffb7 100644 --- a/apk_frauder/Cargo.toml +++ b/apk_frauder/Cargo.toml @@ -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" diff --git a/apk_frauder/src/lib.rs b/apk_frauder/src/lib.rs index 06b556d..df3a135 100644 --- a/apk_frauder/src/lib.rs +++ b/apk_frauder/src/lib.rs @@ -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>, keypassword: Option<&str>, additionnal_files: Option>>, -) { +) -> 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::())); 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(()) } diff --git a/apk_frauder/src/main.rs b/apk_frauder/src/main.rs index ba72058..3cd0332 100644 --- a/apk_frauder/src/main.rs +++ b/apk_frauder/src/main.rs @@ -22,6 +22,6 @@ fn main() { None::>>>, );*/ let file = File::open("zagruski.apk").unwrap(); - let reader = ZipFileReader::new(file); + let reader = ZipFileReader::new(file).unwrap(); println!("{:#?}", &reader.files[..2]); } diff --git a/apk_frauder/src/zip_reader.rs b/apk_frauder/src/zip_reader.rs index 0b2f8e2..e131ce5 100644 --- a/apk_frauder/src/zip_reader.rs +++ b/apk_frauder/src/zip_reader.rs @@ -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 { @@ -25,29 +25,37 @@ pub struct ZipFileReader { } impl ZipFileReader { - pub fn new(mut reader: T) -> Self { + pub fn new(mut reader: T) -> Result { 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 ZipFileReader { 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 ZipFileReader { 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 ZipFileReader { 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 ZipFileReader { } pub fn get_end_of_central_directory_offset(reader: &mut T) -> Option { - 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 ZipFileReader { .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 ZipFileReader { } } - pub fn get_bin(&mut self, offset: u64, size: usize) -> Vec { - self.data.seek(SeekFrom::Start(offset)).unwrap(); + pub fn get_bin(&mut self, offset: u64, size: usize) -> Result> { + 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 { - let file = self.get_file_info(name).unwrap(); + pub fn read_file_as_vec(&mut self, name: &str) -> Result> { + 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> {