diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..df57127 --- /dev/null +++ b/TODO.md @@ -0,0 +1,18 @@ +# Formats +- Mstar upgrade bin - 80% complete - need to handle securewrite and LZO compression +- Samsung Old fw dir - Complete +- TPV(Philips) 2023/TitanOS TIMG - Complete +- PFL UPG(Philips) - Complete +- LG Epk1 - 66% complete, epk1(new) not supported, couldnt find one +- Novatek (NFWB) - Complete +- Samsung MSD10(Legacy and Tizen 2015) - Complete +- Samsung MSD11(Tizen 2016+) - Complete +- Panasonic SDDL.SEC(2011+) - Complete +- LG Epk2 (Crypted and plain header)- Should be complete, but needs some more testing + +# TODO +- implement some key storing system instead of hardcoding +- cleanup some formats especially mstar and samsung old +- cleanup main file +- Do more testing for PFL UPG and Epk2 +- Epk3? diff --git a/src/formats/epk.rs b/src/formats/epk.rs index 1e61924..ae0fadd 100644 --- a/src/formats/epk.rs +++ b/src/formats/epk.rs @@ -14,6 +14,7 @@ pub fn is_epk_file(file: &File) -> bool { } fn check_epk_version(versions: &[u8]) -> Option { + // _ - 0x00 X - a number . - a dot let epk2_pattern = "____XXXX.XXXX.XXXX__XX.XX.XXX_______"; let epk3_pattern = "____X.X.X___________X.X.X___________"; @@ -29,10 +30,9 @@ fn check_epk_version(versions: &[u8]) -> Option { fn match_with_pattern(data: &[u8], pattern: &str) -> bool { for (&b, p) in data.iter().zip(pattern.bytes()) { match p { - b'_' if b != 0x00 => return false, + b'_' if b != 0x00 => return false, b'X' if !b.is_ascii_digit() => return false, - b'.' if b != b'.' => return false, - _ if p != b'_' && p != b'X' && p != b'.' => return false, + b'.' if b != b'.' => return false, _ => {} } } diff --git a/src/formats/epk2.rs b/src/formats/epk2.rs index 0020218..bd5f3eb 100644 --- a/src/formats/epk2.rs +++ b/src/formats/epk2.rs @@ -1,6 +1,6 @@ use std::fs::{self, File, OpenOptions}; use std::path::{Path}; -use std::io::{Write, Seek, SeekFrom}; +use std::io::{Write, Seek, SeekFrom, Cursor}; use aes::Aes128; use ecb::{Decryptor, cipher::{BlockDecryptMut, KeyInit, generic_array::GenericArray}}; @@ -22,6 +22,27 @@ struct Pak { name: String, } +static KEYS: &[(&str, &str)] = &[ + ("Saturn7/BCM3556", "2F2E2D2C2B2A29281716151413121110"), + ("new BCM35230", "6856A0482475A8B41728A35474810203"), + ("mtk5369 - Mediatek GP4", "7184C9C428D03C445188234D5A827196"), + ("mtk5398 (a2) - Mediatek NetCast 4/4.5", "385A992430196A8C44F1985823C01440"), +]; + +fn find_key<'a>(data: &[u8], expected_magic: &[u8]) -> Result)>, Box> { + for (name, key_hex) in KEYS { + let key_bytes = hex::decode(key_hex)?; + let decrypted = match decrypt_aes128_ecb(&key_bytes, data) { + Ok(d) => d, + Err(_) => continue, + }; + if decrypted.starts_with(expected_magic) { + return Ok(Some((name, key_bytes))); + } + } + Ok(None) +} + type Aes128EcbDec = Decryptor; fn decrypt_aes128_ecb(key: &[u8], ciphertext: &[u8]) -> Result, Box> { @@ -38,49 +59,67 @@ fn decrypt_aes128_ecb(key: &[u8], ciphertext: &[u8]) -> Result, Box Result<(), Box> { + file.seek(SeekFrom::Start(128))?; //inital signature - file.seek(SeekFrom::Start(128))?; + let stored_header = common::read_exact(&mut file, 1584)?; //max header size + let header; + + let mut matching_key: Option> = None; //check if header is encrypted - let epak = common::read_exact(&mut file, 4)?; // epak magic + let epak = &stored_header[0..4]; // epak magic if epak == b"epak" { println!("Header is not encrypted."); + header = stored_header; } else { - println!("Header is encrypted. Not supported yet"); - return Ok(()); + println!("Header is encrypted..."); + println!("\nFinding key..."); + //find the key, knowing that the header should start with "epak" + if let Some((key_name, key_bytes)) = find_key(&stored_header, b"epak")? { + println!("Found valid key: {}", key_name); + matching_key = Some(key_bytes); + header = decrypt_aes128_ecb(matching_key.as_ref().unwrap(), &stored_header)?; + } else { + println!("No valid key found!"); + return Ok(()); + } } + //parse header + let mut hdr_reader = Cursor::new(header); - let file_size_bytes = common::read_exact(&mut file, 4)?; + let _epk = common::read_exact(&mut hdr_reader, 4)?; + + let file_size_bytes = common::read_exact(&mut hdr_reader, 4)?; let file_size = u32::from_le_bytes(file_size_bytes.try_into().unwrap()); - let pak_count_bytes = common::read_exact(&mut file, 4)?; + let pak_count_bytes = common::read_exact(&mut hdr_reader, 4)?; let pak_count = u32::from_le_bytes(pak_count_bytes.try_into().unwrap()); - let _epk2 = common::read_exact(&mut file, 4)?; // EPK2 magic + let _epk2 = common::read_exact(&mut hdr_reader, 4)?; // EPK2 magic - let version = common::read_exact(&mut file, 4)?; + let version = common::read_exact(&mut hdr_reader, 4)?; - let ota_id_bytes = common::read_exact(&mut file, 32)?; + let ota_id_bytes = common::read_exact(&mut hdr_reader, 32)?; let ota_id = common::string_from_bytes(&ota_id_bytes); println!("\nEPK info:\nFile size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}\n", file_size, pak_count, ota_id, version[3], version[2], version[1]); let mut paks: Vec = Vec::new(); - + //parse paks in header for i in 0..pak_count { - let offset_bytes = common::read_exact(&mut file, 4)?; + let offset_bytes = common::read_exact(&mut hdr_reader, 4)?; let offset = u32::from_le_bytes(offset_bytes.try_into().unwrap()) + 128; //add 128 bytes of initial signature - let size_bytes = common::read_exact(&mut file, 4)?; + let size_bytes = common::read_exact(&mut hdr_reader, 4)?; let size = u32::from_le_bytes(size_bytes.try_into().unwrap()); - let name_bytes = common::read_exact(&mut file, 4)?; + let name_bytes = common::read_exact(&mut hdr_reader, 4)?; let name = common::string_from_bytes(&name_bytes); - let _version = common::read_exact(&mut file, 4)?; + let _version = common::read_exact(&mut hdr_reader, 4)?; - let segment_size_bytes = common::read_exact(&mut file, 4)?; + let segment_size_bytes = common::read_exact(&mut hdr_reader, 4)?; let segment_size = u32::from_le_bytes(segment_size_bytes.try_into().unwrap()); println!("Pak {}: {}, offset: {}, size: {}, segment size: {}", i + 1, name, offset, size, segment_size); @@ -88,21 +127,9 @@ pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box