diff --git a/src/formats.rs b/src/formats.rs index 3c15d68..dbbd436 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -8,4 +8,5 @@ pub mod msd10; pub mod msd11; pub mod sddl_sec; pub mod epk2; -pub mod epk; \ No newline at end of file +pub mod epk; +pub mod epk3; \ No newline at end of file diff --git a/src/formats/epk.rs b/src/formats/epk.rs index 0f23f63..4a526fb 100644 --- a/src/formats/epk.rs +++ b/src/formats/epk.rs @@ -51,8 +51,56 @@ pub fn extract_epk(file: &File, output_folder: &str) -> Result<(), Box(key_array: &'a [(&'a str, &'a str)], data: &[u8], expected_magic: &[u8]) -> Result)>, Box> { + for (key_hex, name) in key_array { + let key_bytes = hex::decode(key_hex)?; + let decrypted = match decrypt_aes_ecb_auto(&key_bytes, data) { + Ok(d) => d, + Err(_) => continue, + }; + + if decrypted.starts_with(expected_magic) { + return Ok(Some((name, key_bytes))); + } + } + Ok(None) +} + +use aes::Aes128; +use aes::Aes256; +use ecb::{Decryptor, cipher::{BlockDecryptMut, KeyInit, generic_array::GenericArray}}; + +type Aes128EcbDec = Decryptor; +type Aes256EcbDec = Decryptor; + +pub fn decrypt_aes_ecb_auto(key: &[u8], ciphertext: &[u8]) -> Result, Box> { + let mut buffer = ciphertext.to_vec(); + + if key.len() == 32 { + // aes256 + let key_array: [u8; 32] = key.try_into()?; + let mut decryptor = Aes256EcbDec::new(&key_array.into()); + for chunk in buffer.chunks_exact_mut(16) { + let block: &mut [u8; 16] = chunk.try_into()?; + decryptor.decrypt_block_mut(GenericArray::from_mut_slice(block)); + } + } else { + // aes128 + let key_array: [u8; 16] = key.try_into()?; + let mut decryptor = Aes128EcbDec::new(&key_array.into()); + for chunk in buffer.chunks_exact_mut(16) { + let block: &mut [u8; 16] = chunk.try_into()?; + decryptor.decrypt_block_mut(GenericArray::from_mut_slice(block)); + } + } + + Ok(buffer) } \ No newline at end of file diff --git a/src/formats/epk2.rs b/src/formats/epk2.rs index 9cb34cd..7eabd24 100644 --- a/src/formats/epk2.rs +++ b/src/formats/epk2.rs @@ -2,11 +2,9 @@ use std::fs::{self, File, OpenOptions}; use std::path::{Path}; use std::io::{Write, Seek, SeekFrom, Cursor}; -use aes::Aes128; -use ecb::{Decryptor, cipher::{BlockDecryptMut, KeyInit, generic_array::GenericArray}}; - use crate::common; use crate::keys; +use crate::formats::epk::{decrypt_aes_ecb_auto, find_key}; pub fn is_epk2_file(file: &File) -> bool { let header = common::read_file(&file, 128, 4).expect("Failed to read from file."); @@ -23,35 +21,6 @@ struct Pak { name: String, } -fn find_key<'a>(data: &[u8], expected_magic: &[u8]) -> Result)>, Box> { - for (key_hex, name) in keys::EPK2 { - 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> { - let key_array: [u8; 16] = key.try_into()?; - let mut decryptor = Aes128EcbDec::new(&key_array.into()); - let mut buffer = ciphertext.to_vec(); - - for chunk in buffer.chunks_exact_mut(16) { - let block: &mut [u8; 16] = chunk.try_into()?; - decryptor.decrypt_block_mut(GenericArray::from_mut_slice(block)); - } - - Ok(buffer) -} - pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box> { file.seek(SeekFrom::Start(128))?; //inital signature @@ -69,10 +38,10 @@ pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box Result<(), Box Result<(), Box> { + file.seek(SeekFrom::Start(128))?; //inital signature + + let stored_header = common::read_exact(&mut file, 1584)?; //max header size + let header: Vec; + + let mut new_type = false; + + let mut matching_key: Option> = None; + println!("Finding key..."); + + // find the key, knowing that the header should start with "EPK3" + if let Some((key_name, key_bytes)) = find_key(&keys::EPK3, &stored_header, b"EPK3")? { + println!("Found valid key: {}", key_name); + matching_key = Some(key_bytes); + header = decrypt_aes_ecb_auto(matching_key.as_ref().unwrap(), &stored_header)?; + + //try for new format epk3 where theres an additional 128byte signature at the beginning + } else if let Some((key_name, key_bytes)) = find_key(&keys::EPK3, &stored_header[128..], b"EPK3")? { + println!("Found valid key: {}", key_name); + matching_key = Some(key_bytes); + header = decrypt_aes_ecb_auto(matching_key.as_ref().unwrap(), &stored_header)?; + new_type = true; + + } else { + println!("No valid key found!"); + return Ok(()); + } + + let signature_size = if new_type {256} else {128}; + let extra_segment_size = if new_type {4} else {0}; + + let matching_key_bytes = matching_key.as_ref().unwrap(); + + //parse header + let mut hdr_reader = Cursor::new(header); + + if new_type {let _signature = common::read_exact(&mut hdr_reader, 128)?;}; + + let _epk3 = common::read_exact(&mut hdr_reader, 4)?; //EPK3 magic + + let version = common::read_exact(&mut hdr_reader, 4)?; + + let ota_id_bytes = common::read_exact(&mut hdr_reader, 32)?; + let ota_id = common::string_from_bytes(&ota_id_bytes); + + let package_info_size_bytes = common::read_exact(&mut hdr_reader, 4)?; + let package_info_size = u32::from_le_bytes(package_info_size_bytes.try_into().unwrap()); + + println!("\nEPK info:\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}\nPackage Info size: {}\n", + ota_id, version[3], version[2], version[1], package_info_size); + // + + let _versions = common::read_exact(&mut file, 36)?; + + let _signature = common::read_exact(&mut file, signature_size)?; + + //PKG INFO + let pkg_info_encrypted = common::read_exact(&mut file, package_info_size as usize)?; + let pkg_info = decrypt_aes_ecb_auto(matching_key_bytes, &pkg_info_encrypted)?; + + let mut pkg_info_reader = Cursor::new(pkg_info); + + let package_info_list_size_b = common::read_exact(&mut pkg_info_reader, 4)?; + let package_info_list_size = u32::from_le_bytes(package_info_list_size_b.try_into().unwrap()); + + let package_info_count_b = common::read_exact(&mut pkg_info_reader, 4)?; + let package_info_count = u32::from_le_bytes(package_info_count_b.try_into().unwrap()); + + println!("Package info list size: {}\nPackage info count: {}", + package_info_list_size, package_info_count); + + if new_type {let _unknown = common::read_exact(&mut pkg_info_reader, 4)?;}; //uncertain if this is only in new type, but i think it is + + while (pkg_info_reader.position() as usize) < pkg_info_reader.get_ref().len() { + let segment = common::read_exact(&mut pkg_info_reader, 324)?; + + let package_name_b = &segment[8..136]; + let package_name = common::string_from_bytes(&package_name_b); + + let package_size_b = &segment[296..300]; + let package_size = u32::from_le_bytes(package_size_b.try_into().unwrap()); + + //Package segment info + let segment_index_b = &segment[308..312]; + let mut segment_index = u32::from_le_bytes(segment_index_b.try_into().unwrap()); + + let segment_count_b = &segment[312..316]; + let segment_count = u32::from_le_bytes(segment_count_b.try_into().unwrap()); + + let segment_size_b = &segment[316..320]; + let mut segment_size = u32::from_le_bytes(segment_size_b.try_into().unwrap()); + // + + println!("\nPak - {}, Size: {}, Segment Count: {}", + package_name, package_size, segment_count); + + for i in 0..segment_count { + if i > 0 { + let segment = common::read_exact(&mut pkg_info_reader, 324)?; + + let segment_index_b = &segment[308..312]; + segment_index = u32::from_le_bytes(segment_index_b.try_into().unwrap()); + + let segment_size_b = &segment[316..320]; + segment_size = u32::from_le_bytes(segment_size_b.try_into().unwrap()); + } + + println!("- Segment {}/{}, Size: {}", segment_index + 1, segment_count, segment_size); + + let _signature = common::read_exact(&mut file, signature_size)?; + + let encrypted_data = common::read_exact(&mut file, segment_size as usize + extra_segment_size)?; + let out_data = decrypt_aes_ecb_auto(matching_key_bytes, &encrypted_data)?; + + let output_path = Path::new(&output_folder).join(package_name.clone() + ".bin"); + + fs::create_dir_all(&output_folder)?; + let mut out_file = OpenOptions::new() + .append(true) + .create(true) + .open(output_path)?; + + out_file.write_all(&out_data[extra_segment_size..])?; + + println!("-- Saved to file!"); + } + } + + Ok(()) +} \ No newline at end of file diff --git a/src/keys.rs b/src/keys.rs index a0c4229..3c4d529 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -1,4 +1,5 @@ //samsung old keys +//github.com/george-hopkins/samygo-patcher pub static SAMSUNG: &[(&str, &str)] = &[ ("T-GA", "SHWJUH:85a045ae-2296-484c-b457-ede832fcfbe1-646390a3-105e-40aa-85f6-da3086c70111"), ("T-MST5", "SHWJUH:eceb2c14-db11-425e-9ebf-5f9607f0eb4b-3c38193e-751e-4719-8884-9e76322c0cec"), @@ -21,6 +22,7 @@ pub static PFLUPG: &[(&str, &str)] = &[ ]; //MSD10 keys +//github.com/bugficks/msddecrypt //fw prefix, type, key pub static MSD10: &[(&str, &str, &str)] = &[ ("T-NT14M", "old", "95d01e0bae861a05695bc8a6edb2ea835a09accd"), @@ -29,6 +31,7 @@ pub static MSD10: &[(&str, &str, &str)] = &[ ]; //MSD11 keys +//github.com/bugficks/msddecrypt pub static MSD11: &[(&str, &str)] = &[ ("T-JZM", "9b1d077c0d137d406c79ddacb6b159fe"), //2015 ("T-HKMFK", "c7097975e8ab994beb5eaae57e0ba77c"), //2016 @@ -41,6 +44,7 @@ pub static MSD11: &[(&str, &str)] = &[ ]; //epk2 keys +//github.com/openlgtv/epk2extract pub static EPK2: &[(&str, &str)] = &[ ("214BF3C129547AF31D32A5ECB4742192", "unknown"), ("1F1E1D1C1B1A19180706050403020100", "BCM35230/early MTK5369 and LG1152"), @@ -57,4 +61,11 @@ pub static EPK2: &[(&str, &str)] = &[ ("68A284B4953CAD15024BED2C4F852A09", "lm14 - MStar NetCast 4.5"), ("4813B5B63C998A2874EF3320684AC8D9", "lg1152 - LX GP4"), ("14B3623488212250C7C992AACD537447", "lg115x - LX NC4"), +]; + +//epk3 keys +//github.com/openlgtv/epk2extract +pub static EPK3: &[(&str, &str)] = &[ + ("34CC219D3AFC102433109BBC1DA44095", "m14 (m14tv) - LX webOS 1 (2014)"), + ("52A208FA24E7E70730A40999B1C22C148F4920484BC50B515D243E35D14689F1", "o24n - LX webOS 10 (2025)") ]; \ No newline at end of file