From ea18640a78967dfe7b1be9af20483af803dd1b5f Mon Sep 17 00:00:00 2001 From: Jean-Marie 'Histausse' Mineau Date: Sun, 14 Jan 2024 17:14:35 +0100 Subject: [PATCH] parse basic file header --- apk_frauder/src/main.rs | 208 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 203 insertions(+), 5 deletions(-) diff --git a/apk_frauder/src/main.rs b/apk_frauder/src/main.rs index 9798e72..f6900f2 100644 --- a/apk_frauder/src/main.rs +++ b/apk_frauder/src/main.rs @@ -7,6 +7,122 @@ use androscalpel_serializer::{ReadSeek, Result, Serializable}; //struct Signature(pub [u8; 4]); struct Signature(pub u32); +#[derive(Debug, Clone, PartialEq, Eq)] +struct FileHeader { + // signature: Signature(0x02014b50) + version_made_by: u16, + version_needed_to_extract: u16, + general_purpose_flag: u16, + compression_method: u16, + last_mod_file_time: u16, + last_mod_file_data: u16, + crc_32: u32, + compressed_size: u32, + uncompressed_size: u32, + // file_name_length: u16, + // extra_field_length: u16, + // file_comment_length: u16, + disk_number_start: u16, + internal_file_attributes: u16, + external_file_attributes: u32, + offset_local_header: u32, + file_name: Vec, + extra_field: Vec, // TODO: zip64 + file_comment: Vec, +} + +impl Serializable for FileHeader { + fn serialize(&self, output: &mut dyn Write) -> Result<()> { + Self::SIGNATURE.serialize(output)?; + self.version_made_by.serialize(output)?; + self.version_needed_to_extract.serialize(output)?; + self.general_purpose_flag.serialize(output)?; + self.compression_method.serialize(output)?; + self.last_mod_file_time.serialize(output)?; + self.last_mod_file_data.serialize(output)?; + self.crc_32.serialize(output)?; + self.compressed_size.serialize(output)?; + self.uncompressed_size.serialize(output)?; + (self.file_name.len() as u16).serialize(output)?; + (self.extra_field.len() as u16).serialize(output)?; + (self.file_comment.len() as u16).serialize(output)?; + self.disk_number_start.serialize(output)?; + self.internal_file_attributes.serialize(output)?; + self.external_file_attributes.serialize(output)?; + self.offset_local_header.serialize(output)?; + for c in &self.file_name { + c.serialize(output)?; + } + for c in &self.extra_field { + c.serialize(output)?; + } + for c in &self.file_comment { + c.serialize(output)?; + } + Ok(()) + } + + fn deserialize(input: &mut dyn ReadSeek) -> Result { + let signature = Signature::deserialize(input)?; + assert_eq!(signature, Self::SIGNATURE); // TODO + let version_made_by = u16::deserialize(input)?; + let version_needed_to_extract = u16::deserialize(input)?; + let general_purpose_flag = u16::deserialize(input)?; + let compression_method = u16::deserialize(input)?; + let last_mod_file_time = u16::deserialize(input)?; + let last_mod_file_data = u16::deserialize(input)?; + let crc_32 = u32::deserialize(input)?; + let compressed_size = u32::deserialize(input)?; + let uncompressed_size = u32::deserialize(input)?; + let file_name_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 extra_field = vec![]; + let mut file_comment = vec![]; + for _ in 0..file_name_length { + file_name.push(u8::deserialize(input)?); + } + for _ in 0..extra_field_length { + extra_field.push(u8::deserialize(input)?); + } + for _ in 0..file_comment_length { + file_comment.push(u8::deserialize(input)?); + } + Ok(Self { + version_made_by, + version_needed_to_extract, + general_purpose_flag, + compression_method, + last_mod_file_time, + last_mod_file_data, + crc_32, + compressed_size, + uncompressed_size, + disk_number_start, + internal_file_attributes, + external_file_attributes, + offset_local_header, + file_name, + extra_field, + file_comment, + }) + } + + fn size(&self) -> usize { + Self::MIN_SIZE + self.file_name.len() + self.extra_field.len() + self.file_comment.len() + } +} + +impl FileHeader { + const SIGNATURE: Signature = Signature(0x02014b50); + const MIN_SIZE: usize = 4 + 6 * 2 + 4 * 3 + 5 * 2 + 4 * 2; +} + #[derive(Debug, Clone, PartialEq, Eq)] struct Zip64EndCentralDirectory { // signature: Signature @@ -164,7 +280,8 @@ impl Serializable for EndCentralDirectory { struct ZipFile { end_of_central_directory: EndCentralDirectory, - end_of_central_directory_off: u64, + zip64_end_of_central_directory: Option, + files: Vec, data: T, } @@ -183,7 +300,7 @@ impl ZipFile { )) .unwrap(); let zip64_ecd_locator = Zip64EndCentralDirectoryLocator::deserialize(&mut reader).ok(); - if let Some(zip64_ecd_locator) = zip64_ecd_locator { + 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 @@ -195,12 +312,93 @@ impl ZipFile { reader.seek(SeekFrom::Start(zip64_edc_record_off)).unwrap(); let zip64_edc_reccord = Zip64EndCentralDirectory::deserialize(&mut reader).ok(); println!("{zip64_edc_reccord:#?}"); - } + zip64_edc_reccord + } else { + None + }; - Self { + // At this point python's ziplib recompute the location of the central directory from the + // location of the end of central directory in case the zip was concanated after a file. + // We probably don't need that for now. + let mut zip_file = Self { end_of_central_directory, - end_of_central_directory_off, + zip64_end_of_central_directory, data: reader, + files: vec![], + }; + zip_file + .data + .seek(SeekFrom::Start(zip_file.get_ed_offset())) + .unwrap(); + + let mut size_read = 0; + let cd_size = zip_file.get_ed_size(); + while size_read < cd_size { + let file_header = FileHeader::deserialize(&mut zip_file.data).unwrap(); + size_read += file_header.size() as u64; + zip_file.files.push(file_header); + } + assert_eq!(size_read, cd_size); + for f in &zip_file.files { + println!("{f:?}"); + } + zip_file + } + + /* + fn is_zip64(&self) -> bool { + self.zip64_end_of_central_directory.is_some() + } + */ + + fn get_disk_num(&self) -> u32 { + if let Some(zip64_end_of_central_directory) = &self.zip64_end_of_central_directory { + zip64_end_of_central_directory.number_of_this_disk + } else { + self.end_of_central_directory.disk_number as u32 + } + } + + fn get_disk_ed_start(&self) -> u32 { + if let Some(zip64_end_of_central_directory) = &self.zip64_end_of_central_directory { + zip64_end_of_central_directory.disk_number_of_central_directory_start + } else { + self.end_of_central_directory + .disk_number_of_central_directory_start as u32 + } + } + + fn get_number_entries_on_disk(&self) -> u64 { + if let Some(zip64_end_of_central_directory) = &self.zip64_end_of_central_directory { + zip64_end_of_central_directory.number_entry_in_central_directory_on_this_disk + } else { + self.end_of_central_directory + .number_of_entries_in_central_directory_on_disk as u64 + } + } + + fn get_number_entries(&self) -> u64 { + if let Some(zip64_end_of_central_directory) = &self.zip64_end_of_central_directory { + zip64_end_of_central_directory.number_entry_in_central_directory + } else { + self.end_of_central_directory + .number_of_entries_in_central_directory as u64 + } + } + + fn get_ed_size(&self) -> u64 { + if let Some(zip64_end_of_central_directory) = &self.zip64_end_of_central_directory { + zip64_end_of_central_directory.size_of_central_directory + } else { + self.end_of_central_directory.size_central_directory as u64 + } + } + + fn get_ed_offset(&self) -> u64 { + if let Some(zip64_end_of_central_directory) = &self.zip64_end_of_central_directory { + zip64_end_of_central_directory.offset_central_directory + } else { + self.end_of_central_directory.offset_central_directory as u64 } }