add struct for localheader
This commit is contained in:
parent
bea2b9ffc0
commit
0794aac016
3 changed files with 214 additions and 6 deletions
|
|
@ -1,14 +1,9 @@
|
|||
use std::io::{SeekFrom, Write};
|
||||
|
||||
use crate::extra_fields::{ExtraField, GenericExtraField, Zip64ExtraField};
|
||||
use crate::{cp437, Signature};
|
||||
use crate::{cp437, Encoding, Signature};
|
||||
use androscalpel_serializer::{ReadSeek, Result, Serializable};
|
||||
|
||||
pub enum Encoding {
|
||||
CP437,
|
||||
UTF8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FileHeader {
|
||||
// signature: Signature(0x02014b50)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ mod cp437;
|
|||
pub mod end_of_central_directory;
|
||||
pub mod extra_fields;
|
||||
pub mod file_header;
|
||||
pub mod local_file_header;
|
||||
|
||||
use apk_signing_block::*;
|
||||
use end_of_central_directory::*;
|
||||
|
|
@ -15,6 +16,11 @@ use file_header::FileHeader;
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Serializable, Default)]
|
||||
pub struct Signature(pub u32);
|
||||
|
||||
pub enum Encoding {
|
||||
CP437,
|
||||
UTF8,
|
||||
}
|
||||
|
||||
pub struct ZipFile<T: Read + Seek> {
|
||||
pub end_of_central_directory: EndCentralDirectory,
|
||||
pub zip64_end_of_central_directory: Option<Zip64EndCentralDirectory>,
|
||||
|
|
|
|||
207
apk_frauder/src/local_file_header.rs
Normal file
207
apk_frauder/src/local_file_header.rs
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
use std::io::{SeekFrom, Write};
|
||||
|
||||
use crate::extra_fields::{ExtraField, GenericExtraField, Zip64ExtraField};
|
||||
use crate::{cp437, Encoding, Signature};
|
||||
use androscalpel_serializer::{ReadSeek, Result, Serializable};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct LocalFileHeader {
|
||||
// signature: Signature(0x04034b50)
|
||||
pub version_needed_to_extract: u16,
|
||||
pub general_purpose_flag: u16,
|
||||
pub compression_method: u16,
|
||||
pub last_mod_file_time: u16,
|
||||
pub last_mod_file_data: u16,
|
||||
pub crc_32: u32,
|
||||
pub compressed_size: u32,
|
||||
pub uncompressed_size: u32,
|
||||
// file_name_length: u16,
|
||||
// extra_field_length: u16,
|
||||
pub file_name: Vec<u8>,
|
||||
pub extra_field: Vec<ExtraField>,
|
||||
/// Remaining bytes in the extra_fields that could not be parsed as ExtraField
|
||||
pub malformed_extra_field: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Serializable for LocalFileHeader {
|
||||
fn serialize(&self, output: &mut dyn Write) -> Result<()> {
|
||||
Self::SIGNATURE.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)?;
|
||||
for c in &self.file_name {
|
||||
c.serialize(output)?;
|
||||
}
|
||||
for c in &self.extra_field {
|
||||
c.serialize(output)?;
|
||||
}
|
||||
for c in &self.malformed_extra_field {
|
||||
c.serialize(output)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
|
||||
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![];
|
||||
for _ in 0..file_name_length {
|
||||
file_name.push(u8::deserialize(input)?);
|
||||
}
|
||||
let mut header = Self {
|
||||
version_needed_to_extract,
|
||||
general_purpose_flag,
|
||||
compression_method,
|
||||
last_mod_file_time,
|
||||
last_mod_file_data,
|
||||
crc_32,
|
||||
compressed_size,
|
||||
uncompressed_size,
|
||||
file_name,
|
||||
extra_field: vec![],
|
||||
malformed_extra_field: vec![],
|
||||
};
|
||||
//let end_of_extra_field = input.stream_position().unwrap() + extra_field_length as u64;
|
||||
let extra_field_off = input.stream_position().unwrap();
|
||||
let mut extra_size_read = 0;
|
||||
while extra_size_read < extra_field_length as usize {
|
||||
let field_off = input.stream_position().unwrap();
|
||||
let field = ExtraField::deserialize(input);
|
||||
|
||||
if let Err(err) = field {
|
||||
println!(
|
||||
"Failed to parsed extra field in {}: {err:?}",
|
||||
header.get_name()
|
||||
);
|
||||
input.seek(SeekFrom::Start(field_off)).unwrap();
|
||||
break;
|
||||
} else {
|
||||
let field = field.unwrap();
|
||||
extra_size_read += field.size();
|
||||
header.extra_field.push(field);
|
||||
}
|
||||
}
|
||||
if extra_size_read > extra_field_length as usize {
|
||||
println!("Failed to parsed last extra field in {}", header.get_name());
|
||||
let size = header.extra_field.pop().unwrap().size();
|
||||
input.seek(SeekFrom::Current(-(size as i64))).unwrap();
|
||||
}
|
||||
let mut extra_size_read = input.stream_position().unwrap() - extra_field_off;
|
||||
while extra_size_read < extra_field_length as u64 {
|
||||
header.malformed_extra_field.push(u8::deserialize(input)?);
|
||||
extra_size_read += 1;
|
||||
}
|
||||
//input.seek(SeekFrom::Start(end_of_extra_field)).unwrap();
|
||||
for field in &mut header.extra_field {
|
||||
if let ExtraField::Generic(GenericExtraField {
|
||||
id: Zip64ExtraField::ID,
|
||||
data,
|
||||
}) = field
|
||||
{
|
||||
let original_size = uncompressed_size == u32::MAX;
|
||||
let compressed_size = compressed_size == u32::MAX;
|
||||
let offset_header = offset_local_header == u32::MAX;
|
||||
let disk_number = disk_number_start == u16::MAX;
|
||||
let zip64_filed = Zip64ExtraField::from_generic(
|
||||
&GenericExtraField {
|
||||
id: Zip64ExtraField::ID,
|
||||
data: data.clone(),
|
||||
},
|
||||
original_size,
|
||||
compressed_size,
|
||||
offset_header,
|
||||
disk_number,
|
||||
)
|
||||
.unwrap();
|
||||
*field = ExtraField::Zip64(zip64_filed);
|
||||
}
|
||||
}
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
Self::MIN_SIZE
|
||||
+ self.file_name.len()
|
||||
+ self.extra_field.iter().map(|f| f.size()).sum::<usize>()
|
||||
+ self.malformed_extra_field.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalFileHeader {
|
||||
const SIGNATURE: Signature = Signature(0x04034b50);
|
||||
const MIN_SIZE: usize = 4 + 5 * 2 + 3 * 4 + 2 * 2;
|
||||
|
||||
const MASK_UTF8_FILENAME: u16 = 1 << 11;
|
||||
|
||||
pub fn get_name_encoding(&self) -> Encoding {
|
||||
if self.general_purpose_flag & Self::MASK_UTF8_FILENAME != 0 {
|
||||
Encoding::UTF8
|
||||
} else {
|
||||
Encoding::CP437
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> String {
|
||||
match self.get_name_encoding() {
|
||||
Encoding::UTF8 => std::str::from_utf8(&self.file_name).unwrap().into(),
|
||||
Encoding::CP437 => cp437::cp437_to_string(&self.file_name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_uncompressed_size(&self) -> u64 {
|
||||
if self.uncompressed_size != u32::MAX {
|
||||
self.uncompressed_size as u64
|
||||
} else if let Some(ExtraField::Zip64(Zip64ExtraField {
|
||||
original_size: Some(original_size),
|
||||
..
|
||||
})) = self
|
||||
.extra_field
|
||||
.iter()
|
||||
.find(|f| matches!(f, ExtraField::Zip64(_)))
|
||||
{
|
||||
*original_size
|
||||
} else {
|
||||
self.uncompressed_size as u64
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_compressed_size(&self) -> u64 {
|
||||
if self.compressed_size != u32::MAX {
|
||||
self.compressed_size as u64
|
||||
} else if let Some(ExtraField::Zip64(Zip64ExtraField {
|
||||
compressed_size: Some(compressed_size),
|
||||
..
|
||||
})) = self
|
||||
.extra_field
|
||||
.iter()
|
||||
.find(|f| matches!(f, ExtraField::Zip64(_)))
|
||||
{
|
||||
*compressed_size
|
||||
} else {
|
||||
self.compressed_size as u64
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue