Overhaul epk(epk1,epk2,epk3), add epk2b type extractor

This commit is contained in:
theubusu
2026-01-24 16:36:20 +01:00
parent 8d4f8d5366
commit 060d808b44
8 changed files with 300 additions and 137 deletions
+1
View File
@@ -24,6 +24,7 @@ pub mod msd11;
pub mod epk;
pub mod epk1;
pub mod epk2;
pub mod epk2b;
pub mod epk3;
pub mod mtk_pkg;
+4 -1
View File
@@ -43,9 +43,12 @@ fn match_with_pattern(data: &[u8], pattern: &str) -> bool {
}
pub fn extract_epk(file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
let versions = common::read_file(&file, 1712, 36)?;
let epk_version = check_epk_version(&versions);
let platform_version = common::string_from_bytes(&versions[4..20]);
let sdk_version = common::string_from_bytes(&versions[20..36]);
println!("Platform version: {}\nSDK version: {}", platform_version, sdk_version);
if epk_version == Some("epk2".to_string()) {
println!("EPK2 detected!\n");
+37 -42
View File
@@ -13,17 +13,12 @@ struct CommonHeader {
pak_count: u32,
}
#[derive(BinRead)]
struct Pak {
offset : u32,
size : u32,
}
#[derive(BinRead)]
struct PakHeader {
#[br(count = 4)] pak_name_bytes: Vec<u8>,
stored_size: u32,
#[br(count = 15)] platform_id_bytes: Vec<u8>,
image_size: u32,
#[br(count = 64)] platform_id_bytes: Vec<u8>,
#[br(count = 56)] _reserved: Vec<u8>,
}
impl PakHeader {
fn pak_name(&self) -> String {
@@ -34,9 +29,16 @@ impl PakHeader {
}
}
#[derive(BinRead)]
struct Pak {
offset : u32,
size : u32,
}
pub fn is_epk1_file(file: &File) -> bool {
let header = common::read_file(&file, 0, 4).expect("Failed to read from file.");
if header == b"epak" {
let epk2_magic = common::read_file(&file, 12, 4).expect("Failed to read from file."); //for epk2b
let epak_magic = common::read_file(&file, 0, 4).expect("Failed to read from file.");
if epak_magic == b"epak" && epk2_magic != b"EPK2" {
true
} else {
false
@@ -52,13 +54,11 @@ pub fn extract_epk1(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
if init_pak_count > 256 {
println!("\nBig endian EPK1 detected.");
epk1_type = "be";
} else if init_pak_count < 21 {
} else if init_pak_count < 33 {
println!("\nLittle endian EPK1 detected.");
epk1_type = "le";
} else {
//println!("\nEPK1(new) detected.");
//epk1_type = "new";
println!("\nNot supported!");
println!("\nUnknown EPK1 variant!");
return Ok(());
}
@@ -71,66 +71,61 @@ pub fn extract_epk1(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
for _i in 0..10 { //header can fit max 10 pak entries
let pak: Pak = file.read_be()?;
if pak.offset == 0 && pak.size == 0 {
continue;
}
paks.push(Pak { offset: pak.offset, size: pak.size });
}
assert!(header.pak_count as usize == paks.len(), "Paks count in header does not match the amount of non empty pak entries!");
assert!(header.pak_count as usize == paks.len(), "Paks count in header({}) does not match the amount of non empty pak entries({})!", header.pak_count, paks.len());
let version = common::read_exact(&mut file, 4)?;
println!("EPK info:\nFile size: {}\nPak count: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
println!("EPK info -\nData size: {}\nPak count: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
header.file_size, header.pak_count, version[1], version[2], version[3]);
} else if epk1_type == "le" {
let header: CommonHeader = file.read_le()?;
for _i in 0..20 { //header can fit max 20 pak entries
let pak: Pak = file.read_le()?;
//this is to make an odd variant with 32 max pak entries work
let header_size_bytes = common::read_file(&file, 12, 4)?; //offset of first entry, can be treated as header size
let header_size = u32::from_le_bytes(header_size_bytes.try_into().unwrap());
let max_pak_count = (header_size - 48) / 8; //header size minus common header + ota id (48) divide by size of pak entry (8).
assert!(max_pak_count < 128, "Unreasonable calculated pak count {}!!", max_pak_count);
for _i in 0..max_pak_count {
let pak: Pak = file.read_le()?;
if pak.offset == 0 && pak.size == 0 {
continue;
}
paks.push(Pak { offset: pak.offset, size: pak.size });
}
assert!(header.pak_count as usize == paks.len(), "Paks count in header does not match the amount of non empty pak entries!");
assert!(header.pak_count as usize == paks.len(), "Paks count in header({}) does not match the amount of non empty pak entries({})!", header.pak_count, paks.len());
let version = common::read_exact(&mut file, 4)?;
let ota_id_bytes = common::read_exact(&mut file, 32)?;
let ota_id = common::string_from_bytes(&ota_id_bytes);
println!("EPK info:\nFile size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
header.file_size, header.pak_count, ota_id, version[2], version[1], version[0]);
println!("EPK info -\nData size: {}\nHeader size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
header.file_size, header_size, header.pak_count, ota_id, version[2], version[1], version[0]);
}
for (i, pak) in paks.iter().enumerate() {
file.seek(SeekFrom::Start(pak.offset as u64))?;
let pak_header: PakHeader = if epk1_type == "be" {file.read_be()?} else {file.read_le()?};
file.seek(SeekFrom::Start(pak.offset as u64))?;
let pak_header: PakHeader = if epk1_type == "be" {file.read_be()?} else {file.read_le()?};
let data = common::read_file(&file, pak.offset as u64 + 128, pak.size as usize - 128)?;
println!("\n({}/{}) - {}, Offset: {}, Size: {}, Platform: {}",
i + 1, paks.len(), pak_header.pak_name(), pak.offset, pak_header.image_size, pak_header.platform_id());
println!("\n({}/{}) - {}, Offset: {}, Size: {}, Platform: {}",
i + 1, paks.len(), pak_header.pak_name(), pak.offset, pak.size, pak_header.platform_id());
let data = common::read_exact(&mut file, pak_header.image_size as usize)?;
let output_path = Path::new(&output_folder).join(pak_header.pak_name() + ".bin");
let output_path = Path::new(&output_folder).join(pak_header.pak_name() + ".bin");
fs::create_dir_all(&output_folder)?;
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
fs::create_dir_all(&output_folder)?;
let mut out_file = OpenOptions::new()
.write(true)
.create(true)
.open(output_path)?;
out_file.write_all(&data[..pak_header.stored_size as usize])?;
println!("- Saved file!");
}
println!("- Saved file!");
}
println!("\nExtraction finished!");
+58 -42
View File
@@ -7,12 +7,14 @@ use crate::utils::common;
use crate::keys;
use crate::formats::epk::{decrypt_aes_ecb_auto, find_key};
static SIGNATURE_SIZE: u32 = 128;
#[derive(BinRead)]
struct Header {
#[br(count = 4)] _magic_bytes: Vec<u8>,
#[br(count = 4)] _magic_bytes: Vec<u8>, //epak
file_size: u32,
pak_count: u32,
#[br(count = 4)] _epk2_magic: Vec<u8>,
#[br(count = 4)] _epk2_magic: Vec<u8>, //EPK2
#[br(count = 4)] version: Vec<u8>,
#[br(count = 32)] ota_id_bytes: Vec<u8>,
}
@@ -36,6 +38,33 @@ impl PakEntry {
}
}
#[derive(BinRead)]
struct PakHeader {
#[br(count = 4)] _pak_name_bytes: Vec<u8>,
image_size: u32,
#[br(count = 64)] platform_id_bytes: Vec<u8>,
_sw_version: u32,
_sw_date: u32,
_build_type: u32,
segment_count: u32,
segment_size: u32,
segment_index: u32,
#[br(count = 4)] _pak_magic_bytes: Vec<u8>, //MPAK
#[br(count = 24)] _reserved: Vec<u8>,
_segment_crc32: u32,
}
impl PakHeader {
fn platform_id(&self) -> String {
common::string_from_bytes(&self.platform_id_bytes)
}
}
struct Pak {
offset: u32,
_size: u32,
name: String,
}
pub fn is_epk2_file(file: &File) -> bool {
let header = common::read_file(&file, 128, 4).expect("Failed to read from file.");
if header == b"epak" {
@@ -45,12 +74,6 @@ pub fn is_epk2_file(file: &File) -> bool {
}
}
struct Pak {
offset: u32,
size: u32,
name: String,
}
pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
file.seek(SeekFrom::Start(128))?; //inital signature
@@ -68,7 +91,7 @@ pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
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(&keys::EPK2, &stored_header, b"epak")? {
if let Some((key_name, key_bytes)) = find_key(&keys::EPK, &stored_header, b"epak")? {
println!("Found valid key: {}", key_name);
matching_key = Some(key_bytes);
header = decrypt_aes_ecb_auto(matching_key.as_ref().unwrap(), &stored_header)?;
@@ -81,27 +104,25 @@ pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
let mut hdr_reader = Cursor::new(header);
let hdr: Header = hdr_reader.read_le()?;
println!("\nEPK info:\nFile size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}\n",
hdr.file_size, hdr.pak_count, hdr.ota_id(), hdr.version[3], hdr.version[2], hdr.version[1]);
println!("\nEPK info -\nData size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}.{:02x?}\n",
hdr.file_size, hdr.pak_count, hdr.ota_id(), hdr.version[3], hdr.version[2], hdr.version[1], hdr.version[0]);
let mut paks: Vec<Pak> = Vec::new();
//parse paks in header
for i in 0..hdr.pak_count {
let pak: PakEntry = hdr_reader.read_le()?;
println!("Pak {}: {}, offset: {}, size: {}, segment size: {}", i + 1, pak.name(), pak.offset + 128, pak.size, pak.segment_size);
paks.push(Pak { offset: pak.offset + 128, size: pak.size, name: pak.name() });
//here the accounted for signature is the one at the beginning of the EPK file
println!("Pak {} - {}, offset: {}, size: {}, segment size: {}", i + 1, pak.name(), pak.offset + SIGNATURE_SIZE, pak.size, pak.segment_size);
paks.push(Pak { offset: pak.offset + SIGNATURE_SIZE, _size: pak.size, name: pak.name() });
}
let mut signature_count = 0;
//extract paks
for (pak_n, pak) in paks.iter().enumerate() {
let actual_offset = pak.offset + (128 * signature_count);
let actual_offset = pak.offset + (SIGNATURE_SIZE * signature_count);
file.seek(SeekFrom::Start(actual_offset as u64))?;
let _signature = common::read_exact(&mut file, 128)?;
let _signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?;
signature_count += 1;
let encrypted_header = common::read_exact(&mut file, 128)?;
@@ -110,7 +131,7 @@ pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
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(&keys::EPK2, &encrypted_header, pak.name.as_bytes())? {
if let Some((key_name, key_bytes)) = find_key(&keys::EPK, &encrypted_header, pak.name.as_bytes())? {
println!("Found correct key: {}", key_name);
matching_key = Some(key_bytes);
} else {
@@ -118,59 +139,54 @@ pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
return Ok(());
}
}
let matching_key_bytes = matching_key.as_ref().unwrap();
let header = decrypt_aes_ecb_auto(&matching_key_bytes, &encrypted_header)?;
let mut pak_header_reader = Cursor::new(decrypt_aes_ecb_auto(&matching_key_bytes, &encrypted_header)?);
let mut pak_header: PakHeader = pak_header_reader.read_le()?;
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());
println!("\n({}/{}) - {}, Size: {}, Segment count: {}, Platform: {}",
pak_n + 1, paks.len(), pak.name, pak_header.image_size, pak_header.segment_count, pak_header.platform_id());
println!("\n({}/{}) - {}, Size: {}, Segments: {}", pak_n + 1, paks.len(), pak.name, pak.size, segment_count);
for i in 0..segment_count {
for i in 0..pak_header.segment_count {
// for first segment we already read the header so skip doing that for it
if i > 0 {
let _signature = common::read_exact(&mut file, 128)?;
signature_count += 1;
let encrypted_header = common::read_exact(&mut file, 128)?;
let header = decrypt_aes_ecb_auto(&matching_key_bytes, &encrypted_header)?;
segment_size = u32::from_le_bytes(header[88..92].try_into().unwrap());
let mut pak_header_reader = Cursor::new(decrypt_aes_ecb_auto(&matching_key_bytes, &encrypted_header)?);
pak_header = pak_header_reader.read_le()?;
}
assert!(i == pak_header.segment_index, "Unexpected segment index in pak header!, expected: {}, got: {}", i , pak_header.segment_index);
let actual_segment_size =
// check if this is the last segment and not the last PAK
if i == segment_count - 1 && pak_n < paks.len() - 1 {
if i == pak_header.segment_count - 1 && pak_n < paks.len() - 1{
// calculate distance to next PAK
let next_pak_offset = &paks[pak_n + 1].offset + (128 * signature_count);
let next_pak_offset = &paks[pak_n + 1].offset + (SIGNATURE_SIZE * signature_count);
let current_pos = file.stream_position()?;
let distance = next_pak_offset - current_pos as u32;
// if distance less than segment size, use the distance as actual size
if distance < segment_size {
if distance < pak_header.segment_size {
distance
} else {
segment_size
pak_header.segment_size
}
} else {
segment_size
pak_header.segment_size
};
println!("- Segment {}/{} - Size: {}", i + 1, pak_header.segment_count, actual_segment_size);
let segment_data = common::read_exact(&mut file, actual_segment_size as usize)?;
let out_data = decrypt_aes_ecb_auto(&matching_key_bytes, &segment_data)?;
println!("- Segment {}/{} - Size: {}", i + 1, segment_count, actual_segment_size);
let output_path = Path::new(&output_folder).join(pak.name.clone() + ".bin");
let output_path = Path::new(&output_folder).join(format!("{}.bin", pak.name));
fs::create_dir_all(&output_folder)?;
let mut out_file = OpenOptions::new()
.append(true)
.create(true)
.open(output_path)?;
let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?;
println!("-- Saved to file!");
+123
View File
@@ -0,0 +1,123 @@
use std::fs::File;
use std::path::{Path};
use std::fs::{self, OpenOptions};
use std::io::{Write, Seek, SeekFrom};
use binrw::{BinRead, BinReaderExt};
use crate::utils::common;
#[derive(BinRead)]
struct EpkHeader {
#[br(count = 4)] _epk_magic: Vec<u8>, //epak
file_size: u32,
pak_count: u32,
#[br(count = 4)] _epk2_magic: Vec<u8>, //EPK2
#[br(count = 4)] version: Vec<u8>,
#[br(count = 32)] ota_id_bytes: Vec<u8>,
}
impl EpkHeader {
fn ota_id(&self) -> String {
common::string_from_bytes(&self.ota_id_bytes)
}
}
#[derive(BinRead)]
struct PakHeader {
#[br(count = 4)] pak_name_bytes: Vec<u8>,
image_size: u32,
#[br(count = 64)] platform_id_bytes: Vec<u8>,
_sw_version: u32,
_sw_date: u32,
_build_type: u32,
segment_count: u32,
segment_size: u32,
segment_index: u32,
#[br(count = 4)] _pak_magic_bytes: Vec<u8>, //MPAK
#[br(count = 24)] _reserved: Vec<u8>,
_segment_crc32: u32,
}
impl PakHeader {
fn pak_name(&self) -> String {
common::string_from_bytes(&self.pak_name_bytes)
}
fn platform_id(&self) -> String {
common::string_from_bytes(&self.platform_id_bytes)
}
}
#[derive(BinRead)]
struct Pak {
offset : u32,
size : u32,
}
pub fn is_epk2b_file(file: &File) -> bool {
let epk2_magic = common::read_file(&file, 12, 4).expect("Failed to read from file.");
let epak_magic = common::read_file(&file, 0, 4).expect("Failed to read from file.");
if epak_magic == b"epak" && epk2_magic == b"EPK2" {
true
} else {
false
}
}
pub fn extract_epk2b(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
let header: EpkHeader = file.read_le()?;
println!("EPK info -\nData size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
header.file_size, header.pak_count, header.ota_id(), header.version[2], header.version[1], header.version[0]);
let mut paks: Vec<Pak> = Vec::new();
for _i in 0..10 { //header can fit max 10 pak entries
let pak: Pak = file.read_le()?;
if pak.offset == 0 && pak.size == 0 {
continue;
}
paks.push(Pak { offset: pak.offset, size: pak.size });
}
assert!(header.pak_count as usize == paks.len(), "Paks count in header does not match the amount of non empty pak entries!");
for (i, pak) in paks.iter().enumerate() {
file.seek(SeekFrom::Start(pak.offset as u64))?;
let mut pak_header: PakHeader = file.read_le()?;
let mut all_segment_size = 0;
println!("\n({}/{}) - {}, Size: {}, Segment count: {}, Platform: {}",
i + 1, paks.len(), pak_header.pak_name(), pak_header.image_size, pak_header.segment_count, pak_header.platform_id());
for i in 0..pak_header.segment_count {
// for first segment we already read the header so skip doing that for it
if i > 0 {
pak_header = file.read_le()?;
}
assert!(i == pak_header.segment_index, "Unexpected segment index in pak header!, expected: {}, got: {}", i , pak_header.segment_index);
println!("- Segment {}/{} - Size: {}", i + 1, pak_header.segment_count, pak_header.segment_size);
let out_data = common::read_exact(&mut file, pak_header.segment_size as usize)?;
all_segment_size += pak_header.segment_size;
//for the last segment, the extra data should be calculated and stripped.
let segment_limit = if i == pak_header.segment_count - 1 {
let diff = all_segment_size - pak_header.image_size;
pak_header.segment_size - diff
} else {
pak_header.segment_size
};
let output_path = Path::new(&output_folder).join(format!("{}.bin", pak_header.pak_name()));
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[..segment_limit as usize])?;
println!("-- Saved to file!");
}
}
println!("\nExtraction finished!");
Ok(())
}
+37 -14
View File
@@ -9,10 +9,11 @@ use crate::formats::epk::{decrypt_aes_ecb_auto, find_key};
#[derive(BinRead)]
struct Header {
#[br(count = 4)] _magic_bytes: Vec<u8>,
#[br(count = 4)] _magic_bytes: Vec<u8>, //EPK3
#[br(count = 4)] version: Vec<u8>,
#[br(count = 32)] ota_id_bytes: Vec<u8>,
package_info_size: u32,
_bchunked: u32,
}
impl Header {
fn ota_id(&self) -> String {
@@ -20,6 +21,25 @@ impl Header {
}
}
#[derive(BinRead)]
struct HeaderNewEx {
#[br(count = 4)] _pak_info_magic: Vec<u8>,
#[br(count = 6)] encrypt_type_bytes: Vec<u8>,
#[br(count = 6)] update_type_bytes: Vec<u8>,
update_platform_version: f32,
compatible_minimum_version: f32,
need_to_check_compatible_version: i32,
}
impl HeaderNewEx {
fn encrypt_type(&self) -> String {
common::string_from_bytes(&self.encrypt_type_bytes)
}
fn update_type(&self) -> String {
common::string_from_bytes(&self.update_type_bytes)
}
}
#[derive(BinRead)]
struct PkgInfoHeader {
package_info_list_size: u32,
@@ -62,13 +82,13 @@ pub fn extract_epk3(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
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")? {
if let Some((key_name, key_bytes)) = find_key(&keys::EPK, &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")? {
} else if let Some((key_name, key_bytes)) = find_key(&keys::EPK, &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)?;
@@ -89,10 +109,18 @@ pub fn extract_epk3(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
if new_type {let _signature = common::read_exact(&mut hdr_reader, 128)?;};
let hdr: Header = hdr_reader.read_le()?;
println!("\nEPK info:\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}\nPackage Info size: {}\n",
hdr.ota_id(), hdr.version[3], hdr.version[2], hdr.version[1], hdr.package_info_size);
//
let _versions = common::read_exact(&mut file, 36)?;
println!("\nEPK info -\nEPK3 type: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}.{:02x?}\nPackage Info size: {}",
if new_type {"New"} else {"Old"}, hdr.ota_id(), hdr.version[3], hdr.version[2], hdr.version[1], hdr.version[0], hdr.package_info_size);
if new_type {
let ex_hdr: HeaderNewEx = hdr_reader.read_le()?;
println!("Encrypt type: {}\nUpdate type: {}\nUpdate platform version: {:.6}\nCompatible minimum version: {:.6}\nNeed to check compatible version: {}",
ex_hdr.encrypt_type(), ex_hdr.update_type(), ex_hdr.update_platform_version, ex_hdr.compatible_minimum_version, ex_hdr.need_to_check_compatible_version);
}
println!();
let _platform_versions = common::read_exact(&mut file, 36)?;
let _signature = common::read_exact(&mut file, signature_size)?;
//PKG INFO
@@ -125,14 +153,9 @@ pub fn extract_epk3(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
let encrypted_data = common::read_exact(&mut file, entry.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(entry.package_name().clone() + ".bin");
let output_path = Path::new(&output_folder).join(format!("{}.bin", entry.package_name()));
fs::create_dir_all(&output_folder)?;
let mut out_file = OpenOptions::new()
.append(true)
.create(true)
.open(output_path)?;
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!");
+32 -34
View File
@@ -58,9 +58,35 @@ pub static PFLUPG: &[(&str, &str)] = &[
("q5492", "F28ED197C5C40B7F7356D4EDD5526BC238C812EB59400087BBA2D6402B41BBE695EAAE5F6051F1B5EBAC1756F17FED9DA0678F2E6EE59ABF530847DB9249DD025D00FFC67B2ECFF320E99F5DA8A46B1E41479BA3B5CDC110D247B7AF31E887463C8E7A9929BE511C724FB7B4BACBF055F276ED53687E24543815D5437405B0CB"),
];
//epk2 keys
//pana dvd keys (no AES)
pub static PANA_DVD_KEYONLY: &[&str] = &[
("08E03D859AF9F3EE"), //B3A25A0B9D864F08 # 87 firmwares, ..-~2011
];
//pana dvd keys (PANAEUSB pair (AES + cust))
pub static PANA_DVD_AESPAIR: &[(&str, &str)] = &[
("62A39E1C5594AE09244EB326EF7938FA", "06C943F3B997F7E0"), //# 9 firmwares, ~2018
];
//mtk pkg custom keys
//key, iv, desc
pub static MTK_PKG_CUST: &[(&str, &str, &str)] = &[
("D378EAF81D378A801B556985789A7C31", "73079FD19183715E130858588479C652", "Philips 2012"),
("47FBF8CAD62BB95AF3AD9509E5C2175D", "63120FB321B0410F216D6DC2D8641A11", "Philips 2013"),
("135AFB6DE91CD56496244BC7C0E08D63", "C6A38C89F0AF5637EB6E19D35E12E257", "TPV_MTK2K14PLF_EU1"),
("7B17F7764818AE2C897BA69D428D0CB3", "672041CCB9FCD4272B11E57AB6047163", "TPV_MTK2K16PLF_EU0"),
("1B569AA7D2E4CCE66584A7A3D8A45679", "A0E88D5D52A813260D3A34A14AA89416", "TPV_MTK2K17PLF_EU0"),
("B1BFAA407F70C80C650379DFEAFAA40F", "BC1443A0D17AAB2DB1EA0302EF280717", "Common"), //new mtk pkg, worked for Hisense/CVT/Changhong
("DB7D510A8DA635645A844B2F55C0B5BA", "93A97FF5C4BCE585122C7109FC2E1A3F", "Sony 2017 Linux"),
("9320FC32BCDCE3178B7B2BA2A1AF3391", "D93F84B9BDDC9E1BD3E7C114AAA2FAA3", "Sony 2015 Android"),
("F888FC0268C6692D7EB76243E53D47E5", "EE40FE4700EC3197336940D4B30D4BE8", "Sony 2016 Android"),
("07A30C2E4AAE4849EC3DD2301C381868", "BC1443A0D17AAB2DB1EA0302EF280717", "Philips Android"), //worked on TPM171E(2017), TPM191E(2019), TPM211E(2021)
];
//EPK keys
//github.com/openlgtv/epk2extract
pub static EPK2: &[(&str, &str)] = &[
pub static EPK: &[(&str, &str)] = &[
("214BF3C129547AF31D32A5ECB4742192", "unknown"),
("1F1E1D1C1B1A19180706050403020100", "BCM35230/early MTK5369 and LG1152"),
("7184C9C428D03C445188234D5A827196", "mtk5369 - Mediatek GP4"),
@@ -76,39 +102,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)"),
("5A167D8C342EF094800E7CFA2D10F2D0", "m14 (m14tv) - LX webOS 2 (2015)"),
("ADB92D9E23035522F4708CC259B31EA2", "m2 - MStar webOS 3.0 (2016)"),
("E529BCDEDF8E49667C0FA3A81174B65E", "o18 - LX webOS 4.0 (2018)"),
("74514676D68B9A72A0093CEF56D3067484E1F4D5CF7D4B4ED389BED030FA1B09", "k7lp - Realtek webOS 6 (2021)"),
("52A208FA24E7E70730A40999B1C22C148F4920484BC50B515D243E35D14689F1", "o24n - LX webOS 10 (2025)"),
];
//pana dvd keys (no AES)
pub static PANA_DVD_KEYONLY: &[&str] = &[
("08E03D859AF9F3EE"), //B3A25A0B9D864F08 # 87 firmwares, ..-~2011
];
//pana dvd keys (PANAEUSB pair (AES + cust))
pub static PANA_DVD_AESPAIR: &[(&str, &str)] = &[
("62A39E1C5594AE09244EB326EF7938FA", "06C943F3B997F7E0"), //# 9 firmwares, ~2018
];
//mtk pkg custom keys
//github.com/openlgtv/epk2extract
//key, iv, desc
pub static MTK_PKG_CUST: &[(&str, &str, &str)] = &[
("D378EAF81D378A801B556985789A7C31", "73079FD19183715E130858588479C652", "Philips 2012"),
("47FBF8CAD62BB95AF3AD9509E5C2175D", "63120FB321B0410F216D6DC2D8641A11", "Philips 2013"),
("135AFB6DE91CD56496244BC7C0E08D63", "C6A38C89F0AF5637EB6E19D35E12E257", "TPV_MTK2K14PLF_EU1"),
("7B17F7764818AE2C897BA69D428D0CB3", "672041CCB9FCD4272B11E57AB6047163", "TPV_MTK2K16PLF_EU0"),
("1B569AA7D2E4CCE66584A7A3D8A45679", "A0E88D5D52A813260D3A34A14AA89416", "TPV_MTK2K17PLF_EU0"),
("B1BFAA407F70C80C650379DFEAFAA40F", "BC1443A0D17AAB2DB1EA0302EF280717", "Common"), //new mtk pkg, worked for Hisense/CVT/Changhong
("DB7D510A8DA635645A844B2F55C0B5BA", "93A97FF5C4BCE585122C7109FC2E1A3F", "Sony 2017 Linux"),
("9320FC32BCDCE3178B7B2BA2A1AF3391", "D93F84B9BDDC9E1BD3E7C114AAA2FAA3", "Sony 2015 Android"),
("F888FC0268C6692D7EB76243E53D47E5", "EE40FE4700EC3197336940D4B30D4BE8", "Sony 2016 Android"),
("07A30C2E4AAE4849EC3DD2301C381868", "BC1443A0D17AAB2DB1EA0302EF280717", "Philips Android"), //worked on TPM171E(2017), TPM191E(2019), TPM211E(2021)
];
+8 -4
View File
@@ -90,16 +90,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("EPK1 file detected!");
formats::epk1::extract_epk1(&file, &output_path)?;
}
//epk2 with unencrypted header
else if formats::epk2::is_epk2_file(&file) {
println!("EPK2 file detected!");
formats::epk2::extract_epk2(&file, &output_path)?;
else if formats::epk2b::is_epk2b_file(&file) {
println!("EPK2B file detected!");
formats::epk2b::extract_epk2b(&file, &output_path)?;
}
//epk with encrypted header - it can be epk2 or epk3 so we need to check
else if formats::epk::is_epk_file(&file) {
println!("EPK file detected!");
formats::epk::extract_epk(&file, &output_path)?;
}
//epk2 with unencrypted header
else if formats::epk2::is_epk2_file(&file) {
println!("EPK2 file detected!");
formats::epk2::extract_epk2(&file, &output_path)?;
}
else if formats::ruf::is_ruf_file(&file) {
println!("RUF file detected!");
formats::ruf::extract_ruf(&file, &output_path)?;