add support for data descriptor
This commit is contained in:
parent
28f47eaba4
commit
047d2636c3
3 changed files with 195 additions and 78 deletions
|
|
@ -143,7 +143,74 @@ impl FileInfo {
|
|||
}
|
||||
}
|
||||
|
||||
/// Replace the dex files a an apk an resigned the apk.
|
||||
/// Replace the dex files in an apk and strip the old signature.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn replace_dex_unsigned(
|
||||
apk: impl AsRef<Path>,
|
||||
dst: impl AsRef<Path>,
|
||||
dexfiles: &mut [impl Read + Seek],
|
||||
additionnal_files: Option<HashMap<String, Option<impl Read + Seek>>>,
|
||||
) -> Result<()> {
|
||||
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(&dst).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()
|
||||
.context("No dex file found in apk")?)
|
||||
.clone();
|
||||
|
||||
apk.unlink_signature_files();
|
||||
apk.unlink_bytecode_files();
|
||||
|
||||
if let Some(additionnal_files) = &additionnal_files {
|
||||
apk.files
|
||||
.retain(|file| additionnal_files.get(&file.get_name()).is_none());
|
||||
}
|
||||
|
||||
for f in apk.files.clone() {
|
||||
apk_out.insert_file_from_zip(f, &mut apk);
|
||||
}
|
||||
|
||||
for (i, mut dex) in dexfiles.iter_mut().enumerate() {
|
||||
if i == 0 {
|
||||
file_info_ref.set_name("classes.dex");
|
||||
} else {
|
||||
file_info_ref.set_name(&format!("classes{}.dex", i + 1));
|
||||
}
|
||||
apk_out.insert_file(
|
||||
&mut dex,
|
||||
file_info_ref.header.clone(),
|
||||
Some(file_info_ref.local_header.clone()),
|
||||
file_info_ref.data_descriptor.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(mut additionnal_files) = additionnal_files {
|
||||
for (name, data) in &mut additionnal_files {
|
||||
file_info_ref.set_name(name);
|
||||
if let Some(data) = data.as_mut() {
|
||||
apk_out.insert_file(
|
||||
data,
|
||||
file_info_ref.header.clone(),
|
||||
Some(file_info_ref.local_header.clone()),
|
||||
file_info_ref.data_descriptor.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
apk_out.write_central_directory();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Replace the dex files in an apk an resigned the apk.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
|
|
@ -177,58 +244,8 @@ pub fn replace_dex(
|
|||
let aligned_path = tmp_dir.join("aligned.apk");
|
||||
fs::create_dir_all(&tmp_dir).context("Failed to create temporary directory")?;
|
||||
|
||||
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());
|
||||
replace_dex_unsigned(apk, &unaligned_path, dexfiles, additionnal_files)?;
|
||||
|
||||
let mut file_info_ref = (*apk
|
||||
.get_classes_file_info()
|
||||
.first()
|
||||
.context("No dex file found in apk")?)
|
||||
.clone();
|
||||
|
||||
apk.unlink_signature_files();
|
||||
apk.unlink_bytecode_files();
|
||||
if let Some(additionnal_files) = &additionnal_files {
|
||||
apk.files
|
||||
.retain(|file| additionnal_files.get(&file.get_name()).is_none());
|
||||
}
|
||||
for f in apk.files.clone() {
|
||||
apk_out.insert_file_from_zip(f, &mut apk);
|
||||
}
|
||||
for (i, mut dex) in dexfiles.iter_mut().enumerate() {
|
||||
if i == 0 {
|
||||
file_info_ref.set_name("classes.dex");
|
||||
} else {
|
||||
file_info_ref.set_name(&format!("classes{}.dex", i + 1));
|
||||
}
|
||||
apk_out.insert_file(
|
||||
&mut dex,
|
||||
file_info_ref.header.clone(),
|
||||
Some(file_info_ref.local_header.clone()),
|
||||
file_info_ref.data_descriptor.clone(),
|
||||
);
|
||||
}
|
||||
if let Some(mut additionnal_files) = additionnal_files {
|
||||
for (name, data) in &mut additionnal_files {
|
||||
file_info_ref.set_name(name);
|
||||
if let Some(data) = data.as_mut() {
|
||||
apk_out.insert_file(
|
||||
data,
|
||||
file_info_ref.header.clone(),
|
||||
Some(file_info_ref.local_header.clone()),
|
||||
file_info_ref.data_descriptor.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
apk_out.write_central_directory();
|
||||
// TODO: we can probably do that ourself an spare ourself the trouble of finding zipalign
|
||||
Command::new(zipalign)
|
||||
.arg("-v")
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ use std::io;
|
|||
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
|
||||
|
||||
use crate::compression::CompressionMethod;
|
||||
use crate::data_descriptor::DataDescriptor;
|
||||
use crate::data_descriptor::{DataDescriptor, DataDescriptor32, DataDescriptor64};
|
||||
use crate::end_of_central_directory::{
|
||||
EndCentralDirectory, Zip64EndCentralDirectory, Zip64EndCentralDirectoryLocator,
|
||||
};
|
||||
use crate::{FileHeader, FileInfo, LocalFileHeader, ZipFileReader, general_purpose_flags};
|
||||
use crate::{
|
||||
general_purpose_flags, ExtraField, FileHeader, FileInfo, LocalFileHeader, ZipFileReader,
|
||||
};
|
||||
use androscalpel_serializer::Serializable;
|
||||
use flate2::write::DeflateEncoder;
|
||||
use flate2::{Compression, CrcWriter};
|
||||
|
|
@ -67,9 +69,13 @@ impl<T: Write> ZipFileWriter<T> {
|
|||
data_descriptor: Option<DataDescriptor>,
|
||||
) {
|
||||
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_USE_DATA_DESCRIPTOR == 0,
|
||||
// "Writing file with data_descriptor is not yet implemented"
|
||||
//); // TODO
|
||||
let mut use_data_descriptor = data_descriptor.is_some()
|
||||
|| ((header.general_purpose_flags & general_purpose_flags::MASK_USE_DATA_DESCRIPTOR)
|
||||
!= 0);
|
||||
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
|
||||
|
|
@ -88,9 +94,12 @@ impl<T: Write> ZipFileWriter<T> {
|
|||
);
|
||||
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_USE_DATA_DESCRIPTOR == 0,
|
||||
// "Writing file with data_descriptor is not yet implemented"
|
||||
//); // TODO
|
||||
use_data_descriptor |=
|
||||
header.general_purpose_flags & general_purpose_flags::MASK_USE_DATA_DESCRIPTOR != 0;
|
||||
assert!(
|
||||
header.general_purpose_flags & general_purpose_flags::MASK_STRONG_ENCRYPTION == 0
|
||||
);
|
||||
|
|
@ -110,15 +119,20 @@ impl<T: Write> ZipFileWriter<T> {
|
|||
header.compression_method == CompressionMethod::Deflated
|
||||
|| header.compression_method == CompressionMethod::Stored
|
||||
);
|
||||
let mut general_purpose_flags = header.general_purpose_flags;
|
||||
// We only support options for Deflate parameter and data descriptor
|
||||
if header.compression_method == CompressionMethod::Deflated {
|
||||
general_purpose_flags &= general_purpose_flags::MASK_COMPRESS_OPTION_1
|
||||
| general_purpose_flags::MASK_COMPRESS_OPTION_2;
|
||||
} else {
|
||||
general_purpose_flags = 0
|
||||
}
|
||||
if use_data_descriptor {
|
||||
general_purpose_flags |= general_purpose_flags::MASK_USE_DATA_DESCRIPTOR;
|
||||
}
|
||||
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
|
||||
},
|
||||
general_purpose_flags,
|
||||
compression_method: header.compression_method,
|
||||
last_mod_file_time: header.last_mod_file_time,
|
||||
last_mod_file_data: header.last_mod_file_data,
|
||||
|
|
@ -139,6 +153,10 @@ impl<T: Write> ZipFileWriter<T> {
|
|||
| general_purpose_flags::MASK_COMPRESS_OPTION_2)
|
||||
} else {
|
||||
0
|
||||
} | if use_data_descriptor {
|
||||
general_purpose_flags::MASK_USE_DATA_DESCRIPTOR
|
||||
} else {
|
||||
0
|
||||
},
|
||||
compression_method: header.compression_method,
|
||||
last_mod_file_time: header.last_mod_file_time,
|
||||
|
|
@ -198,9 +216,74 @@ impl<T: Write> ZipFileWriter<T> {
|
|||
header.set_uncompressed_size(local_header.get_uncompressed_size());
|
||||
header.set_offset_local_header(header_offset);
|
||||
|
||||
if data_descriptor.is_some() {
|
||||
panic!("Writing file with data_descriptor is not yet implemented");
|
||||
let use_z64 = header
|
||||
.extra_field
|
||||
.iter()
|
||||
.any(|f| matches!(f, ExtraField::Zip64(_)));
|
||||
|
||||
let data_descriptor = if use_data_descriptor {
|
||||
match data_descriptor {
|
||||
None if use_z64 => Some(DataDescriptor::Zip64(DataDescriptor64 {
|
||||
crc_32: local_header.crc_32,
|
||||
compressed_size: local_header.get_compressed_size(),
|
||||
uncompressed_size: local_header.get_uncompressed_size(),
|
||||
use_signature: true,
|
||||
})),
|
||||
None => Some(DataDescriptor::Zip32(DataDescriptor32 {
|
||||
crc_32: local_header.crc_32,
|
||||
compressed_size: local_header.get_compressed_size() as u32,
|
||||
uncompressed_size: local_header.get_uncompressed_size() as u32,
|
||||
use_signature: true,
|
||||
})),
|
||||
Some(DataDescriptor::Zip32(DataDescriptor32 { use_signature, .. })) if use_z64 => {
|
||||
Some(DataDescriptor::Zip64(DataDescriptor64 {
|
||||
crc_32: local_header.crc_32,
|
||||
compressed_size: local_header.get_compressed_size(),
|
||||
uncompressed_size: local_header.get_uncompressed_size(),
|
||||
use_signature,
|
||||
}))
|
||||
}
|
||||
Some(DataDescriptor::Zip64(DataDescriptor64 { use_signature, .. })) if !use_z64 => {
|
||||
Some(DataDescriptor::Zip32(DataDescriptor32 {
|
||||
crc_32: local_header.crc_32,
|
||||
compressed_size: local_header.get_compressed_size() as u32,
|
||||
uncompressed_size: local_header.get_uncompressed_size() as u32,
|
||||
use_signature,
|
||||
}))
|
||||
}
|
||||
Some(DataDescriptor::Zip64(DataDescriptor64 { use_signature, .. })) => {
|
||||
Some(DataDescriptor::Zip64(DataDescriptor64 {
|
||||
crc_32: local_header.crc_32,
|
||||
compressed_size: local_header.get_compressed_size(),
|
||||
uncompressed_size: local_header.get_uncompressed_size(),
|
||||
use_signature,
|
||||
}))
|
||||
}
|
||||
Some(DataDescriptor::Zip32(DataDescriptor32 { use_signature, .. })) => {
|
||||
Some(DataDescriptor::Zip32(DataDescriptor32 {
|
||||
crc_32: local_header.crc_32,
|
||||
compressed_size: local_header.get_compressed_size() as u32,
|
||||
uncompressed_size: local_header.get_uncompressed_size() as u32,
|
||||
use_signature,
|
||||
}))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match &data_descriptor {
|
||||
Some(DataDescriptor::Zip32(data_descriptor)) => {
|
||||
data_descriptor.serialize(&mut self.data).unwrap();
|
||||
self.current_offset += data_descriptor.size() as u64;
|
||||
}
|
||||
Some(DataDescriptor::Zip64(data_descriptor)) => {
|
||||
data_descriptor.serialize(&mut self.data).unwrap();
|
||||
self.current_offset += data_descriptor.size() as u64;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let file_info = FileInfo {
|
||||
local_header,
|
||||
header,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue