mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-16 05:04:22 +02:00
Overhaul epk(epk1,epk2,epk3), add epk2b type extractor
This commit is contained in:
+58
-42
@@ -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!");
|
||||
|
||||
Reference in New Issue
Block a user