start adding error handling to apk_frauder
This commit is contained in:
parent
615f7a8f52
commit
5940434694
6 changed files with 106 additions and 65 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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::<Vec<_>>();
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue