androscalpel/apk_frauder/src/local_file_header.rs
2024-01-17 13:57:01 +01:00

272 lines
9.5 KiB
Rust

use std::io::{SeekFrom, Write};
use crate::compression::CompressionMethod;
use crate::extra_fields::{ExtraField, GenericExtraField, Zip64ExtraField};
use crate::{cp437, general_purpose_flags, 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_flags: u16,
pub compression_method: CompressionMethod,
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>,
pub decryption_header: Option<DecryptionHeader>,
}
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_flags.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)?;
}
if let Some(header) = &self.decryption_header {
header.serialize(output)?;
}
Ok(())
}
fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
let signature = Signature::deserialize(input)?;
assert_eq!(signature, Self::SIGNATURE); // TODO
let version_needed_to_extract = u16::deserialize(input)?;
let general_purpose_flags = u16::deserialize(input)?;
let compression_method = CompressionMethod::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 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_flags,
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![],
decryption_header: None,
};
//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);
}
}
let mut failed_last_extra_field = false;
if extra_size_read > extra_field_length as usize {
//println!("Failed to parse last extra field in {}", header.get_name());
failed_last_extra_field = true;
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;
}
// If it is not padding from zipalign
if failed_last_extra_field
&& header.malformed_extra_field != vec![0]
&& header.malformed_extra_field != vec![0, 0]
&& header.malformed_extra_field != vec![0, 0, 0]
{
println!("Failed to parse last extra field in {}", header.get_name());
}
//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 = false;
let disk_number = false;
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);
}
}
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)
}
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()
+ self
.decryption_header
.as_ref()
.map(|h| h.size())
.unwrap_or(0)
}
}
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_flags & 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
}
}
}
#[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()
}
}