explore files
This commit is contained in:
parent
0794aac016
commit
0d305fbe62
5 changed files with 189 additions and 47 deletions
|
|
@ -51,6 +51,6 @@ impl Serializable for ApkSigningBlock {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&self) -> usize {
|
fn size(&self) -> usize {
|
||||||
self.data.len() + 16 + 4 + 4
|
self.data.len() + 16 + 8 + 8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::io::{SeekFrom, Write};
|
use std::io::{SeekFrom, Write};
|
||||||
|
|
||||||
use crate::extra_fields::{ExtraField, GenericExtraField, Zip64ExtraField};
|
use crate::extra_fields::{ExtraField, GenericExtraField, Zip64ExtraField};
|
||||||
use crate::{cp437, Encoding, Signature};
|
use crate::{cp437, general_purpose_flags, Encoding, Signature};
|
||||||
use androscalpel_serializer::{ReadSeek, Result, Serializable};
|
use androscalpel_serializer::{ReadSeek, Result, Serializable};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|
@ -9,7 +9,7 @@ pub struct FileHeader {
|
||||||
// signature: Signature(0x02014b50)
|
// signature: Signature(0x02014b50)
|
||||||
pub version_made_by: u16,
|
pub version_made_by: u16,
|
||||||
pub version_needed_to_extract: u16,
|
pub version_needed_to_extract: u16,
|
||||||
pub general_purpose_flag: u16,
|
pub general_purpose_flags: u16,
|
||||||
pub compression_method: u16,
|
pub compression_method: u16,
|
||||||
pub last_mod_file_time: u16,
|
pub last_mod_file_time: u16,
|
||||||
pub last_mod_file_data: u16,
|
pub last_mod_file_data: u16,
|
||||||
|
|
@ -35,7 +35,7 @@ impl Serializable for FileHeader {
|
||||||
Self::SIGNATURE.serialize(output)?;
|
Self::SIGNATURE.serialize(output)?;
|
||||||
self.version_made_by.serialize(output)?;
|
self.version_made_by.serialize(output)?;
|
||||||
self.version_needed_to_extract.serialize(output)?;
|
self.version_needed_to_extract.serialize(output)?;
|
||||||
self.general_purpose_flag.serialize(output)?;
|
self.general_purpose_flags.serialize(output)?;
|
||||||
self.compression_method.serialize(output)?;
|
self.compression_method.serialize(output)?;
|
||||||
self.last_mod_file_time.serialize(output)?;
|
self.last_mod_file_time.serialize(output)?;
|
||||||
self.last_mod_file_data.serialize(output)?;
|
self.last_mod_file_data.serialize(output)?;
|
||||||
|
|
@ -69,7 +69,7 @@ impl Serializable for FileHeader {
|
||||||
assert_eq!(signature, Self::SIGNATURE); // TODO
|
assert_eq!(signature, Self::SIGNATURE); // TODO
|
||||||
let version_made_by = u16::deserialize(input)?;
|
let version_made_by = u16::deserialize(input)?;
|
||||||
let version_needed_to_extract = u16::deserialize(input)?;
|
let version_needed_to_extract = u16::deserialize(input)?;
|
||||||
let general_purpose_flag = u16::deserialize(input)?;
|
let general_purpose_flags = u16::deserialize(input)?;
|
||||||
let compression_method = u16::deserialize(input)?;
|
let compression_method = u16::deserialize(input)?;
|
||||||
let last_mod_file_time = u16::deserialize(input)?;
|
let last_mod_file_time = u16::deserialize(input)?;
|
||||||
let last_mod_file_data = u16::deserialize(input)?;
|
let last_mod_file_data = u16::deserialize(input)?;
|
||||||
|
|
@ -90,7 +90,7 @@ impl Serializable for FileHeader {
|
||||||
let mut header = Self {
|
let mut header = Self {
|
||||||
version_made_by,
|
version_made_by,
|
||||||
version_needed_to_extract,
|
version_needed_to_extract,
|
||||||
general_purpose_flag,
|
general_purpose_flags,
|
||||||
compression_method,
|
compression_method,
|
||||||
last_mod_file_time,
|
last_mod_file_time,
|
||||||
last_mod_file_data,
|
last_mod_file_data,
|
||||||
|
|
@ -181,10 +181,8 @@ impl FileHeader {
|
||||||
const SIGNATURE: Signature = Signature(0x02014b50);
|
const SIGNATURE: Signature = Signature(0x02014b50);
|
||||||
const MIN_SIZE: usize = 4 + 6 * 2 + 4 * 3 + 5 * 2 + 4 * 2;
|
const MIN_SIZE: usize = 4 + 6 * 2 + 4 * 3 + 5 * 2 + 4 * 2;
|
||||||
|
|
||||||
const MASK_UTF8_FILENAME: u16 = 1 << 11;
|
|
||||||
|
|
||||||
pub fn get_name_encoding(&self) -> Encoding {
|
pub fn get_name_encoding(&self) -> Encoding {
|
||||||
if self.general_purpose_flag & Self::MASK_UTF8_FILENAME != 0 {
|
if self.general_purpose_flags & general_purpose_flags::MASK_UTF8_FILENAME != 0 {
|
||||||
Encoding::UTF8
|
Encoding::UTF8
|
||||||
} else {
|
} else {
|
||||||
Encoding::CP437
|
Encoding::CP437
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ pub mod local_file_header;
|
||||||
use apk_signing_block::*;
|
use apk_signing_block::*;
|
||||||
use end_of_central_directory::*;
|
use end_of_central_directory::*;
|
||||||
use file_header::FileHeader;
|
use file_header::FileHeader;
|
||||||
|
use local_file_header::LocalFileHeader;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serializable, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serializable, Default)]
|
||||||
pub struct Signature(pub u32);
|
pub struct Signature(pub u32);
|
||||||
|
|
@ -21,10 +22,39 @@ pub enum Encoding {
|
||||||
UTF8,
|
UTF8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod general_purpose_flags {
|
||||||
|
pub const MASK_ENCRYPTED: u16 = 1 << 0;
|
||||||
|
pub const MASK_COMPRESS_OPTION_1: u16 = 1 << 1;
|
||||||
|
pub const MASK_COMPRESS_OPTION_2: u16 = 1 << 2;
|
||||||
|
pub const MASK_USE_DATA_DESCRIPTOR: u16 = 1 << 3;
|
||||||
|
pub const MASK_STRONG_ENCRYPTION: u16 = 1 << 6;
|
||||||
|
pub const MASK_UTF8_FILENAME: u16 = 1 << 11;
|
||||||
|
pub const MASK_ENCRYPTED_CENTRAL_DIR: u16 = 1 << 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct File {
|
||||||
|
pub local_header: LocalFileHeader,
|
||||||
|
pub header: FileHeader,
|
||||||
|
}
|
||||||
|
// TODO: support data descriptor (MASK_USE_DATA_DESCRIPTOR)
|
||||||
|
|
||||||
|
impl File {
|
||||||
|
pub fn get_name(&self) -> String {
|
||||||
|
self.header.get_name()
|
||||||
|
}
|
||||||
|
pub fn get_offset_local_header(&self) -> u64 {
|
||||||
|
self.header.get_offset_local_header()
|
||||||
|
}
|
||||||
|
pub fn get_compressed_size(&self) -> u64 {
|
||||||
|
self.header.get_compressed_size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ZipFile<T: Read + Seek> {
|
pub struct ZipFile<T: Read + Seek> {
|
||||||
pub end_of_central_directory: EndCentralDirectory,
|
pub end_of_central_directory: EndCentralDirectory,
|
||||||
pub zip64_end_of_central_directory: Option<Zip64EndCentralDirectory>,
|
pub zip64_end_of_central_directory: Option<Zip64EndCentralDirectory>,
|
||||||
pub files: Vec<FileHeader>,
|
pub files: Vec<File>,
|
||||||
pub apk_sign_block: Option<ApkSigningBlock>,
|
pub apk_sign_block: Option<ApkSigningBlock>,
|
||||||
pub data: T,
|
pub data: T,
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +67,6 @@ impl<T: Read + Seek> ZipFile<T> {
|
||||||
.seek(SeekFrom::Start(end_of_central_directory_off))
|
.seek(SeekFrom::Start(end_of_central_directory_off))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let end_of_central_directory = EndCentralDirectory::deserialize(&mut reader).unwrap();
|
let end_of_central_directory = EndCentralDirectory::deserialize(&mut reader).unwrap();
|
||||||
println!("{end_of_central_directory:#?}");
|
|
||||||
reader
|
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,
|
||||||
|
|
@ -50,13 +79,10 @@ impl<T: Read + Seek> ZipFile<T> {
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
assert!(zip64_ecd_locator.total_number_of_disks <= 1);
|
assert!(zip64_ecd_locator.total_number_of_disks <= 1);
|
||||||
println!("Zip64 ECD Locator {:#?}", zip64_ecd_locator);
|
|
||||||
let zip64_edc_record_off =
|
let zip64_edc_record_off =
|
||||||
zip64_ecd_locator.offset_zip64_end_of_central_directory_record;
|
zip64_ecd_locator.offset_zip64_end_of_central_directory_record;
|
||||||
reader.seek(SeekFrom::Start(zip64_edc_record_off)).unwrap();
|
reader.seek(SeekFrom::Start(zip64_edc_record_off)).unwrap();
|
||||||
let zip64_edc_reccord = Zip64EndCentralDirectory::deserialize(&mut reader).ok();
|
Zip64EndCentralDirectory::deserialize(&mut reader).ok()
|
||||||
println!("{zip64_edc_reccord:#?}");
|
|
||||||
zip64_edc_reccord
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
@ -73,39 +99,51 @@ impl<T: Read + Seek> ZipFile<T> {
|
||||||
};
|
};
|
||||||
zip_file
|
zip_file
|
||||||
.data
|
.data
|
||||||
.seek(SeekFrom::Start(zip_file.get_ed_offset()))
|
.seek(SeekFrom::Start(zip_file.get_cd_offset()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut size_read = 0;
|
let mut size_read = 0;
|
||||||
let cd_size = zip_file.get_ed_size();
|
let cd_size = zip_file.get_cd_size();
|
||||||
while size_read < cd_size {
|
while size_read < cd_size {
|
||||||
let file_header = FileHeader::deserialize(&mut zip_file.data).unwrap();
|
let header = FileHeader::deserialize(&mut zip_file.data).unwrap();
|
||||||
println!("{file_header:#?}");
|
size_read += header.size() as u64;
|
||||||
size_read += file_header.size() as u64;
|
let pos_in_dir = zip_file.data.stream_position().unwrap();
|
||||||
zip_file.files.push(file_header);
|
if header.general_purpose_flags & general_purpose_flags::MASK_ENCRYPTED_CENTRAL_DIR != 0
|
||||||
|
{
|
||||||
|
panic!("Central directory encryption not supported");
|
||||||
}
|
}
|
||||||
assert_eq!(size_read, cd_size);
|
|
||||||
if zip_file.get_ed_offset() > 16 {
|
|
||||||
zip_file
|
zip_file
|
||||||
.data
|
.data
|
||||||
.seek(SeekFrom::Start(zip_file.get_ed_offset() - 16))
|
.seek(SeekFrom::Start(header.get_offset_local_header()))
|
||||||
|
.unwrap();
|
||||||
|
let local_header = LocalFileHeader::deserialize(&mut zip_file.data).unwrap();
|
||||||
|
zip_file.data.seek(SeekFrom::Start(pos_in_dir)).unwrap();
|
||||||
|
zip_file.files.push(File {
|
||||||
|
local_header,
|
||||||
|
header,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
assert_eq!(size_read, cd_size);
|
||||||
|
if zip_file.get_cd_offset() > 16 {
|
||||||
|
zip_file
|
||||||
|
.data
|
||||||
|
.seek(SeekFrom::Start(zip_file.get_cd_offset() - 16))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let magic = Magic::deserialize(&mut zip_file.data).unwrap();
|
let magic = Magic::deserialize(&mut zip_file.data).unwrap();
|
||||||
if magic == ApkSigningBlock::MAGIC {
|
if magic == ApkSigningBlock::MAGIC {
|
||||||
zip_file
|
zip_file
|
||||||
.data
|
.data
|
||||||
.seek(SeekFrom::Start(zip_file.get_ed_offset() - 16 - 8))
|
.seek(SeekFrom::Start(zip_file.get_cd_offset() - 16 - 8))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let block_size = u64::deserialize(&mut zip_file.data).unwrap();
|
let block_size = u64::deserialize(&mut zip_file.data).unwrap();
|
||||||
zip_file
|
zip_file
|
||||||
.data
|
.data
|
||||||
.seek(SeekFrom::Start(zip_file.get_ed_offset() - block_size - 8))
|
.seek(SeekFrom::Start(zip_file.get_cd_offset() - block_size - 8))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
zip_file.apk_sign_block = ApkSigningBlock::deserialize(&mut zip_file.data).ok();
|
zip_file.apk_sign_block = ApkSigningBlock::deserialize(&mut zip_file.data).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//println!("{:?}", zip_file.apk_sign_block);
|
|
||||||
|
|
||||||
zip_file
|
zip_file
|
||||||
}
|
}
|
||||||
|
|
@ -149,7 +187,7 @@ impl<T: Read + Seek> ZipFile<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ed_size(&self) -> u64 {
|
pub fn get_cd_size(&self) -> u64 {
|
||||||
if let Some(zip64_end_of_central_directory) = &self.zip64_end_of_central_directory {
|
if let Some(zip64_end_of_central_directory) = &self.zip64_end_of_central_directory {
|
||||||
zip64_end_of_central_directory.size_of_central_directory
|
zip64_end_of_central_directory.size_of_central_directory
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -157,7 +195,7 @@ impl<T: Read + Seek> ZipFile<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ed_offset(&self) -> u64 {
|
pub fn get_cd_offset(&self) -> u64 {
|
||||||
if let Some(zip64_end_of_central_directory) = &self.zip64_end_of_central_directory {
|
if let Some(zip64_end_of_central_directory) = &self.zip64_end_of_central_directory {
|
||||||
zip64_end_of_central_directory.offset_central_directory
|
zip64_end_of_central_directory.offset_central_directory
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -197,7 +235,7 @@ impl<T: Read + Seek> ZipFile<T> {
|
||||||
/// META-INF/*.DSA
|
/// META-INF/*.DSA
|
||||||
/// META-INF/*.RSA
|
/// META-INF/*.RSA
|
||||||
/// META-INF/SIG-*
|
/// META-INF/SIG-*
|
||||||
pub fn get_jar_sig_files(&self) -> Vec<&FileHeader> {
|
pub fn get_jar_sig_files(&self) -> Vec<&File> {
|
||||||
self.files
|
self.files
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|file| {
|
.filter(|file| {
|
||||||
|
|
@ -229,4 +267,49 @@ impl<T: Read + Seek> ZipFile<T> {
|
||||||
pub fn is_signed_v2(&self) -> bool {
|
pub fn is_signed_v2(&self) -> bool {
|
||||||
self.apk_sign_block.is_some()
|
self.apk_sign_block.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_holes(&self) {
|
||||||
|
let mut files: Vec<&File> = self.files.iter().collect();
|
||||||
|
files.sort_by_key(|f| f.get_offset_local_header());
|
||||||
|
let mut lst_offset = 0;
|
||||||
|
for file in files.iter() {
|
||||||
|
if file.get_offset_local_header() != lst_offset {
|
||||||
|
println!(
|
||||||
|
"Hole before {} between 0x{:x} and 0x{:x}",
|
||||||
|
file.get_name(),
|
||||||
|
lst_offset,
|
||||||
|
file.get_offset_local_header()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
lst_offset += file.local_header.size() as u64;
|
||||||
|
lst_offset += file.get_compressed_size();
|
||||||
|
}
|
||||||
|
if let Some(apk_sign_block) = &self.apk_sign_block {
|
||||||
|
let apk_sb_off = self.get_cd_offset() - apk_sign_block.size() as u64;
|
||||||
|
if apk_sb_off != lst_offset {
|
||||||
|
println!(
|
||||||
|
"Hole before apk signing block, between 0x{:x} and 0x{:x}",
|
||||||
|
lst_offset, apk_sb_off
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
lst_offset += apk_sign_block.size() as u64;
|
||||||
|
}
|
||||||
|
if self.get_cd_offset() != lst_offset {
|
||||||
|
println!(
|
||||||
|
"Hole before central directory between 0x{:x} and 0x{:x}",
|
||||||
|
lst_offset,
|
||||||
|
self.get_cd_offset()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bin(&mut self, offset: u64, size: usize) -> Vec<u8> {
|
||||||
|
self.data.seek(SeekFrom::Start(offset)).unwrap();
|
||||||
|
let mut data = vec![];
|
||||||
|
for _ in 0..size {
|
||||||
|
data.push(u8::deserialize(&mut self.data).unwrap());
|
||||||
|
}
|
||||||
|
data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use std::io::{SeekFrom, Write};
|
use std::io::{SeekFrom, Write};
|
||||||
|
|
||||||
use crate::extra_fields::{ExtraField, GenericExtraField, Zip64ExtraField};
|
use crate::extra_fields::{ExtraField, GenericExtraField, Zip64ExtraField};
|
||||||
use crate::{cp437, Encoding, Signature};
|
use crate::{cp437, general_purpose_flags, Encoding, Signature};
|
||||||
use androscalpel_serializer::{ReadSeek, Result, Serializable};
|
use androscalpel_serializer::{ReadSeek, Result, Serializable};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct LocalFileHeader {
|
pub struct LocalFileHeader {
|
||||||
// signature: Signature(0x04034b50)
|
// signature: Signature(0x04034b50)
|
||||||
pub version_needed_to_extract: u16,
|
pub version_needed_to_extract: u16,
|
||||||
pub general_purpose_flag: u16,
|
pub general_purpose_flags: u16,
|
||||||
pub compression_method: u16,
|
pub compression_method: u16,
|
||||||
pub last_mod_file_time: u16,
|
pub last_mod_file_time: u16,
|
||||||
pub last_mod_file_data: u16,
|
pub last_mod_file_data: u16,
|
||||||
|
|
@ -21,13 +21,14 @@ pub struct LocalFileHeader {
|
||||||
pub extra_field: Vec<ExtraField>,
|
pub extra_field: Vec<ExtraField>,
|
||||||
/// Remaining bytes in the extra_fields that could not be parsed as ExtraField
|
/// Remaining bytes in the extra_fields that could not be parsed as ExtraField
|
||||||
pub malformed_extra_field: Vec<u8>,
|
pub malformed_extra_field: Vec<u8>,
|
||||||
|
pub decryption_header: Option<DecryptionHeader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serializable for LocalFileHeader {
|
impl Serializable for LocalFileHeader {
|
||||||
fn serialize(&self, output: &mut dyn Write) -> Result<()> {
|
fn serialize(&self, output: &mut dyn Write) -> Result<()> {
|
||||||
Self::SIGNATURE.serialize(output)?;
|
Self::SIGNATURE.serialize(output)?;
|
||||||
self.version_needed_to_extract.serialize(output)?;
|
self.version_needed_to_extract.serialize(output)?;
|
||||||
self.general_purpose_flag.serialize(output)?;
|
self.general_purpose_flags.serialize(output)?;
|
||||||
self.compression_method.serialize(output)?;
|
self.compression_method.serialize(output)?;
|
||||||
self.last_mod_file_time.serialize(output)?;
|
self.last_mod_file_time.serialize(output)?;
|
||||||
self.last_mod_file_data.serialize(output)?;
|
self.last_mod_file_data.serialize(output)?;
|
||||||
|
|
@ -45,15 +46,17 @@ impl Serializable for LocalFileHeader {
|
||||||
for c in &self.malformed_extra_field {
|
for c in &self.malformed_extra_field {
|
||||||
c.serialize(output)?;
|
c.serialize(output)?;
|
||||||
}
|
}
|
||||||
|
if let Some(header) = &self.decryption_header {
|
||||||
|
header.serialize(output)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
|
fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
|
||||||
let signature = Signature::deserialize(input)?;
|
let signature = Signature::deserialize(input)?;
|
||||||
assert_eq!(signature, Self::SIGNATURE); // TODO
|
assert_eq!(signature, Self::SIGNATURE); // TODO
|
||||||
let version_made_by = u16::deserialize(input)?;
|
|
||||||
let version_needed_to_extract = u16::deserialize(input)?;
|
let version_needed_to_extract = u16::deserialize(input)?;
|
||||||
let general_purpose_flag = u16::deserialize(input)?;
|
let general_purpose_flags = u16::deserialize(input)?;
|
||||||
let compression_method = u16::deserialize(input)?;
|
let compression_method = u16::deserialize(input)?;
|
||||||
let last_mod_file_time = u16::deserialize(input)?;
|
let last_mod_file_time = u16::deserialize(input)?;
|
||||||
let last_mod_file_data = u16::deserialize(input)?;
|
let last_mod_file_data = u16::deserialize(input)?;
|
||||||
|
|
@ -62,18 +65,13 @@ impl Serializable for LocalFileHeader {
|
||||||
let uncompressed_size = u32::deserialize(input)?;
|
let uncompressed_size = u32::deserialize(input)?;
|
||||||
let file_name_length = u16::deserialize(input)?;
|
let file_name_length = u16::deserialize(input)?;
|
||||||
let extra_field_length = u16::deserialize(input)?;
|
let extra_field_length = u16::deserialize(input)?;
|
||||||
let file_comment_length = u16::deserialize(input)?;
|
|
||||||
let disk_number_start = u16::deserialize(input)?;
|
|
||||||
let internal_file_attributes = u16::deserialize(input)?;
|
|
||||||
let external_file_attributes = u32::deserialize(input)?;
|
|
||||||
let offset_local_header = u32::deserialize(input)?;
|
|
||||||
let mut file_name = vec![];
|
let mut file_name = vec![];
|
||||||
for _ in 0..file_name_length {
|
for _ in 0..file_name_length {
|
||||||
file_name.push(u8::deserialize(input)?);
|
file_name.push(u8::deserialize(input)?);
|
||||||
}
|
}
|
||||||
let mut header = Self {
|
let mut header = Self {
|
||||||
version_needed_to_extract,
|
version_needed_to_extract,
|
||||||
general_purpose_flag,
|
general_purpose_flags,
|
||||||
compression_method,
|
compression_method,
|
||||||
last_mod_file_time,
|
last_mod_file_time,
|
||||||
last_mod_file_data,
|
last_mod_file_data,
|
||||||
|
|
@ -83,6 +81,7 @@ impl Serializable for LocalFileHeader {
|
||||||
file_name,
|
file_name,
|
||||||
extra_field: vec![],
|
extra_field: vec![],
|
||||||
malformed_extra_field: vec![],
|
malformed_extra_field: vec![],
|
||||||
|
decryption_header: None,
|
||||||
};
|
};
|
||||||
//let end_of_extra_field = input.stream_position().unwrap() + extra_field_length as u64;
|
//let end_of_extra_field = input.stream_position().unwrap() + extra_field_length as u64;
|
||||||
let extra_field_off = input.stream_position().unwrap();
|
let extra_field_off = input.stream_position().unwrap();
|
||||||
|
|
@ -123,8 +122,8 @@ impl Serializable for LocalFileHeader {
|
||||||
{
|
{
|
||||||
let original_size = uncompressed_size == u32::MAX;
|
let original_size = uncompressed_size == u32::MAX;
|
||||||
let compressed_size = compressed_size == u32::MAX;
|
let compressed_size = compressed_size == u32::MAX;
|
||||||
let offset_header = offset_local_header == u32::MAX;
|
let offset_header = false;
|
||||||
let disk_number = disk_number_start == u16::MAX;
|
let disk_number = false;
|
||||||
let zip64_filed = Zip64ExtraField::from_generic(
|
let zip64_filed = Zip64ExtraField::from_generic(
|
||||||
&GenericExtraField {
|
&GenericExtraField {
|
||||||
id: Zip64ExtraField::ID,
|
id: Zip64ExtraField::ID,
|
||||||
|
|
@ -139,6 +138,12 @@ impl Serializable for LocalFileHeader {
|
||||||
*field = ExtraField::Zip64(zip64_filed);
|
*field = ExtraField::Zip64(zip64_filed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (header.general_purpose_flags & general_purpose_flags::MASK_ENCRYPTED != 0)
|
||||||
|
&& (header.general_purpose_flags & general_purpose_flags::MASK_STRONG_ENCRYPTION != 0)
|
||||||
|
{
|
||||||
|
header.decryption_header = Some(DecryptionHeader::deserialize(input)?);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(header)
|
Ok(header)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,6 +152,11 @@ impl Serializable for LocalFileHeader {
|
||||||
+ self.file_name.len()
|
+ self.file_name.len()
|
||||||
+ self.extra_field.iter().map(|f| f.size()).sum::<usize>()
|
+ self.extra_field.iter().map(|f| f.size()).sum::<usize>()
|
||||||
+ self.malformed_extra_field.len()
|
+ self.malformed_extra_field.len()
|
||||||
|
+ self
|
||||||
|
.decryption_header
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| h.size())
|
||||||
|
.unwrap_or(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,7 +167,7 @@ impl LocalFileHeader {
|
||||||
const MASK_UTF8_FILENAME: u16 = 1 << 11;
|
const MASK_UTF8_FILENAME: u16 = 1 << 11;
|
||||||
|
|
||||||
pub fn get_name_encoding(&self) -> Encoding {
|
pub fn get_name_encoding(&self) -> Encoding {
|
||||||
if self.general_purpose_flag & Self::MASK_UTF8_FILENAME != 0 {
|
if self.general_purpose_flags & Self::MASK_UTF8_FILENAME != 0 {
|
||||||
Encoding::UTF8
|
Encoding::UTF8
|
||||||
} else {
|
} else {
|
||||||
Encoding::CP437
|
Encoding::CP437
|
||||||
|
|
@ -205,3 +215,47 @@ impl LocalFileHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct DecryptionHeader {
|
||||||
|
// pub ivsize: u16,
|
||||||
|
pub iv_data: Vec<u8>,
|
||||||
|
// pub remaining_size: u32,
|
||||||
|
// TODO: parse, not needed for now
|
||||||
|
pub other_data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for DecryptionHeader {
|
||||||
|
fn serialize(&self, output: &mut dyn Write) -> Result<()> {
|
||||||
|
(self.iv_data.len() as u16).serialize(output)?;
|
||||||
|
for c in &self.iv_data {
|
||||||
|
c.serialize(output)?;
|
||||||
|
}
|
||||||
|
(self.other_data.len() as u32).serialize(output)?;
|
||||||
|
for c in &self.other_data {
|
||||||
|
c.serialize(output)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
|
||||||
|
let iv_size = u16::deserialize(input)?;
|
||||||
|
let mut iv_data = vec![];
|
||||||
|
for _ in 0..iv_size {
|
||||||
|
iv_data.push(u8::deserialize(input)?);
|
||||||
|
}
|
||||||
|
let remaining_size = u32::deserialize(input)?;
|
||||||
|
let mut other_data = vec![];
|
||||||
|
for _ in 0..remaining_size {
|
||||||
|
other_data.push(u8::deserialize(input)?);
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
iv_data,
|
||||||
|
other_data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
2 + self.iv_data.len() + 4 + self.other_data.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,16 @@ fn main() {
|
||||||
//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 = ZipFile::new(file);
|
let zip_file = ZipFile::new(file);
|
||||||
//println!("{}", zip_file.get_file_names().join("\n"));
|
//println!("{}", zip_file.get_file_names().join("\n"));
|
||||||
|
for file in &zip_file.files {
|
||||||
|
println!("{}", file.get_name());
|
||||||
|
println!("local: {:?}", file.local_header.malformed_extra_field);
|
||||||
|
println!("central dir: {:?}", file.header.malformed_extra_field);
|
||||||
|
println!();
|
||||||
|
}
|
||||||
/*println!(
|
/*println!(
|
||||||
"uncompressed size: {}",
|
"uncompressed size: {}",
|
||||||
zip_file.files[0].get_uncompressed_size()
|
zip_file.files[0].get_uncompressed_size()
|
||||||
);*/
|
);
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
zip_file
|
zip_file
|
||||||
|
|
@ -18,10 +24,11 @@ fn main() {
|
||||||
.map(|f| f.get_name())
|
.map(|f| f.get_name())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("\n")
|
.join("\n")
|
||||||
);
|
);*/
|
||||||
if zip_file.is_signed_v2() {
|
if zip_file.is_signed_v2() {
|
||||||
println!("Signed >= v2");
|
println!("Signed >= v2");
|
||||||
} else {
|
} else {
|
||||||
println!("Not signed whith scheme >= v2");
|
println!("Not signed whith scheme >= v2");
|
||||||
}
|
}
|
||||||
|
zip_file.check_holes();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue