From 7b6a5980c8d26a8bf4998841fabc29bcef0d268f Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Fri, 19 Jan 2024 16:41:59 +0100 Subject: [PATCH] insert file in zip --- apk_frauder/src/cp437.rs | 145 +++++++++++++++++++++++++++++++++ apk_frauder/src/error.rs | 4 + apk_frauder/src/file_header.rs | 32 ++++++++ apk_frauder/src/lib.rs | 1 + apk_frauder/src/main.rs | 21 ++++- apk_frauder/src/zip_writer.rs | 15 ++-- 6 files changed, 210 insertions(+), 8 deletions(-) create mode 100644 apk_frauder/src/error.rs diff --git a/apk_frauder/src/cp437.rs b/apk_frauder/src/cp437.rs index 6e1a426..f59690c 100644 --- a/apk_frauder/src/cp437.rs +++ b/apk_frauder/src/cp437.rs @@ -1,4 +1,5 @@ /// Convert cp437 encoded string to [`String`]. +use crate::error::Error; pub fn cp437_char_to_char(input: u8) -> char { char::from_u32(match input { @@ -138,3 +139,147 @@ pub fn cp437_char_to_char(input: u8) -> char { pub fn cp437_to_string(input: &[u8]) -> String { input.iter().cloned().map(cp437_char_to_char).collect() } + +pub fn char_to_cp437_char(input: char) -> Result { + let input: u32 = input.into(); + match input { + 0x00..=0x7f => Ok(input as u8), + 0x00c7 => Ok(0x80), + 0x00fc => Ok(0x81), + 0x00e9 => Ok(0x82), + 0x00e2 => Ok(0x83), + 0x00e4 => Ok(0x84), + 0x00e0 => Ok(0x85), + 0x00e5 => Ok(0x86), + 0x00e7 => Ok(0x87), + 0x00ea => Ok(0x88), + 0x00eb => Ok(0x89), + 0x00e8 => Ok(0x8a), + 0x00ef => Ok(0x8b), + 0x00ee => Ok(0x8c), + 0x00ec => Ok(0x8d), + 0x00c4 => Ok(0x8e), + 0x00c5 => Ok(0x8f), + 0x00c9 => Ok(0x90), + 0x00e6 => Ok(0x91), + 0x00c6 => Ok(0x92), + 0x00f4 => Ok(0x93), + 0x00f6 => Ok(0x94), + 0x00f2 => Ok(0x95), + 0x00fb => Ok(0x96), + 0x00f9 => Ok(0x97), + 0x00ff => Ok(0x98), + 0x00d6 => Ok(0x99), + 0x00dc => Ok(0x9a), + 0x00a2 => Ok(0x9b), + 0x00a3 => Ok(0x9c), + 0x00a5 => Ok(0x9d), + 0x20a7 => Ok(0x9e), + 0x0192 => Ok(0x9f), + 0x00e1 => Ok(0xa0), + 0x00ed => Ok(0xa1), + 0x00f3 => Ok(0xa2), + 0x00fa => Ok(0xa3), + 0x00f1 => Ok(0xa4), + 0x00d1 => Ok(0xa5), + 0x00aa => Ok(0xa6), + 0x00ba => Ok(0xa7), + 0x00bf => Ok(0xa8), + 0x2310 => Ok(0xa9), + 0x00ac => Ok(0xaa), + 0x00bd => Ok(0xab), + 0x00bc => Ok(0xac), + 0x00a1 => Ok(0xad), + 0x00ab => Ok(0xae), + 0x00bb => Ok(0xaf), + 0x2591 => Ok(0xb0), + 0x2592 => Ok(0xb1), + 0x2593 => Ok(0xb2), + 0x2502 => Ok(0xb3), + 0x2524 => Ok(0xb4), + 0x2561 => Ok(0xb5), + 0x2562 => Ok(0xb6), + 0x2556 => Ok(0xb7), + 0x2555 => Ok(0xb8), + 0x2563 => Ok(0xb9), + 0x2551 => Ok(0xba), + 0x2557 => Ok(0xbb), + 0x255d => Ok(0xbc), + 0x255c => Ok(0xbd), + 0x255b => Ok(0xbe), + 0x2510 => Ok(0xbf), + 0x2514 => Ok(0xc0), + 0x2534 => Ok(0xc1), + 0x252c => Ok(0xc2), + 0x251c => Ok(0xc3), + 0x2500 => Ok(0xc4), + 0x253c => Ok(0xc5), + 0x255e => Ok(0xc6), + 0x255f => Ok(0xc7), + 0x255a => Ok(0xc8), + 0x2554 => Ok(0xc9), + 0x2569 => Ok(0xca), + 0x2566 => Ok(0xcb), + 0x2560 => Ok(0xcc), + 0x2550 => Ok(0xcd), + 0x256c => Ok(0xce), + 0x2567 => Ok(0xcf), + 0x2568 => Ok(0xd0), + 0x2564 => Ok(0xd1), + 0x2565 => Ok(0xd2), + 0x2559 => Ok(0xd3), + 0x2558 => Ok(0xd4), + 0x2552 => Ok(0xd5), + 0x2553 => Ok(0xd6), + 0x256b => Ok(0xd7), + 0x256a => Ok(0xd8), + 0x2518 => Ok(0xd9), + 0x250c => Ok(0xda), + 0x2588 => Ok(0xdb), + 0x2584 => Ok(0xdc), + 0x258c => Ok(0xdd), + 0x2590 => Ok(0xde), + 0x2580 => Ok(0xdf), + 0x03b1 => Ok(0xe0), + 0x00df => Ok(0xe1), + 0x0393 => Ok(0xe2), + 0x03c0 => Ok(0xe3), + 0x03a3 => Ok(0xe4), + 0x03c3 => Ok(0xe5), + 0x00b5 => Ok(0xe6), + 0x03c4 => Ok(0xe7), + 0x03a6 => Ok(0xe8), + 0x0398 => Ok(0xe9), + 0x03a9 => Ok(0xea), + 0x03b4 => Ok(0xeb), + 0x221e => Ok(0xec), + 0x03c6 => Ok(0xed), + 0x03b5 => Ok(0xee), + 0x2229 => Ok(0xef), + 0x2261 => Ok(0xf0), + 0x00b1 => Ok(0xf1), + 0x2265 => Ok(0xf2), + 0x2264 => Ok(0xf3), + 0x2320 => Ok(0xf4), + 0x2321 => Ok(0xf5), + 0x00f7 => Ok(0xf6), + 0x2248 => Ok(0xf7), + 0x00b0 => Ok(0xf8), + 0x2219 => Ok(0xf9), + 0x00b7 => Ok(0xfa), + 0x221a => Ok(0xfb), + 0x207f => Ok(0xfc), + 0x00b2 => Ok(0xfd), + 0x25a0 => Ok(0xfe), + 0x00a0 => Ok(0xff), + _ => Err(Error::NotCp437), + } +} + +pub fn string_to_cp437(input: &str) -> Result, Error> { + let mut bin = vec![]; + for cha in input.chars() { + bin.push(char_to_cp437_char(cha)?); + } + Ok(bin) +} diff --git a/apk_frauder/src/error.rs b/apk_frauder/src/error.rs new file mode 100644 index 0000000..9f119d0 --- /dev/null +++ b/apk_frauder/src/error.rs @@ -0,0 +1,4 @@ +#[derive(Debug, PartialEq, Clone)] +pub enum Error { + NotCp437, +} diff --git a/apk_frauder/src/file_header.rs b/apk_frauder/src/file_header.rs index 3b0b3e2..9f1bd01 100644 --- a/apk_frauder/src/file_header.rs +++ b/apk_frauder/src/file_header.rs @@ -1,6 +1,8 @@ use std::io::{SeekFrom, Write}; use crate::compression::CompressionMethod; +use crate::cp437::string_to_cp437; +use crate::error::Error; use crate::extra_fields::{ExtraField, GenericExtraField, Zip64ExtraField}; use crate::{cp437, general_purpose_flags, Encoding, Signature}; use androscalpel_serializer::{ReadSeek, Result, Serializable}; @@ -188,6 +190,36 @@ impl FileHeader { const SIGNATURE: Signature = Signature(0x02014b50); const MIN_SIZE: usize = 4 + 6 * 2 + 4 * 3 + 5 * 2 + 4 * 2; + pub fn new_default(name: &str) -> Self { + let mut general_purpose_flags = 0; + let file_name = match string_to_cp437(name) { + Ok(name) => name, + Err(Error::NotCp437) => { + general_purpose_flags |= general_purpose_flags::MASK_UTF8_FILENAME; + name.as_bytes().into() + } + }; + FileHeader { + version_made_by: 768, + version_needed_to_extract: 0, + general_purpose_flags, + compression_method: CompressionMethod::Deflated, + last_mod_file_time: 0, + last_mod_file_data: 0, + crc_32: 0, + compressed_size: 0, + uncompressed_size: 0, + disk_number_start: 0, + internal_file_attributes: 0, + external_file_attributes: 0b1000000110100100 << 16, // TODO + offset_local_header: 0, + file_name, + extra_field: vec![], + malformed_extra_field: vec![], + file_comment: vec![], + } + } + pub fn get_name_encoding(&self) -> Encoding { if self.general_purpose_flags & general_purpose_flags::MASK_UTF8_FILENAME != 0 { Encoding::UTF8 diff --git a/apk_frauder/src/lib.rs b/apk_frauder/src/lib.rs index ca51bf6..5a865e5 100644 --- a/apk_frauder/src/lib.rs +++ b/apk_frauder/src/lib.rs @@ -4,6 +4,7 @@ pub mod apk_signing_block; pub mod compression; mod cp437; pub mod end_of_central_directory; +pub mod error; pub mod extra_fields; pub mod file_header; pub mod local_file_header; diff --git a/apk_frauder/src/main.rs b/apk_frauder/src/main.rs index f308536..2f0810e 100644 --- a/apk_frauder/src/main.rs +++ b/apk_frauder/src/main.rs @@ -1,11 +1,13 @@ +use apk_frauder::file_header::FileHeader; use apk_frauder::ZipFileReader; use apk_frauder::ZipFileWriter; use std::fs::File; +use std::io::Cursor; fn main() { - let file = File::open("app-release.apk").expect("failed to open file"); + //let file = File::open("app-release.apk").expect("failed to open file"); //let file = File::open("tst_64.zip").expect("failed to open file"); - let zip_file = ZipFileReader::new(file); + //let zip_file = ZipFileReader::new(file); /* //println!("{}", zip_file.get_file_names().join("\n")); for file in &zip_file.files { @@ -35,7 +37,7 @@ fn main() { } zip_file.check_holes(); */ - println!("{:#?}", zip_file.get_file_info("classes.dex")); + //println!("{:#?}", zip_file.get_file_info("classes.dex")); /* let file = File::open("tst_64.zip").expect("failed to open file"); @@ -49,4 +51,17 @@ fn main() { out_file.write_central_directory(); */ //println!("{:#?}", zip_file.zip64_end_of_central_directory); + let out_file = File::create("test_write.zip").expect("failed to create file"); + let mut out_file = ZipFileWriter::new(out_file, None); + out_file.insert_file( + &mut Cursor::new(b"plop\n"), + FileHeader::new_default("plop.txt"), + None, + ); + out_file.insert_file( + &mut Cursor::new(b"Hello World\n"), + FileHeader::new_default("plip.txt"), + None, + ); + out_file.write_central_directory(); } diff --git a/apk_frauder/src/zip_writer.rs b/apk_frauder/src/zip_writer.rs index 5b3205b..0d52294 100644 --- a/apk_frauder/src/zip_writer.rs +++ b/apk_frauder/src/zip_writer.rs @@ -166,13 +166,12 @@ impl ZipFileWriter { CompressionMethod::Deflated => { // TODO: find a way to do this in place, the compressed data can be large, and storing it // in memory is bad, but Deflate consume the Reader, so we cannot juste give it self.data - // TODO: Compression::default -> nop, use flags + // TODO: Compression::default -> use flag? let mut compressor = - DeflateEncoder::new(CrcWriter::new(Vec::new()), Compression::default()); + CrcWriter::new(DeflateEncoder::new(Vec::new(), Compression::default())); io::copy(file, &mut compressor).unwrap(); - let finished = compressor.finish().unwrap(); - local_header.crc_32 = finished.crc().sum(); - finished.into_inner() + local_header.crc_32 = compressor.crc().sum(); + compressor.into_inner().finish().unwrap() } _ => unimplemented!(), }; @@ -187,6 +186,12 @@ impl ZipFileWriter { header.set_uncompressed_size(local_header.get_uncompressed_size()); header.set_offset_local_header(header_offset); + // TODO: compression flags are not used right now, set to zero + local_header.general_purpose_flags &= !(general_purpose_flags::MASK_COMPRESS_OPTION_1 + | general_purpose_flags::MASK_COMPRESS_OPTION_2); + header.general_purpose_flags &= !(general_purpose_flags::MASK_COMPRESS_OPTION_1 + | general_purpose_flags::MASK_COMPRESS_OPTION_2); + let file_info = FileInfo { local_header, header,