mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-16 05:04:22 +02:00
Epk with crypted header + Find key for epk2
This commit is contained in:
@@ -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?
|
||||||
+1
-1
@@ -14,6 +14,7 @@ pub fn is_epk_file(file: &File) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_epk_version(versions: &[u8]) -> Option<String> {
|
fn check_epk_version(versions: &[u8]) -> Option<String> {
|
||||||
|
// _ - 0x00 X - a number . - a dot
|
||||||
let epk2_pattern = "____XXXX.XXXX.XXXX__XX.XX.XXX_______";
|
let epk2_pattern = "____XXXX.XXXX.XXXX__XX.XX.XXX_______";
|
||||||
let epk3_pattern = "____X.X.X___________X.X.X___________";
|
let epk3_pattern = "____X.X.X___________X.X.X___________";
|
||||||
|
|
||||||
@@ -32,7 +33,6 @@ fn match_with_pattern(data: &[u8], pattern: &str) -> bool {
|
|||||||
b'_' if b != 0x00 => return false,
|
b'_' if b != 0x00 => return false,
|
||||||
b'X' if !b.is_ascii_digit() => return false,
|
b'X' if !b.is_ascii_digit() => return false,
|
||||||
b'.' if b != b'.' => return false,
|
b'.' if b != b'.' => return false,
|
||||||
_ if p != b'_' && p != b'X' && p != b'.' => return false,
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+74
-31
@@ -1,6 +1,6 @@
|
|||||||
use std::fs::{self, File, OpenOptions};
|
use std::fs::{self, File, OpenOptions};
|
||||||
use std::path::{Path};
|
use std::path::{Path};
|
||||||
use std::io::{Write, Seek, SeekFrom};
|
use std::io::{Write, Seek, SeekFrom, Cursor};
|
||||||
|
|
||||||
use aes::Aes128;
|
use aes::Aes128;
|
||||||
use ecb::{Decryptor, cipher::{BlockDecryptMut, KeyInit, generic_array::GenericArray}};
|
use ecb::{Decryptor, cipher::{BlockDecryptMut, KeyInit, generic_array::GenericArray}};
|
||||||
@@ -22,6 +22,27 @@ struct Pak {
|
|||||||
name: String,
|
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<Option<(&'a str, Vec<u8>)>, Box<dyn std::error::Error>> {
|
||||||
|
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<Aes128>;
|
type Aes128EcbDec = Decryptor<Aes128>;
|
||||||
|
|
||||||
fn decrypt_aes128_ecb(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
fn decrypt_aes128_ecb(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
||||||
@@ -38,49 +59,67 @@ fn decrypt_aes128_ecb(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, Box<dyn
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
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<Vec<u8>> = None;
|
||||||
|
|
||||||
//check if header is encrypted
|
//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" {
|
if epak == b"epak" {
|
||||||
println!("Header is not encrypted.");
|
println!("Header is not encrypted.");
|
||||||
|
header = stored_header;
|
||||||
} else {
|
} else {
|
||||||
println!("Header is encrypted. Not supported yet");
|
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(());
|
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 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 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);
|
let ota_id = common::string_from_bytes(&ota_id_bytes);
|
||||||
|
|
||||||
println!("\nEPK info:\nFile size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}\n",
|
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]);
|
file_size, pak_count, ota_id, version[3], version[2], version[1]);
|
||||||
|
|
||||||
let mut paks: Vec<Pak> = Vec::new();
|
let mut paks: Vec<Pak> = Vec::new();
|
||||||
|
//parse paks in header
|
||||||
for i in 0..pak_count {
|
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 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 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 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());
|
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);
|
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<dyn
|
|||||||
paks.push(Pak { offset, size, name });
|
paks.push(Pak { offset, size, name });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saturn7/BCM3556
|
|
||||||
//let key = "2F2E2D2C2B2A29281716151413121110";
|
|
||||||
|
|
||||||
// new BCM35230
|
|
||||||
let key = "6856A0482475A8B41728A35474810203";
|
|
||||||
|
|
||||||
//mtk5369 - Mediatek GP4 - HE_DTV_GP4I_AFAAATAA
|
|
||||||
//let key = "7184C9C428D03C445188234D5A827196";
|
|
||||||
|
|
||||||
let key_bytes = hex::decode(key)?;
|
|
||||||
|
|
||||||
let mut signature_count = 0;
|
let mut signature_count = 0;
|
||||||
|
//extract paks
|
||||||
for (pak_n, pak) in paks.iter().enumerate() {
|
for (pak_n, pak) in paks.iter().enumerate() {
|
||||||
|
|
||||||
let actual_offset = pak.offset + (128 * signature_count);
|
let actual_offset = pak.offset + (128 * signature_count);
|
||||||
|
|
||||||
file.seek(SeekFrom::Start(actual_offset as u64))?;
|
file.seek(SeekFrom::Start(actual_offset as u64))?;
|
||||||
@@ -111,7 +138,23 @@ pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
|||||||
signature_count += 1;
|
signature_count += 1;
|
||||||
|
|
||||||
let encrypted_header = common::read_exact(&mut file, 128)?;
|
let encrypted_header = common::read_exact(&mut file, 128)?;
|
||||||
let header = decrypt_aes128_ecb(&key_bytes, &encrypted_header)?;
|
|
||||||
|
//the file's header was not encrypted so we dont have the key yet
|
||||||
|
if matching_key.is_none() {
|
||||||
|
println!("\nFinding key...");
|
||||||
|
//find the key, knowing that the header should start with with the paks name
|
||||||
|
if let Some((key_name, key_bytes)) = find_key(&encrypted_header, pak.name.as_bytes())? {
|
||||||
|
println!("Found correct key: {}", key_name);
|
||||||
|
matching_key = Some(key_bytes);
|
||||||
|
} else {
|
||||||
|
println!("No valid key found!");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let matching_key_bytes = matching_key.as_ref().unwrap();
|
||||||
|
|
||||||
|
let header = decrypt_aes128_ecb(&matching_key_bytes, &encrypted_header)?;
|
||||||
|
|
||||||
let segment_count = u32::from_le_bytes(header[84..88].try_into().unwrap());
|
let segment_count = u32::from_le_bytes(header[84..88].try_into().unwrap());
|
||||||
let mut segment_size = u32::from_le_bytes(header[88..92].try_into().unwrap());
|
let mut segment_size = u32::from_le_bytes(header[88..92].try_into().unwrap());
|
||||||
@@ -125,7 +168,7 @@ pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
|||||||
signature_count += 1;
|
signature_count += 1;
|
||||||
|
|
||||||
let encrypted_header = common::read_exact(&mut file, 128)?;
|
let encrypted_header = common::read_exact(&mut file, 128)?;
|
||||||
let header = decrypt_aes128_ecb(&key_bytes, &encrypted_header)?;
|
let header = decrypt_aes128_ecb(&matching_key_bytes, &encrypted_header)?;
|
||||||
segment_size = u32::from_le_bytes(header[88..92].try_into().unwrap());
|
segment_size = u32::from_le_bytes(header[88..92].try_into().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +192,7 @@ pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
|||||||
};
|
};
|
||||||
|
|
||||||
let segment_data = common::read_exact(&mut file, actual_segment_size as usize)?;
|
let segment_data = common::read_exact(&mut file, actual_segment_size as usize)?;
|
||||||
let out_data = decrypt_aes128_ecb(&key_bytes, &segment_data)?;
|
let out_data = decrypt_aes128_ecb(&matching_key_bytes, &segment_data)?;
|
||||||
|
|
||||||
println!("- Segment {}/{}, size: {}", i + 1, segment_count, actual_segment_size);
|
println!("- Segment {}/{}, size: {}", i + 1, segment_count, actual_segment_size);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user