add crc
This commit is contained in:
parent
99ecf178df
commit
3f521b5754
4 changed files with 277 additions and 5 deletions
|
|
@ -220,6 +220,31 @@ impl FileHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_uncompressed_size(&mut self, uncompressed_size: u64) {
|
||||||
|
if let Some(ExtraField::Zip64(Zip64ExtraField {
|
||||||
|
original_size: Some(original_size),
|
||||||
|
..
|
||||||
|
})) = self
|
||||||
|
.extra_field
|
||||||
|
.iter_mut()
|
||||||
|
.find(|f| matches!(f, ExtraField::Zip64(_)))
|
||||||
|
{
|
||||||
|
*original_size = uncompressed_size;
|
||||||
|
self.uncompressed_size = u32::MAX;
|
||||||
|
} else if uncompressed_size > u32::MAX as u64 {
|
||||||
|
self.extra_field.push(ExtraField::Zip64(Zip64ExtraField {
|
||||||
|
original_size: Some(uncompressed_size),
|
||||||
|
compressed_size: Some(self.compressed_size as u64),
|
||||||
|
disk_number: None,
|
||||||
|
offset_header: None,
|
||||||
|
}));
|
||||||
|
self.uncompressed_size = u32::MAX;
|
||||||
|
self.compressed_size = u32::MAX;
|
||||||
|
} else {
|
||||||
|
self.uncompressed_size = uncompressed_size as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_compressed_size(&self) -> u64 {
|
pub fn get_compressed_size(&self) -> u64 {
|
||||||
if self.compressed_size != u32::MAX {
|
if self.compressed_size != u32::MAX {
|
||||||
self.compressed_size as u64
|
self.compressed_size as u64
|
||||||
|
|
@ -237,6 +262,31 @@ impl FileHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_compressed_size(&mut self, compressed_size: u64) {
|
||||||
|
if let Some(ExtraField::Zip64(Zip64ExtraField {
|
||||||
|
compressed_size: Some(compressed_size_),
|
||||||
|
..
|
||||||
|
})) = self
|
||||||
|
.extra_field
|
||||||
|
.iter_mut()
|
||||||
|
.find(|f| matches!(f, ExtraField::Zip64(_)))
|
||||||
|
{
|
||||||
|
*compressed_size_ = compressed_size;
|
||||||
|
self.compressed_size = u32::MAX;
|
||||||
|
} else if compressed_size > u32::MAX as u64 {
|
||||||
|
self.extra_field.push(ExtraField::Zip64(Zip64ExtraField {
|
||||||
|
original_size: Some(self.uncompressed_size as u64),
|
||||||
|
compressed_size: Some(compressed_size),
|
||||||
|
disk_number: None,
|
||||||
|
offset_header: None,
|
||||||
|
}));
|
||||||
|
self.uncompressed_size = u32::MAX;
|
||||||
|
self.compressed_size = u32::MAX;
|
||||||
|
} else {
|
||||||
|
self.compressed_size = compressed_size as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_offset_local_header(&self) -> u64 {
|
pub fn get_offset_local_header(&self) -> u64 {
|
||||||
if self.offset_local_header != u32::MAX {
|
if self.offset_local_header != u32::MAX {
|
||||||
self.offset_local_header as u64
|
self.offset_local_header as u64
|
||||||
|
|
@ -254,6 +304,32 @@ impl FileHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_offset_local_header(&mut self, offset: u64) {
|
||||||
|
if let Some(ExtraField::Zip64(Zip64ExtraField {
|
||||||
|
offset_header: Some(offset_header),
|
||||||
|
..
|
||||||
|
})) = self
|
||||||
|
.extra_field
|
||||||
|
.iter_mut()
|
||||||
|
.find(|f| matches!(f, ExtraField::Zip64(_)))
|
||||||
|
{
|
||||||
|
*offset_header = offset;
|
||||||
|
self.offset_local_header = u32::MAX;
|
||||||
|
} else if offset > u32::MAX as u64 {
|
||||||
|
self.extra_field.push(ExtraField::Zip64(Zip64ExtraField {
|
||||||
|
original_size: Some(self.uncompressed_size as u64),
|
||||||
|
compressed_size: Some(self.compressed_size as u64),
|
||||||
|
disk_number: None,
|
||||||
|
offset_header: Some(offset),
|
||||||
|
}));
|
||||||
|
self.uncompressed_size = u32::MAX;
|
||||||
|
self.compressed_size = u32::MAX;
|
||||||
|
self.offset_local_header = u32::MAX;
|
||||||
|
} else {
|
||||||
|
self.offset_local_header = offset as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_disk_number_start(&self) -> u32 {
|
pub fn get_disk_number_start(&self) -> u32 {
|
||||||
if self.disk_number_start != u16::MAX {
|
if self.disk_number_start != u16::MAX {
|
||||||
self.disk_number_start as u32
|
self.disk_number_start as u32
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,31 @@ impl LocalFileHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_uncompressed_size(&mut self, uncompressed_size: u64) {
|
||||||
|
if let Some(ExtraField::Zip64(Zip64ExtraField {
|
||||||
|
original_size: Some(original_size),
|
||||||
|
..
|
||||||
|
})) = self
|
||||||
|
.extra_field
|
||||||
|
.iter_mut()
|
||||||
|
.find(|f| matches!(f, ExtraField::Zip64(_)))
|
||||||
|
{
|
||||||
|
*original_size = uncompressed_size;
|
||||||
|
self.uncompressed_size = u32::MAX;
|
||||||
|
} else if uncompressed_size > u32::MAX as u64 {
|
||||||
|
self.extra_field.push(ExtraField::Zip64(Zip64ExtraField {
|
||||||
|
original_size: Some(uncompressed_size),
|
||||||
|
compressed_size: Some(self.compressed_size as u64),
|
||||||
|
disk_number: None,
|
||||||
|
offset_header: None,
|
||||||
|
}));
|
||||||
|
self.uncompressed_size = u32::MAX;
|
||||||
|
self.compressed_size = u32::MAX;
|
||||||
|
} else {
|
||||||
|
self.uncompressed_size = uncompressed_size as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_compressed_size(&self) -> u64 {
|
pub fn get_compressed_size(&self) -> u64 {
|
||||||
if self.compressed_size != u32::MAX {
|
if self.compressed_size != u32::MAX {
|
||||||
self.compressed_size as u64
|
self.compressed_size as u64
|
||||||
|
|
@ -231,6 +256,31 @@ impl LocalFileHeader {
|
||||||
self.compressed_size as u64
|
self.compressed_size as u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_compressed_size(&mut self, compressed_size: u64) {
|
||||||
|
if let Some(ExtraField::Zip64(Zip64ExtraField {
|
||||||
|
compressed_size: Some(compressed_size_),
|
||||||
|
..
|
||||||
|
})) = self
|
||||||
|
.extra_field
|
||||||
|
.iter_mut()
|
||||||
|
.find(|f| matches!(f, ExtraField::Zip64(_)))
|
||||||
|
{
|
||||||
|
*compressed_size_ = compressed_size;
|
||||||
|
self.compressed_size = u32::MAX;
|
||||||
|
} else if compressed_size > u32::MAX as u64 {
|
||||||
|
self.extra_field.push(ExtraField::Zip64(Zip64ExtraField {
|
||||||
|
original_size: Some(self.uncompressed_size as u64),
|
||||||
|
compressed_size: Some(compressed_size),
|
||||||
|
disk_number: None,
|
||||||
|
offset_header: None,
|
||||||
|
}));
|
||||||
|
self.uncompressed_size = u32::MAX;
|
||||||
|
self.compressed_size = u32::MAX;
|
||||||
|
} else {
|
||||||
|
self.compressed_size = compressed_size as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ use apk_frauder::ZipFileWriter;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
/*
|
|
||||||
let file = File::open("app-release.apk").expect("failed to open file");
|
let file = File::open("app-release.apk").expect("failed to open file");
|
||||||
//let file = File::open("tst_64.zip").expect("failed to open file");
|
//let file = File::open("tst_64.zip").expect("failed to open file");
|
||||||
let zip_file = ZipFileReader::new(file);
|
let zip_file = ZipFileReader::new(file);
|
||||||
|
/*
|
||||||
//println!("{}", zip_file.get_file_names().join("\n"));
|
//println!("{}", zip_file.get_file_names().join("\n"));
|
||||||
for file in &zip_file.files {
|
for file in &zip_file.files {
|
||||||
println!("{}", file.get_name());
|
println!("{}", file.get_name());
|
||||||
|
|
@ -34,9 +34,10 @@ fn main() {
|
||||||
println!("Not signed whith scheme >= v2");
|
println!("Not signed whith scheme >= v2");
|
||||||
}
|
}
|
||||||
zip_file.check_holes();
|
zip_file.check_holes();
|
||||||
println!("{:#?}", zip_file.get_file_info("classes.dex"));
|
|
||||||
*/
|
*/
|
||||||
|
println!("{:#?}", zip_file.get_file_info("classes.dex"));
|
||||||
|
|
||||||
|
/*
|
||||||
let file = File::open("tst_64.zip").expect("failed to open file");
|
let file = File::open("tst_64.zip").expect("failed to open file");
|
||||||
let out_file = File::create("tst_64.out.zip").expect("failed to create file");
|
let out_file = File::create("tst_64.out.zip").expect("failed to create file");
|
||||||
let mut zip_file = ZipFileReader::new(file);
|
let mut zip_file = ZipFileReader::new(file);
|
||||||
|
|
@ -46,5 +47,6 @@ fn main() {
|
||||||
out_file.insert_file_from_zip(f, &mut zip_file);
|
out_file.insert_file_from_zip(f, &mut zip_file);
|
||||||
}
|
}
|
||||||
out_file.write_central_directory();
|
out_file.write_central_directory();
|
||||||
|
*/
|
||||||
//println!("{:#?}", zip_file.zip64_end_of_central_directory);
|
//println!("{:#?}", zip_file.zip64_end_of_central_directory);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
use std::io;
|
||||||
|
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
|
||||||
|
|
||||||
|
use crate::compression::CompressionMethod;
|
||||||
use crate::end_of_central_directory::{
|
use crate::end_of_central_directory::{
|
||||||
EndCentralDirectory, Zip64EndCentralDirectory, Zip64EndCentralDirectoryLocator,
|
EndCentralDirectory, Zip64EndCentralDirectory, Zip64EndCentralDirectoryLocator,
|
||||||
};
|
};
|
||||||
use crate::{FileInfo, ZipFileReader};
|
use crate::{general_purpose_flags, FileHeader, FileInfo, LocalFileHeader, ZipFileReader};
|
||||||
use androscalpel_serializer::Serializable;
|
use androscalpel_serializer::Serializable;
|
||||||
|
use flate2::write::DeflateEncoder;
|
||||||
|
use flate2::{Compression, CrcWriter};
|
||||||
|
|
||||||
pub struct ZipFileWriter<T: Write> {
|
pub struct ZipFileWriter<T: Write> {
|
||||||
files: Vec<FileInfo>,
|
files: Vec<FileInfo>,
|
||||||
|
|
@ -25,7 +29,7 @@ impl<T: Write> ZipFileWriter<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert an file from an existing zip
|
/// Insert a file from an existing zip
|
||||||
pub fn insert_file_from_zip<U: Read + Seek>(
|
pub fn insert_file_from_zip<U: Read + Seek>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut file_info: FileInfo,
|
mut file_info: FileInfo,
|
||||||
|
|
@ -50,6 +54,146 @@ impl<T: Write> ZipFileWriter<T> {
|
||||||
self.files.push(file_info);
|
self.files.push(file_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert a file
|
||||||
|
/// `header` provide for the values that are not computed from the file.
|
||||||
|
/// `local_header` can be provided is information in the local header differ
|
||||||
|
/// from the central directory header.
|
||||||
|
pub fn insert_file<U: Read + Seek>(
|
||||||
|
&mut self,
|
||||||
|
file: &mut U,
|
||||||
|
mut header: FileHeader,
|
||||||
|
local_header: Option<LocalFileHeader>,
|
||||||
|
) {
|
||||||
|
assert!(header.general_purpose_flags & general_purpose_flags::MASK_ENCRYPTED == 0);
|
||||||
|
assert!(
|
||||||
|
header.general_purpose_flags & general_purpose_flags::MASK_USE_DATA_DESCRIPTOR == 0
|
||||||
|
); // TODO
|
||||||
|
assert!(header.general_purpose_flags & general_purpose_flags::MASK_STRONG_ENCRYPTION == 0);
|
||||||
|
assert!(
|
||||||
|
header.general_purpose_flags & general_purpose_flags::MASK_ENCRYPTED_CENTRAL_DIR == 0
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
header.general_purpose_flags
|
||||||
|
& (general_purpose_flags::MASK_COMPRESS_OPTION_1
|
||||||
|
| general_purpose_flags::MASK_COMPRESS_OPTION_2
|
||||||
|
| general_purpose_flags::MASK_UTF8_FILENAME)
|
||||||
|
== 0
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
header.compression_method == CompressionMethod::Deflated
|
||||||
|
|| header.compression_method == CompressionMethod::Stored
|
||||||
|
);
|
||||||
|
let mut local_header = if let Some(header) = local_header {
|
||||||
|
assert!(header.general_purpose_flags & general_purpose_flags::MASK_ENCRYPTED == 0);
|
||||||
|
assert!(
|
||||||
|
header.general_purpose_flags & general_purpose_flags::MASK_USE_DATA_DESCRIPTOR == 0
|
||||||
|
); // TODO
|
||||||
|
assert!(
|
||||||
|
header.general_purpose_flags & general_purpose_flags::MASK_STRONG_ENCRYPTION == 0
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
header.general_purpose_flags & general_purpose_flags::MASK_ENCRYPTED_CENTRAL_DIR
|
||||||
|
== 0
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
header.general_purpose_flags
|
||||||
|
& (general_purpose_flags::MASK_COMPRESS_OPTION_1
|
||||||
|
| general_purpose_flags::MASK_COMPRESS_OPTION_2
|
||||||
|
| general_purpose_flags::MASK_UTF8_FILENAME)
|
||||||
|
== 0
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
header.compression_method == CompressionMethod::Deflated
|
||||||
|
|| header.compression_method == CompressionMethod::Stored
|
||||||
|
);
|
||||||
|
LocalFileHeader {
|
||||||
|
version_needed_to_extract: header.version_needed_to_extract,
|
||||||
|
general_purpose_flags: if header.compression_method == CompressionMethod::Deflated {
|
||||||
|
header.general_purpose_flags
|
||||||
|
& (general_purpose_flags::MASK_COMPRESS_OPTION_1
|
||||||
|
| general_purpose_flags::MASK_COMPRESS_OPTION_2)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
},
|
||||||
|
compression_method: header.compression_method,
|
||||||
|
last_mod_file_time: header.last_mod_file_time,
|
||||||
|
last_mod_file_data: header.last_mod_file_data,
|
||||||
|
crc_32: 0,
|
||||||
|
compressed_size: 0,
|
||||||
|
uncompressed_size: 0,
|
||||||
|
file_name: header.file_name,
|
||||||
|
extra_field: header.extra_field,
|
||||||
|
malformed_extra_field: header.malformed_extra_field.clone(),
|
||||||
|
decryption_header: None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LocalFileHeader {
|
||||||
|
version_needed_to_extract: header.version_needed_to_extract,
|
||||||
|
general_purpose_flags: if header.compression_method == CompressionMethod::Deflated {
|
||||||
|
header.general_purpose_flags
|
||||||
|
& (general_purpose_flags::MASK_COMPRESS_OPTION_1
|
||||||
|
| general_purpose_flags::MASK_COMPRESS_OPTION_2)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
},
|
||||||
|
compression_method: header.compression_method,
|
||||||
|
last_mod_file_time: header.last_mod_file_time,
|
||||||
|
last_mod_file_data: header.last_mod_file_data,
|
||||||
|
crc_32: 0,
|
||||||
|
compressed_size: 0,
|
||||||
|
uncompressed_size: 0,
|
||||||
|
file_name: header.file_name.clone(),
|
||||||
|
extra_field: header.extra_field.clone(),
|
||||||
|
malformed_extra_field: header.malformed_extra_field.clone(),
|
||||||
|
decryption_header: None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let pos = file.stream_position().unwrap();
|
||||||
|
let pos_end = file.seek(SeekFrom::End(0)).unwrap();
|
||||||
|
file.seek(SeekFrom::Start(pos)).unwrap();
|
||||||
|
let uncompressed_size = pos_end - pos;
|
||||||
|
let header_offset = self.current_offset;
|
||||||
|
local_header.set_uncompressed_size(uncompressed_size);
|
||||||
|
|
||||||
|
let data = match local_header.compression_method {
|
||||||
|
CompressionMethod::Stored => {
|
||||||
|
let mut crc_writer = CrcWriter::new(Vec::new());
|
||||||
|
io::copy(file, &mut crc_writer).unwrap();
|
||||||
|
local_header.crc_32 = crc_writer.crc().sum();
|
||||||
|
crc_writer.into_inner()
|
||||||
|
}
|
||||||
|
CompressionMethod::Deflated => {
|
||||||
|
// TODO: find a way to do this in place, the compressed data can be large, and storing it
|
||||||
|
// in memory is bad, but Deflate consume the Reader, so we cannot juste give it self.data
|
||||||
|
// TODO: Compression::default -> nop, use flags
|
||||||
|
let mut compressor =
|
||||||
|
DeflateEncoder::new(CrcWriter::new(Vec::new()), Compression::default());
|
||||||
|
io::copy(file, &mut compressor).unwrap();
|
||||||
|
let finished = compressor.finish().unwrap();
|
||||||
|
local_header.crc_32 = finished.crc().sum();
|
||||||
|
finished.into_inner()
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
local_header.set_compressed_size(data.len() as u64);
|
||||||
|
local_header.serialize(&mut self.data).unwrap();
|
||||||
|
self.current_offset += local_header.size() as u64;
|
||||||
|
io::copy(&mut Cursor::new(data), &mut self.data).unwrap();
|
||||||
|
self.current_offset += local_header.get_compressed_size();
|
||||||
|
|
||||||
|
header.crc_32 = local_header.crc_32;
|
||||||
|
header.set_compressed_size(local_header.get_compressed_size());
|
||||||
|
header.set_uncompressed_size(local_header.get_uncompressed_size());
|
||||||
|
header.set_offset_local_header(header_offset);
|
||||||
|
|
||||||
|
let file_info = FileInfo {
|
||||||
|
local_header,
|
||||||
|
header,
|
||||||
|
};
|
||||||
|
self.files.push(file_info);
|
||||||
|
}
|
||||||
|
|
||||||
/// Finish the zip file by writing the central directory.
|
/// Finish the zip file by writing the central directory.
|
||||||
pub fn write_central_directory(&mut self) {
|
pub fn write_central_directory(&mut self) {
|
||||||
for file_info in &self.files {
|
for file_info in &self.files {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue