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.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
|
|
@ -67,9 +67,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.75"
|
version = "1.0.98"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
]
|
]
|
||||||
|
|
@ -79,6 +79,7 @@ name = "apk_frauder"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"androscalpel_serializer",
|
"androscalpel_serializer",
|
||||||
|
"anyhow",
|
||||||
"flate2",
|
"flate2",
|
||||||
"log",
|
"log",
|
||||||
"rand",
|
"rand",
|
||||||
|
|
|
||||||
|
|
@ -3012,7 +3012,7 @@ impl Apk {
|
||||||
+ Send
|
+ Send
|
||||||
+ Sync,
|
+ Sync,
|
||||||
{
|
{
|
||||||
let mut apk_z = ZipFileReader::new(apk);
|
let mut apk_z = ZipFileReader::new(apk)?;
|
||||||
let mut apk = Self::default();
|
let mut apk = Self::default();
|
||||||
let dex_names = apk_z
|
let dex_names = apk_z
|
||||||
.get_classes_file_info()
|
.get_classes_file_info()
|
||||||
|
|
@ -3020,7 +3020,7 @@ impl Apk {
|
||||||
.map(|info| info.get_name())
|
.map(|info| info.get_name())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
for name in dex_names {
|
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)?;
|
apk.add_dex_file(&name, &data, label_ins.clone(), cache)?;
|
||||||
}
|
}
|
||||||
Ok(apk)
|
Ok(apk)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
androscalpel_serializer = { version = "0.1.0", path = "../androscalpel_serializer" }
|
androscalpel_serializer = { version = "0.1.0", path = "../androscalpel_serializer" }
|
||||||
|
anyhow = "1.0.98"
|
||||||
flate2 = { version = "1.0.28", features = ["rust_backend"] }
|
flate2 = { version = "1.0.28", features = ["rust_backend"] }
|
||||||
log = "0.4.25"
|
log = "0.4.25"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use androscalpel_serializer::Serializable;
|
use androscalpel_serializer::Serializable;
|
||||||
|
use anyhow::{Context, Result};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
@ -162,7 +163,7 @@ pub fn replace_dex(
|
||||||
apksigner: Option<impl AsRef<Path>>,
|
apksigner: Option<impl AsRef<Path>>,
|
||||||
keypassword: Option<&str>,
|
keypassword: Option<&str>,
|
||||||
additionnal_files: Option<HashMap<String, Option<impl Read + Seek>>>,
|
additionnal_files: Option<HashMap<String, Option<impl Read + Seek>>>,
|
||||||
) {
|
) -> Result<()> {
|
||||||
let zipalign = if let Some(path) = &zipalign {
|
let zipalign = if let Some(path) = &zipalign {
|
||||||
path.as_ref().as_os_str()
|
path.as_ref().as_os_str()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -176,17 +177,22 @@ pub fn replace_dex(
|
||||||
let tmp_dir = env::temp_dir().join(format!("apk_frauder_{:x}", rand::random::<u128>()));
|
let tmp_dir = env::temp_dir().join(format!("apk_frauder_{:x}", rand::random::<u128>()));
|
||||||
let unaligned_path = tmp_dir.join("stripped.apk");
|
let unaligned_path = tmp_dir.join("stripped.apk");
|
||||||
let aligned_path = tmp_dir.join("aligned.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 file = File::open(&apk).with_context(|| {
|
||||||
let mut apk = ZipFileReader::new(file);
|
format!(
|
||||||
let file = File::create(&unaligned_path).expect("failed to create file");
|
"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 apk_out = ZipFileWriter::new(file, apk.zip64_end_of_central_directory.clone());
|
||||||
|
|
||||||
let mut file_info_ref = (*apk
|
let mut file_info_ref = (*apk
|
||||||
.get_classes_file_info()
|
.get_classes_file_info()
|
||||||
.first()
|
.first()
|
||||||
.expect("No dex file found in apk"))
|
.context("No dex file found in apk")?)
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
apk.unlink_signature_files();
|
apk.unlink_signature_files();
|
||||||
|
|
@ -233,7 +239,7 @@ pub fn replace_dex(
|
||||||
.arg(unaligned_path.as_os_str())
|
.arg(unaligned_path.as_os_str())
|
||||||
.arg(aligned_path.as_os_str())
|
.arg(aligned_path.as_os_str())
|
||||||
.status()
|
.status()
|
||||||
.unwrap();
|
.context("Failed to run zipalign")?;
|
||||||
|
|
||||||
let mut cmd = Command::new(apksigner);
|
let mut cmd = Command::new(apksigner);
|
||||||
let cmd = cmd
|
let cmd = cmd
|
||||||
|
|
@ -249,6 +255,7 @@ pub fn replace_dex(
|
||||||
} else {
|
} else {
|
||||||
cmd
|
cmd
|
||||||
};
|
};
|
||||||
cmd.status().unwrap();
|
cmd.status().context("Failled to run apksigne")?;
|
||||||
fs::remove_dir_all(tmp_dir).expect("Failled to remove tmp dir");
|
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]>>>>,
|
None::<HashMap<String, Option<Cursor<&[u8]>>>>,
|
||||||
);*/
|
);*/
|
||||||
let file = File::open("zagruski.apk").unwrap();
|
let file = File::open("zagruski.apk").unwrap();
|
||||||
let reader = ZipFileReader::new(file);
|
let reader = ZipFileReader::new(file).unwrap();
|
||||||
println!("{:#?}", &reader.files[..2]);
|
println!("{:#?}", &reader.files[..2]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,3 @@
|
||||||
use log::{info, warn};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::io::{Read, Seek, SeekFrom};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
apk_signing_block::ApkSigningBlock,
|
apk_signing_block::ApkSigningBlock,
|
||||||
apk_signing_block::Magic,
|
apk_signing_block::Magic,
|
||||||
|
|
@ -13,7 +9,11 @@ use crate::{
|
||||||
general_purpose_flags, FileHeader, FileInfo, LocalFileHeader, Signature,
|
general_purpose_flags, FileHeader, FileInfo, LocalFileHeader, Signature,
|
||||||
};
|
};
|
||||||
use androscalpel_serializer::Serializable;
|
use androscalpel_serializer::Serializable;
|
||||||
|
use anyhow::{bail, Context, Result};
|
||||||
use flate2::read::DeflateDecoder;
|
use flate2::read::DeflateDecoder;
|
||||||
|
use log::{info, warn};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct ZipFileReader<T: Read + Seek> {
|
pub struct ZipFileReader<T: Read + Seek> {
|
||||||
|
|
@ -25,29 +25,37 @@ pub struct ZipFileReader<T: Read + Seek> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Read + Seek> ZipFileReader<T> {
|
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 =
|
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
|
reader
|
||||||
.seek(SeekFrom::Start(end_of_central_directory_off))
|
.seek(SeekFrom::Start(end_of_central_directory_off))
|
||||||
.unwrap();
|
.context("Failed to seek to end of central directory")?;
|
||||||
let end_of_central_directory = EndCentralDirectory::deserialize(&mut reader).unwrap();
|
let end_of_central_directory = EndCentralDirectory::deserialize(&mut reader)
|
||||||
reader
|
.context("Failed to deserialize end of central directory")?;
|
||||||
|
let zip64_end_of_central_directory = if reader
|
||||||
.seek(SeekFrom::Start(
|
.seek(SeekFrom::Start(
|
||||||
end_of_central_directory_off - Zip64EndCentralDirectoryLocator::SIZE as u64,
|
end_of_central_directory_off - Zip64EndCentralDirectoryLocator::SIZE as u64,
|
||||||
))
|
))
|
||||||
.unwrap();
|
.is_ok()
|
||||||
let zip64_ecd_locator = Zip64EndCentralDirectoryLocator::deserialize(&mut reader).ok();
|
{
|
||||||
let zip64_end_of_central_directory = if let Some(zip64_ecd_locator) = zip64_ecd_locator {
|
let zip64_ecd_locator = Zip64EndCentralDirectoryLocator::deserialize(&mut reader).ok();
|
||||||
assert_eq!(
|
if let Some(zip64_ecd_locator) = zip64_ecd_locator {
|
||||||
zip64_ecd_locator.disk_number_of_zip64_end_central_directory_start,
|
assert_eq!(
|
||||||
0
|
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 =
|
assert!(zip64_ecd_locator.total_number_of_disks <= 1);
|
||||||
zip64_ecd_locator.offset_zip64_end_of_central_directory_record;
|
let zip64_edc_record_off =
|
||||||
reader.seek(SeekFrom::Start(zip64_edc_record_off)).unwrap();
|
zip64_ecd_locator.offset_zip64_end_of_central_directory_record;
|
||||||
Zip64EndCentralDirectory::deserialize(&mut reader).ok()
|
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 {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
@ -66,24 +74,29 @@ impl<T: Read + Seek> ZipFileReader<T> {
|
||||||
zip_file
|
zip_file
|
||||||
.data
|
.data
|
||||||
.seek(SeekFrom::Start(zip_file.get_cd_offset()))
|
.seek(SeekFrom::Start(zip_file.get_cd_offset()))
|
||||||
.unwrap();
|
.context("Failed to seek to central directory")?;
|
||||||
|
|
||||||
let mut size_read = 0;
|
let mut size_read = 0;
|
||||||
let cd_size = zip_file.get_cd_size();
|
let cd_size = zip_file.get_cd_size();
|
||||||
while size_read < 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);
|
//println!("{:#?}", header);
|
||||||
size_read += header.size() as u64;
|
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
|
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
|
zip_file
|
||||||
.data
|
.data
|
||||||
.seek(SeekFrom::Start(header.get_offset_local_header()))
|
.seek(SeekFrom::Start(header.get_offset_local_header()))
|
||||||
.unwrap();
|
.context("Failled to seek to local header")?;
|
||||||
let local_header = LocalFileHeader::deserialize(&mut zip_file.data).unwrap();
|
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
|
let data_descriptor = if (local_header.general_purpose_flags
|
||||||
& general_purpose_flags::MASK_USE_DATA_DESCRIPTOR
|
& general_purpose_flags::MASK_USE_DATA_DESCRIPTOR
|
||||||
!= 0)
|
!= 0)
|
||||||
|
|
@ -94,20 +107,25 @@ impl<T: Read + Seek> ZipFileReader<T> {
|
||||||
zip_file
|
zip_file
|
||||||
.data
|
.data
|
||||||
.seek(SeekFrom::Current(header.compressed_size as i64))
|
.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() {
|
if zip_file.zip64_end_of_central_directory.is_some() {
|
||||||
Some(DataDescriptor::Zip64(
|
Some(DataDescriptor::Zip64(
|
||||||
DataDescriptor64::deserialize(&mut zip_file.data).unwrap(),
|
DataDescriptor64::deserialize(&mut zip_file.data)
|
||||||
|
.context("Failed to deserialize data descriptor 64")?,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Some(DataDescriptor::Zip32(
|
Some(DataDescriptor::Zip32(
|
||||||
DataDescriptor32::deserialize(&mut zip_file.data).unwrap(),
|
DataDescriptor32::deserialize(&mut zip_file.data)
|
||||||
|
.context("Failed to deserialize data descriptor")?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
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 {
|
zip_file.files.push(FileInfo {
|
||||||
local_header,
|
local_header,
|
||||||
header,
|
header,
|
||||||
|
|
@ -119,24 +137,26 @@ impl<T: Read + Seek> ZipFileReader<T> {
|
||||||
zip_file
|
zip_file
|
||||||
.data
|
.data
|
||||||
.seek(SeekFrom::Start(zip_file.get_cd_offset() - 16))
|
.seek(SeekFrom::Start(zip_file.get_cd_offset() - 16))
|
||||||
.unwrap();
|
.context("Failed to seek to central directory")?;
|
||||||
let magic = Magic::deserialize(&mut zip_file.data).unwrap();
|
let magic =
|
||||||
|
Magic::deserialize(&mut zip_file.data).context("Failed to deserialize Magic")?;
|
||||||
if magic == ApkSigningBlock::MAGIC {
|
if magic == ApkSigningBlock::MAGIC {
|
||||||
zip_file
|
zip_file
|
||||||
.data
|
.data
|
||||||
.seek(SeekFrom::Start(zip_file.get_cd_offset() - 16 - 8))
|
.seek(SeekFrom::Start(zip_file.get_cd_offset() - 16 - 8))
|
||||||
.unwrap();
|
.context("Failed to seek to central directory")?;
|
||||||
let block_size = u64::deserialize(&mut zip_file.data).unwrap();
|
let block_size = u64::deserialize(&mut zip_file.data)
|
||||||
|
.context("Failed to deserialize block size")?;
|
||||||
zip_file
|
zip_file
|
||||||
.data
|
.data
|
||||||
.seek(SeekFrom::Start(zip_file.get_cd_offset() - block_size - 8))
|
.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.apk_sign_block = ApkSigningBlock::deserialize(&mut zip_file.data).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
zip_file
|
Ok(zip_file)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_zip64(&self) -> bool {
|
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> {
|
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 sig = Signature::default();
|
||||||
let mut comment_size = 0;
|
let mut comment_size = 0;
|
||||||
while sig != EndCentralDirectory::SIGNATURE {
|
while sig != EndCentralDirectory::SIGNATURE {
|
||||||
|
|
@ -203,8 +223,8 @@ impl<T: Read + Seek> ZipFileReader<T> {
|
||||||
.seek(SeekFrom::End(
|
.seek(SeekFrom::End(
|
||||||
-(EndCentralDirectory::MIN_SIZE as i64) - comment_size,
|
-(EndCentralDirectory::MIN_SIZE as i64) - comment_size,
|
||||||
))
|
))
|
||||||
.unwrap();
|
.ok()?;
|
||||||
sig = Signature::deserialize(reader).unwrap();
|
sig = Signature::deserialize(reader).ok()?;
|
||||||
comment_size += 1;
|
comment_size += 1;
|
||||||
if comment_size > 65536
|
if comment_size > 65536
|
||||||
|| comment_size as usize + EndCentralDirectory::MIN_SIZE > file_size as usize
|
|| 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> {
|
pub fn get_bin(&mut self, offset: u64, size: usize) -> Result<Vec<u8>> {
|
||||||
self.data.seek(SeekFrom::Start(offset)).unwrap();
|
self.data
|
||||||
|
.seek(SeekFrom::Start(offset))
|
||||||
|
.context("Failed to seek to data")?;
|
||||||
let mut data = vec![0u8; size];
|
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 {
|
for _ in 0..size {
|
||||||
data.push(u8::deserialize(&mut self.data).unwrap());
|
data.push(u8::deserialize(&mut self.data).unwrap());
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
data
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_file_as_vec(&mut self, name: &str) -> Vec<u8> {
|
pub fn read_file_as_vec(&mut self, name: &str) -> Result<Vec<u8>> {
|
||||||
let file = self.get_file_info(name).unwrap();
|
let file = self
|
||||||
|
.get_file_info(name)
|
||||||
|
.with_context(|| format!("Failed to get info for {name}"))?;
|
||||||
let offset = file.get_file_offset();
|
let offset = file.get_file_offset();
|
||||||
let size_c = file.header.compressed_size as usize;
|
let size_c = file.header.compressed_size as usize;
|
||||||
let size = file.header.uncompressed_size as usize;
|
let size = file.header.uncompressed_size as usize;
|
||||||
let compression_method = file.header.compression_method;
|
let compression_method = file.header.compression_method;
|
||||||
let mut data = vec![0u8; size_c];
|
let mut data = vec![0u8; size_c];
|
||||||
self.data.seek(SeekFrom::Start(offset)).unwrap();
|
self.data
|
||||||
self.data.read_exact(&mut data).unwrap();
|
.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 {
|
match compression_method {
|
||||||
CompressionMethod::Stored => {}
|
CompressionMethod::Stored => {}
|
||||||
CompressionMethod::Deflated => {
|
CompressionMethod::Deflated => {
|
||||||
let mut decomp_data = vec![0u8; size];
|
let mut decomp_data = vec![0u8; size];
|
||||||
let mut deflater = DeflateDecoder::new(&data[..]);
|
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
|
data = decomp_data
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
data
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_file_info(&self, name: &str) -> Option<&FileInfo> {
|
pub fn get_file_info(&self, name: &str) -> Option<&FileInfo> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue