+ cleanup

This commit is contained in:
Pari Malam
2026-06-05 21:02:40 +08:00
parent 612ba74bc8
commit c1f5b7961d
65 changed files with 670 additions and 641 deletions
+6 -6
View File
@@ -52,11 +52,11 @@ pub mod gx_dvb;
pub mod onkyo;
pub mod philips_bdp;
pub mod tsb_bin;
pub mod pup;
pub mod vestel;
pub mod novatek_raw;
pub mod pup;
pub mod vestel;
pub mod novatek_raw;
pub mod msd;
pub mod msd;
pub mod msd10;
pub mod msd11;
@@ -77,7 +77,7 @@ pub mod mtk_bdp;
/// means that container formats (which embed other formats) must appear
/// before their inner formats to avoid misdetection.
pub fn get_registry() -> Vec<Format> {
return vec![
vec![
Format {
name: "mstar",
detector_func: crate::formats::mstar::is_mstar_file,
@@ -289,4 +289,4 @@ pub fn get_registry() -> Vec<Format> {
extractor_func: crate::formats::novatek_raw::extract_novatek_raw,
},
]
}
}
+7 -6
View File
@@ -10,6 +10,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use crate::utils::sparse::unsparse_to_file;
use include::*;
use log::info;
pub fn is_amlogic_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -26,7 +27,7 @@ pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let header: ImageHeader = file.read_le()?;
println!("File info -\nImage size: {}\nItem align size: {}\nItem count: {}\nFormat version: {}",
info!("File info -\nImage size: {}\nItem align size: {}\nItem count: {}\nFormat version: {}",
header.image_size, header.item_align_size, header.item_count, header.version);
if header.version != 2 {
@@ -41,13 +42,13 @@ pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
}
for (i, item) in items.iter().enumerate() {
println!("\n({}/{}) - {}, Type: {}, Offset: {}, Size: {} {}",
info!("\n({}/{}) - {}, Type: {}, Offset: {}, Size: {} {}",
i+1, header.item_count, item.name(), item.item_type(), item.offset_in_image, item.item_size, if item.is_sparse() {"[SPARSE]"} else {""});
if item.item_type() == "VERIFY" { //verify item is SHA1 of partition item
let sum_bytes = common::read_file(&file, item.offset_in_image, item.item_size as usize)?;
let sum = common::string_from_bytes(&sum_bytes);
println!("- Checksum for {}: {}", item.name(), sum);
info!("- Checksum for {}: {}", item.name(), sum);
} else {
let data = common::read_file(&file, item.offset_in_image, item.item_size as usize)?;
@@ -57,15 +58,15 @@ pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
fs::create_dir_all(&app_ctx.output_dir)?;
if item.is_sparse() {
println!("- Unsparsing...");
info!("- Unsparsing...");
unsparse_to_file(&data, output_path)?;
println!("-- Saved file!");
info!("-- Saved file!");
continue
} else {
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
println!("- Saved file!");
info!("- Saved file!");
}
}
+7 -6
View File
@@ -1,3 +1,5 @@
use log::info;
mod include;
mod android_ota_update_metadata;
use std::any::Any;
@@ -29,7 +31,7 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box<dyn Any>) ->
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let header: Header = file.read_be()?;
println!("File info:\nFormat version: {}\nManifest size: {}", header.file_format_version, header.manifest_size);
info!("File info:\nFormat version: {}\nManifest size: {}", header.file_format_version, header.manifest_size);
if header.file_format_version != 2 {
return Err("Unsupported format version! (Only 2 is supported right now)".into());
@@ -42,7 +44,7 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box<dyn Any>) ->
for (i, partition) in manifest.partitions.into_iter().enumerate() {
let operation_count = partition.operations.len();
println!("\n#{} - {}, Size: {}, Operations: {}",
info!("\n#{} - {}, Size: {}, Operations: {}",
i + 1, partition.partition_name, partition.new_partition_info.unwrap().size.unwrap(), operation_count);
fs::create_dir_all(&app_ctx.output_dir)?;
@@ -60,9 +62,8 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box<dyn Any>) ->
//because the amount of operations can reach up to the thousands, i think its best to update the current line
//to not clog up the terminal and so you know what the program is actually doing
print!("\r- ({}/{}) - {}({}), Offset: {}, Size: {}",
info!("- ({}/{}) - {}({}), Offset: {}, Size: {}",
i + 1, operation_count, operation_name_str, operation.r#type, offset, size);
std::io::stdout().flush()?;
let data = common::read_file(&file, offset, size as usize)?;
@@ -76,13 +77,13 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Box<dyn Any>) ->
else if operation.r#type == 8 { //REPLACE_XZ - decompress with xz and write
out_data = decompress_xz(&data)?;
} else {
println!("-- Unsupported operation!");
info!("-- Unsupported operation!");
break
}
out_file.write_all(&out_data)?;
}
println!("\n-- Saved!");
info!("\n-- Saved!");
}
Ok(())
+5 -4
View File
@@ -9,6 +9,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use include::*;
use log::info;
pub fn is_bdl_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -26,7 +27,7 @@ pub fn extract_bdl(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
let header: BdlHeader = file.read_le()?;
println!("File info:\nPackage count: {}\nDate: {}\nManufacturer: {}\nModel: {}\nVersion: {}\nInfo: {}",
info!("File info:\nPackage count: {}\nDate: {}\nManufacturer: {}\nModel: {}\nVersion: {}\nInfo: {}",
header.pkg_count, header.date(), header.manufacturer(), header.model(), header.version(), header.info());
let mut pkgs: Vec<PkgListEntry> = Vec::new();
@@ -39,7 +40,7 @@ pub fn extract_bdl(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
for (i, pkg) in pkgs.iter().enumerate() {
file.seek(SeekFrom::Start(pkg.offset))?;
let pkg_header: PkgHeader = file.read_le()?;
println!("\nPackage ({}/{}) - Name: {}, Version: {}, Entry Count: {}, Manufacturer: {}, Offset: {}, Size: {}",
info!("\nPackage ({}/{}) - Name: {}, Version: {}, Entry Count: {}, Manufacturer: {}, Offset: {}, Size: {}",
i + 1, header.pkg_count, pkg_header.name(), pkg_header.version(), pkg_header.entry_count, pkg_header.manufacturer(), pkg.offset, pkg.size);
let mut pkg_entries: Vec<PkgEntry> = Vec::new();
@@ -53,7 +54,7 @@ pub fn extract_bdl(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
fs::create_dir_all(&pkg_folder)?;
for (i, pkg_entry) in pkg_entries.iter().enumerate() {
println!("- Entry {}/{} - Name: {}, Offset: {}, Size: {}",
info!("- Entry {}/{} - Name: {}, Offset: {}, Size: {}",
i + 1, pkg_header.entry_count, pkg_entry.name(), pkg_entry.offset, pkg_entry.size);
let calc_offset = pkg.offset + pkg_entry.offset;
@@ -64,7 +65,7 @@ pub fn extract_bdl(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
out_file.write_all(&data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
}
+7 -6
View File
@@ -9,6 +9,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use include::*;
use log::info;
pub fn is_cd5_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -32,7 +33,7 @@ pub fn extract_cd5(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
let dwld_hdr: DownloadHeader = dwld_hdr_reader.read_be()?;
// like Loader Data screen
println!("File info -\nManufacturer code: {}\nHardware Version: {}\nVersion(DSN): {}(0x{:02x})\nVariant/Sub-variant: 0x{:02x}/0x{:02x}\nModule count: {}",
info!("File info -\nManufacturer code: {}\nHardware Version: {}\nVersion(DSN): {}(0x{:02x})\nVariant/Sub-variant: 0x{:02x}/0x{:02x}\nModule count: {}",
dwld_hdr.manufacturer_code, dwld_hdr.hardware_version, dwld_hdr.version, dwld_hdr.version, dwld_hdr.variant, dwld_hdr.sub_variant, dwld_hdr.module_count);
for (i, module) in dwld_hdr.module_entries.iter().enumerate() {
@@ -46,7 +47,7 @@ pub fn extract_cd5(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
return Err("Module id mismatch in download header and module header!".into());
}
println!("\n({}/{}) Module {}(0x{:02x}) - Version(DSN): {}(0x{:02x}), Size: {}, Segment size: {}, Segment count: {} {}",
info!("\n({}/{}) Module {}(0x{:02x}) - Version(DSN): {}(0x{:02x}), Size: {}, Segment size: {}, Segment count: {} {}",
i+1, dwld_hdr.module_count, mod_hdr.module_id, mod_hdr.module_id, module.version, module.version, mod_hdr.out_size, mod_hdr.segment_size, mod_hdr.segment_count,
if mod_hdr.is_encrypted() {"[ENCRYPTED]"} else {""});
@@ -61,19 +62,19 @@ pub fn extract_cd5(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
return Err("Module id mismatch in segment and module header!".into());
}
println!(" Segment {}/{} - Size: {}", s_i+1, mod_hdr.segment_count, segment.data_size);
info!(" Segment {}/{} - Size: {}", s_i+1, mod_hdr.segment_count, segment.data_size);
module_data.append(&mut segment.data);
}
let out_data;
if mod_hdr.is_encrypted() {
println!("- Warning: data is encrypted, so cannot read inner header - saving ENCRYPTED data!");
info!("- Warning: data is encrypted, so cannot read inner header - saving ENCRYPTED data!");
out_data = module_data;
}
else {
let mut mod_data_rdr = Cursor::new(module_data);
let inner_mod_hdr: InnerModuleHeader = mod_data_rdr.read_be()?;
println!("- Inner header size: {}, Data size: {}", inner_mod_hdr.header_size, inner_mod_hdr.data_size);
info!("- Inner header size: {}, Data size: {}", inner_mod_hdr.header_size, inner_mod_hdr.data_size);
mod_data_rdr.seek(SeekFrom::Start(inner_mod_hdr.header_size as u64))?;
out_data = common::read_exact(&mut mod_data_rdr, inner_mod_hdr.data_size as usize)?;
}
@@ -83,7 +84,7 @@ pub fn extract_cd5(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
+5 -4
View File
@@ -5,6 +5,7 @@ use crate::AppContext;
use crate::utils::common;
use crate::utils::aes::decrypt_aes_ecb_auto;
use crate::formats;
use log::info;
pub struct EpkContext {
epk_version: u8,
@@ -55,21 +56,21 @@ fn match_with_pattern(data: &[u8], pattern: &str) -> bool {
pub fn extract_epk(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<EpkContext>().expect("Missing context");
let ctx = ctx.downcast::<EpkContext>().map_err(|_| "Invalid context type")?;
let versions = common::read_file(&file, 1712, 36)?;
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);
info!("Platform version: {}\nSDK version: {}", platform_version, sdk_version);
file.seek(std::io::SeekFrom::Start(0))?;
if ctx.epk_version == 2 {
println!("EPK2 detected!\n");
info!("EPK2 detected!\n");
formats::epk2::extract_epk2(app_ctx, Box::new(()))?;
} else if ctx.epk_version == 3 {
println!("EPK3 detected!\n");
info!("EPK3 detected!\n");
formats::epk3::extract_epk3(app_ctx, Box::new(()))?;
}
+7 -6
View File
@@ -9,6 +9,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use include::*;
use log::info;
pub fn is_epk1_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -30,10 +31,10 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
let init_pak_count = u32::from_le_bytes(init_pak_count_bytes.try_into().unwrap());
if init_pak_count > 256 {
println!("\nBig endian EPK1 detected.");
info!("\nBig endian EPK1 detected.");
epk1_type = Epk1Type::BigEndian;
} else if init_pak_count < 33 {
println!("\nLittle endian EPK1 detected.");
info!("\nLittle endian EPK1 detected.");
epk1_type = Epk1Type::LittleEndian;
} else {
return Err("Unknown EPK1 variant!".into());
@@ -60,7 +61,7 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
let version = common::read_exact(&mut file, 4)?;
println!("EPK info -\nData size: {}\nPak count: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
info!("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 == Epk1Type::LittleEndian {
@@ -91,7 +92,7 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
let ota_id_bytes = common::read_exact(&mut file, 32)?;
let ota_id = common::string_from_bytes(&ota_id_bytes);
println!("EPK info -\nData size: {}\nHeader size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
info!("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]);
}
@@ -99,7 +100,7 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
file.seek(SeekFrom::Start(pak.offset as u64))?;
let pak_header: PakHeader = if epk1_type == Epk1Type::BigEndian {file.read_be()?} else {file.read_le()?};
println!("\n({}/{}) - {}, Offset: {}, Size: {}, Platform: {}",
info!("\n({}/{}) - {}, Offset: {}, Size: {}, Platform: {}",
i + 1, paks.len(), pak_header.pak_name(), pak.offset, pak_header.image_size, pak_header.platform_id());
let data = common::read_exact(&mut file, pak_header.image_size as usize)?;
@@ -109,7 +110,7 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
println!("- Saved file!");
info!("- Saved file!");
}
Ok(())
+12 -11
View File
@@ -13,6 +13,7 @@ use crate::keys;
use crate::formats::epk::find_key;
use crate::utils::aes::decrypt_aes_ecb_auto;
use include::*;
use log::info;
pub fn is_epk2_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -37,14 +38,14 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
//check if header is encrypted
let epak = &stored_header[0..4]; // epak magic
if epak == b"epak" {
println!("Header is not encrypted.");
info!("Header is not encrypted.");
header = stored_header;
} else {
println!("Header is encrypted...");
println!("\nFinding key...");
info!("Header is encrypted...");
info!("\nFinding key...");
//find the key, knowing that the header should start with "epak"
if let Some((key_name, key_bytes)) = find_key(&keys::EPK, &stored_header, b"epak")? {
println!("Found valid key: {}", key_name);
info!("Found valid key: {}", key_name);
matching_key = Some(key_bytes);
header = decrypt_aes_ecb_auto(matching_key.as_ref().unwrap(), &stored_header)?;
opt_dump_dec_hdr(app_ctx, &header, "header")?;
@@ -57,7 +58,7 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
let mut hdr_reader = Cursor::new(header);
let hdr: Header = hdr_reader.read_le()?;
println!("\nEPK info -\nData size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}.{:02x?}\n",
info!("\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();
@@ -65,7 +66,7 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
for i in 0..hdr.pak_count {
let pak: PakEntry = hdr_reader.read_le()?;
//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);
info!("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() });
}
@@ -82,10 +83,10 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
//the file's header was not encrypted so we dont have the key yet
if matching_key.is_none() {
println!("\nFinding key...");
info!("\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::EPK, &encrypted_header, pak.name.as_bytes())? {
println!("Found correct key: {}", key_name);
info!("Found correct key: {}", key_name);
matching_key = Some(key_bytes);
} else {
return Err("No valid key found!".into());
@@ -96,7 +97,7 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
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()?;
println!("\n({}/{}) - {}, Size: {}, Segment count: {}, Platform: {}",
info!("\n({}/{}) - {}, Size: {}, Segment count: {}, Platform: {}",
pak_n + 1, paks.len(), pak.name, pak_header.image_size, pak_header.segment_count, pak_header.platform_id());
let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", pak.name));
@@ -137,14 +138,14 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
pak_header.segment_size
};
println!("- Segment {}/{} - Size: {}", i + 1, pak_header.segment_count, actual_segment_size);
info!("- 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)?;
out_file.write_all(&out_data)?;
println!("-- Saved to file!");
info!("-- Saved to file!");
}
}
+5 -4
View File
@@ -9,6 +9,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use include::*;
use log::info;
pub fn is_epk2b_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -26,7 +27,7 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let header: EpkHeader = file.read_le()?;
println!("EPK info -\nData size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
info!("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();
@@ -49,7 +50,7 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let mut all_segment_size = 0;
println!("\n({}/{}) - {}, Size: {}, Segment count: {}, Platform: {}",
info!("\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());
let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", pak_header.pak_name()));
@@ -66,7 +67,7 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
return Err(format!("Unexpected segment index in pak header!, expected: {}, got: {}", i , pak_header.segment_index).into());
}
println!("- Segment {}/{} - Size: {}", i + 1, pak_header.segment_count, pak_header.segment_size);
info!("- 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;
@@ -80,7 +81,7 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
out_file.write_all(&out_data[..segment_limit as usize])?;
println!("-- Saved to file!");
info!("-- Saved to file!");
}
}
+11 -10
View File
@@ -13,6 +13,7 @@ use crate::keys;
use crate::formats::epk::find_key;
use crate::utils::aes::decrypt_aes_ecb_auto;
use include::*;
use log::info;
pub fn is_epk3_file(_app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
Ok(None)
@@ -27,11 +28,11 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
let mut new_type = false;
let matching_key: Option<Vec<u8>>;
println!("Finding key...");
info!("Finding key...");
// find the key, knowing that the header should start with "EPK3" (old type 128 byte signature)
if let Some((key_name, key_bytes)) = find_key(&keys::EPK, &stored_header[128..], b"EPK3")? {
println!("Found valid key: {}", key_name);
info!("Found valid key: {}", key_name);
matching_key = Some(key_bytes);
_header_signature = &stored_header[..128];
header = decrypt_aes_ecb_auto(matching_key.as_ref().unwrap(), &stored_header[128..])?;
@@ -39,7 +40,7 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
//try for new format epk3 (new type 256 byte signature)
} else if let Some((key_name, key_bytes)) = find_key(&keys::EPK, &stored_header[256..], b"EPK3")? {
println!("Found valid key: {}", key_name);
info!("Found valid key: {}", key_name);
matching_key = Some(key_bytes);
_header_signature = &stored_header[..256];
header = decrypt_aes_ecb_auto(matching_key.as_ref().unwrap(), &stored_header[256..])?;
@@ -59,16 +60,16 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
let mut hdr_reader = Cursor::new(header);
let hdr: Header = hdr_reader.read_le()?;
println!("\nEPK info -\nEPK3 type: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}.{:02x?}\nPackage Info size: {}",
info!("\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: {}",
info!("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!();
info!("");
let _platform_versions = common::read_exact(&mut file, 36)?;
let _pkg_info_signature = common::read_exact(&mut file, signature_size)?;
@@ -81,7 +82,7 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
let mut pkg_info_reader = Cursor::new(pkg_info);
let pkg_info_hdr: PkgInfoHeader = pkg_info_reader.read_le()?;
println!("Package info list size: {}\nPackage info count: {}",
info!("Package info list size: {}\nPackage info count: {}",
pkg_info_hdr.package_info_list_size, pkg_info_hdr.package_info_count);
if new_type {let _unknown = common::read_exact(&mut pkg_info_reader, 4)?;}; //new type has additional value
@@ -90,7 +91,7 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
while (pkg_info_reader.position() as usize) < pkg_info_reader.get_ref().len() {
let mut entry: PkgInfoEntry = pkg_info_reader.read_le()?;
println!("\n({}) - {}, Size: {}, Segments: {}",
info!("\n({}) - {}, Size: {}, Segments: {}",
pak_i, entry.package_name(), entry.package_size, entry.segment_count);
let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", entry.package_name()));
@@ -102,7 +103,7 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
entry = pkg_info_reader.read_le()?;
}
println!("- Segment {}/{}, Size: {}", entry.segment_index + 1, entry.segment_count, entry.segment_size);
info!("- Segment {}/{}, Size: {}", entry.segment_index + 1, entry.segment_count, entry.segment_size);
let _segment_signature = common::read_exact(&mut file, signature_size)?;
@@ -111,7 +112,7 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
out_file.write_all(&out_data[extra_segment_size..])?;
println!("-- Saved to file!");
info!("-- Saved to file!");
}
pak_i += 1;
}
+1 -1
View File
@@ -40,7 +40,7 @@ pub fn is_cmpr(data: &[u8], size: u32) -> bool {
if count > size || data_size > size {return false};
return (size - 0x20 - (count*12) - 12) == data_size;
(size - 0x20 - (count*12) - 12) == data_size
}
//image rom
+12 -11
View File
@@ -12,6 +12,7 @@ use crate::formats::funai_upg::funai_des::funai_des_decrypt;
use include::*;
use crate::keys;
use crate::utils::compression::decompress_zlib;
use log::info;
pub struct FunaiBdpContext {
key: u32,
@@ -36,12 +37,12 @@ pub fn is_funai_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, B
pub fn extract_funai_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<FunaiBdpContext>().expect("Missing context");
let ctx = ctx.downcast::<FunaiBdpContext>().map_err(|_| "Invalid context type")?;
let mut data = Vec::new(); //to decrypt entire file
file.read_to_end(&mut data)?;
println!("Decrypting file...");
info!("Decrypting file...");
data = funai_des_decrypt(&data, ctx.key);
let mut file_reader = Cursor::new(data);
@@ -56,17 +57,17 @@ pub fn extract_funai_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(),
}
for (i, entry) in entries.iter().enumerate() {
println!("\n({}/{}) - {}, Offset: {}, Size: {}", i +1, index_entry_count, entry.name(), entry.offset, entry.size);
info!("\n({}/{}) - {}, Offset: {}, Size: {}", i +1, index_entry_count, entry.name(), entry.offset, entry.size);
file_reader.seek(SeekFrom::Start(entry.offset as u64))?;
let mut data = common::read_exact(&mut file_reader, entry.size as usize)?;
if is_cmpr(&data, entry.size) {
println!("- cmpr detected!, 'uncompressing' data...");
info!("- cmpr detected!, 'uncompressing' data...");
data = uncmpr_data(&data)?;
} else if entry.name().ends_with("_image_rom") {
println!("- Decompressing image ROM...");
info!("- Decompressing image ROM...");
data = uncomp_image_rom(&data)?;
} else {//strip partition name at start
@@ -80,7 +81,7 @@ pub fn extract_funai_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(),
out_file.write_all(&data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
Ok(())
@@ -90,13 +91,13 @@ pub fn uncmpr_data(data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut data_reader = Cursor::new(data);
let _part_name = read_exact(&mut data_reader, 0x20)?;
let cmpr_header: CmprHeader = data_reader.read_le()?;
println!("[cmpr] out chk: {:02x}, count: {}, data size: {}",
info!("[cmpr] out chk: {:02x}, count: {}, data size: {}",
cmpr_header.out_checksum, cmpr_header.count, cmpr_header.data_size);
let mut out_data: Vec<u8> = Vec::new();
for (i, entry) in cmpr_header.entries.iter().enumerate() {
println!("[cmpr] ({}/{}) size: {}, mode: {}, fill: {:02x}",
info!("[cmpr] ({}/{}) size: {}, mode: {}, fill: {:02x}",
i+1, cmpr_header.count, entry.size, entry.mode, entry.fill);
let mut data;
@@ -120,20 +121,20 @@ pub fn uncomp_image_rom(data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Erro
let mut data_reader = Cursor::new(data);
let _part_name = read_exact(&mut data_reader, 0x20)?;
let header: ImageRomHeader = data_reader.read_le()?;
println!("[rom] count: {}", header.count);
info!("[rom] count: {}", header.count);
let mut out_data: Vec<u8> = Vec::new();
for (i, entry) in header.entries.iter().enumerate() {
let offset = header.start_offset + entry.offset;
println!("[rom] ({}/{}) offset: {}, size: {}",
info!("[rom] ({}/{}) offset: {}, size: {}",
i+1, header.count, offset, entry.size);
data_reader.seek(SeekFrom::Start(offset as u64))?;
let compr_data = read_exact(&mut data_reader, entry.size as usize)?;
println!("[rom] - Decompressing...");
info!("[rom] - Decompressing...");
let mut decomp_data = decompress_zlib(&compr_data)?;
out_data.append(&mut decomp_data);
+8 -6
View File
@@ -9,6 +9,7 @@ use std::io::{Write, Seek, SeekFrom};
use crate::utils::common;
use crate::formats::mstar_secure_old::{is_mstar_secure_old_file, extract_mstar_secure_old};
use include::*;
use log::info;
struct FunaiMstarCtx {
data_offset: u64,
@@ -38,10 +39,10 @@ pub fn is_funai_mstar_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>,
pub fn extract_funai_mstar(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<FunaiMstarCtx>().expect("Missing context");
let ctx = ctx.downcast::<FunaiMstarCtx>().map_err(|_| "Invalid context type")?;
let info = InfoStruct::from_str(ctx.info_str).unwrap();
println!("File info -\nFile code: {}\nBrand name: {}\nModel name: {}\nSoC Version: {}\nFRC Version: {}",
info!("File info -\nFile code: {}\nBrand name: {}\nModel name: {}\nSoC Version: {}\nFRC Version: {}",
info.file_code, info.brand_name, info.model_name, info.soc_version, info.frc_version);
let payloads: Vec<(&str, usize)> = vec![("SoC", info.soc_size), ("FRC60", info.frc60_size), ("FRC120", info.frc120_size)];
@@ -53,7 +54,7 @@ pub fn extract_funai_mstar(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
if size == 0 {
continue
}
println!("\n#{} - {}, Size: {}", p_i+1, name, size);
info!("\n#{} - {}, Size: {}", p_i+1, name, size);
let data = common::read_exact(&mut file, size)?;
@@ -62,7 +63,7 @@ pub fn extract_funai_mstar(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?;
out_file.write_all(&data)?;
println!("- Saved file!");
info!("- Saved file!");
//extract SoC which (should be) mstar_secure_old, this is just a simple container for that format ( so we will go funai_mstar -> mstar_secure_old -> mstar (DUMB?) )
if name == "SoC" {
@@ -73,11 +74,12 @@ pub fn extract_funai_mstar(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
options: app_ctx.options.clone(),
dry_run: app_ctx.dry_run,
quiet: app_ctx.quiet,
verbose: app_ctx.verbose,
};
//do check and extarct
//do check and extract
if let Some(result) = is_mstar_secure_old_file(&in_ctx)? {
println!("- Extracting mstar_secure_old...");
info!("- Extracting mstar_secure_old...");
extract_mstar_secure_old(&in_ctx, result)?;
};
+7 -6
View File
@@ -12,6 +12,7 @@ use crate::utils::common;
use include::*;
use crate::keys;
use funai_des::funai_des_decrypt;
use log::info;
pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -30,7 +31,7 @@ pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
let header: Header = file.read_le()?;
let mut key: Option<u32> = None;
println!("File info:\nFile size: {}\nEntry count: {}", header.file_size, header.entry_count);
info!("File info:\nFile size: {}\nEntry count: {}", header.file_size, header.entry_count);
for i in 0..header.entry_count {
let entry: Entry = file.read_le()?;
@@ -47,7 +48,7 @@ pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
let decrypted = funai_des_decrypt(&data, key_u32);
if is_valid_ver_string(&decrypted) {
println!("Matched key: {}\nFirmware info: {}",
info!("Matched key: {}\nFirmware info: {}",
key_hex, common::string_from_bytes(&decrypted));
key = Some(key_u32);
break
@@ -55,14 +56,14 @@ pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
}
}
println!("\n({}/{}) - Type: {}, Size: {}", i + 1, header.entry_count, entry.entry_type, entry.entry_size);
info!("\n({}/{}) - Type: {}, Size: {}", i + 1, header.entry_count, entry.entry_type, entry.entry_size);
if entry.encryption_flag == 1 {
if let Some(key_u32) = key {
println!("- Decrypting...");
info!("- Decrypting...");
data = funai_des_decrypt(&data, key_u32);
} else {
println!("- Warning! Failed to find decryption key, saving encrypted data")
info!("- Warning! Failed to find decryption key, saving encrypted data")
}
}
@@ -72,7 +73,7 @@ pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
Ok(())
+6 -5
View File
@@ -12,6 +12,7 @@ use crate::keys;
use crate::formats::funai_upg::funai_des::funai_des_decrypt;
use crate::formats::funai_upg::include::is_valid_ver_string;
use include::*;
use log::info;
pub fn is_funai_upg_phl_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -46,7 +47,7 @@ pub fn extract_funai_upg_phl(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result
let decrypted = funai_des_decrypt(&data, key_u32);
if is_valid_ver_string(&decrypted[..16]) {
println!("Matched key: {}\nFirmware info: {}\nFirmware date: {}",
info!("Matched key: {}\nFirmware info: {}\nFirmware date: {}",
key_hex, common::string_from_bytes(&decrypted[..16]), common::string_from_bytes(&decrypted[16..]));
key = Some(key_u32);
break
@@ -54,14 +55,14 @@ pub fn extract_funai_upg_phl(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result
}
}
println!("\n#{} - Type: {}, Size: {}", i + 1, entry.body_type, entry.size);
info!("\n#{} - Type: {}, Size: {}", i + 1, entry.body_type, entry.size);
if let Some(key_u32) = key {
println!("- Decrypting...");
info!("- Decrypting...");
data = funai_des_decrypt(&data, key_u32);
} else {
println!("- Warning! Failed to find decryption key, saving encrypted data")
info!("- Warning! Failed to find decryption key, saving encrypted data")
}
let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", entry.body_type));
@@ -70,7 +71,7 @@ pub fn extract_funai_upg_phl(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
+4 -3
View File
@@ -8,6 +8,7 @@ use crate::AppContext;
use crate::utils::common;
use binrw::BinReaderExt;
use include::*;
use log::info;
pub fn is_gx_dvb_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -26,10 +27,10 @@ pub fn extract_gx_dvb(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Bo
file.seek(SeekFrom::Start(TABLE_OFFSET))?;
let table: PartTable = file.read_be()?;
println!("Part count: {}", table.part_count);
info!("Part count: {}", table.part_count);
for (i, part) in table.part_entries.iter().enumerate() {
println!("\n({}/{}) - {}, Offset: {}, Total size: {}, Used size: {}",
info!("\n({}/{}) - {}, Offset: {}, Total size: {}, Used size: {}",
i+1, table.part_count, part.name(), part.start, part.total_size, part.used_size);
let data = common::read_file(&file, part.start as u64, part.total_size as usize)?;
@@ -39,7 +40,7 @@ pub fn extract_gx_dvb(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Bo
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
println!("- Saved file!");
info!("- Saved file!");
}
Ok(())
+6 -5
View File
@@ -10,6 +10,7 @@ use binrw::BinReaderExt;
use crate::utils::aes::{decrypt_aes128_cbc_pcks7};
use crate::utils::common;
use include::*;
use log::info;
pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -26,14 +27,14 @@ pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Res
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let header: Header = file.read_le()?;
println!("File info -\nKey ID: {}\nVersion(1): {}\nVersion(2): {}\nVersion(3): {}\nVersion(4): {}\nData size: {}\nChunk count: {}\nChunk size: {}\n\nPayload Count: {}",
info!("File info -\nKey ID: {}\nVersion(1): {}\nVersion(2): {}\nVersion(3): {}\nVersion(4): {}\nData size: {}\nChunk count: {}\nChunk size: {}\n\nPayload Count: {}",
header.file_infos[0], header.ver1(), header.ver2(), header.ver3(), header.ver4(), header.data_size, header.chunk_count, header.chunk_size, header.payload_count);
let mut entries: Vec<Entry> = Vec::new();
for i in 0..header.payload_count {
let entry: Entry = file.read_le()?;
println!("{}. {} - Start offset: {}, Size: {}",
info!("{}. {} - Start offset: {}, Size: {}",
i + 1, entry.name(), entry.start_offset, entry.size);
entries.push(entry);
}
@@ -65,13 +66,13 @@ pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Res
}
}
println!("\nDecrypting data...");
info!("\nDecrypting data...");
let decrypted_data = decrypt_aes128_cbc_pcks7(&encrypted_data, &aes_key, &aes_iv)?;
let mut data_reader = Cursor::new(decrypted_data);
for (i , entry) in entries.iter().enumerate() {
println!("\n({}/{}) - {}, Size: {}, Start offset: {}", i+1, header.payload_count, entry.name(), entry.size, entry.start_offset);
info!("\n({}/{}) - {}, Size: {}, Start offset: {}", i+1, header.payload_count, entry.name(), entry.size, entry.start_offset);
let data = common::read_exact(&mut data_reader, entry.size as usize)?;
@@ -82,7 +83,7 @@ pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Res
out_file.seek(SeekFrom::Start(entry.start_offset.into()))?;
out_file.write_all(&data)?;
println!("- Saved file!");
info!("- Saved file!");
}
Ok(())
+44 -42
View File
@@ -3,6 +3,8 @@
use std::io::{Seek, SeekFrom, Cursor};
use binrw::{BinRead, BinReaderExt};
use log::info;
use crate::utils::common;
#[derive(BinRead)]
@@ -100,44 +102,44 @@ pub fn parse_ouith_blob(blob: &[u8], print_tree: bool) -> Result<(Vec<MSDItem>,
while reader.stream_position()? < blob.len() as u64 {
chunk_n += 1;
let chunk: ChunkHeader = reader.read_be()?;
if print_tree { println!("\nChunk {} - Size: {}, Value: {}", chunk_n, chunk.size, chunk.value); };
if print_tree { info!("\nChunk {} - Size: {}, Value: {}", chunk_n, chunk.size, chunk.value); };
let chunk_end = reader.stream_position()? + chunk.size as u64;
//parse top level descriptor. it can only be ID 1(OUUpgradeItemDesc) or 2(OUGroupDesc)
let top_descriptor: DescriptorHeader = reader.read_be()?;
if top_descriptor.tag == 0x01 {
if print_tree { println!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); };
if print_tree { info!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); };
let item_id: u32 = reader.read_be()?;
if print_tree { println!(" Item ID: {}", item_id); };
if print_tree { info!(" Item ID: {}", item_id); };
//REQUIRED are items in order: OUDestinationDesc(0x03), OUDataProcessingDesc(0x07), OUGroupInfoDesc(0x13). OPTIONAL items: OUDependenciesDesc(0x04), OUDataPostProcessingDesc(0x08)
//In MSD files, no others seem to be used than the required ones. We will ignore all data after required descriptors.
let destination_descriptor: DescriptorHeader = reader.read_be()?;
if destination_descriptor.tag != 0x03 {return Err(format!("Unexpected descriptor type in OUUpgradeItemDesc, Expected: 0x03, Got: 0x{:02x}!", destination_descriptor.tag).into())}
if print_tree { println!(" OUDestinationDesc(0x03) - Size: {}", destination_descriptor.size); };
if print_tree { info!(" OUDestinationDesc(0x03) - Size: {}", destination_descriptor.size); };
let out_size: u32 = reader.read_be()?;
if print_tree { println!(" Out data size: {}", out_size); };
if print_tree { info!(" Out data size: {}", out_size); };
//OUDestinationDesc needs one of OUSWFileVersionDesc(0x0B), OUPartitionVersionDesc(0x0A), OUCMACDataDesc(0x11). Their structure is the same. so we can store the type
let type_descriptor: DescriptorHeader = reader.read_be()?;
if ![0x0B, 0xA, 0x11].contains(&type_descriptor.tag) {return Err(format!("Unexpected descriptor type in OUDestinationDesc, Expected: one of 0x0B, 0x0A, 0x11, Got: 0x{:02x}!", type_descriptor.tag).into())}
if print_tree { println!(" Type descriptor(0x{:02x}) - Size: {}", type_descriptor.tag, type_descriptor.size); };
if print_tree { info!(" Type descriptor(0x{:02x}) - Size: {}", type_descriptor.tag, type_descriptor.size); };
let destination_info: CommonDestinationInfo = reader.read_be()?;
if print_tree {
println!(" Name length: {}", destination_info.name_len);
println!(" Name: {}", destination_info.name());
println!(" Version: {}", destination_info.version);
info!(" Name length: {}", destination_info.name_len);
info!(" Name: {}", destination_info.name());
info!(" Version: {}", destination_info.version);
};
//OUDataProcessingDesc can have OUXOREncryptionDesc(0x0D), OUAESEncryptionDesc(0x0E), OUCompressionDesc(0x0F), OUSecureHashValidationDesc(0x18), OURSAValidationDesc(0x10), OUDataCopyDesc(0x16), OUKeepCurrentDataDesc(0x1E), OUCRC32ValidationDesc(0x12)
let data_processing_descriptor: DescriptorHeader = reader.read_be()?;
if data_processing_descriptor.tag != 0x07 {return Err(format!("Unexpected descriptor type in OUUpgradeItemDesc, Expected: 0x07, Got: 0x{:02x}!", data_processing_descriptor.tag).into())}
if print_tree { println!(" OUDataProcessingDesc(0x07) - Size: {}", data_processing_descriptor.size); };
if print_tree { info!(" OUDataProcessingDesc(0x07) - Size: {}", data_processing_descriptor.size); };
let heading_size: u32 = reader.read_be()?;
if print_tree { println!(" Heading size: {}", heading_size); };
if print_tree { info!(" Heading size: {}", heading_size); };
let data_size: u32 = reader.read_be()?;
if print_tree { println!(" Data size: {}", data_size); };
if print_tree { info!(" Data size: {}", data_size); };
let mut aes_encryption = false;
//let mut crc32_hash: Option<u32> = None;
@@ -149,46 +151,46 @@ pub fn parse_ouith_blob(blob: &[u8], print_tree: bool) -> Result<(Vec<MSDItem>,
if ![0x0D, 0x0E, 0x0F, 0x18, 0x10, 0x16, 0x1E, 0x12].contains(&descriptor.tag) {return Err(format!("Unexpected descriptor type in OUDataProcessingDesc, Expected: one of 0x0D, 0x0E, 0x0F, 0x18, 0x10, 0x16, 0x1E, 0x12, Got: 0x{:02x}!", descriptor.tag).into())}
if descriptor.tag == 0x0E {
//OUAESEncryptionDesc
if print_tree { println!(" OUAESEncryptionDesc(0x0E) - Size: {}", descriptor.size); };
if print_tree { info!(" OUAESEncryptionDesc(0x0E) - Size: {}", descriptor.size); };
let aes_encryption_desc: OUAESEncryptionDesc = reader.read_be()?;
if print_tree {
println!(" Mode: {}", aes_encryption_desc.mode);
println!(" Key size: {}", aes_encryption_desc.key_size);
println!(" Salt size: {}", aes_encryption_desc.salt_size);
info!(" Mode: {}", aes_encryption_desc.mode);
info!(" Key size: {}", aes_encryption_desc.key_size);
info!(" Salt size: {}", aes_encryption_desc.salt_size);
};
aes_encryption = true;
}
else if descriptor.tag == 0x10 {
//OURSAValidationDesc
if print_tree { println!(" OURSAValidationDesc(0x10) - Size: {}", descriptor.size); };
if print_tree { info!(" OURSAValidationDesc(0x10) - Size: {}", descriptor.size); };
let rsa_validation_desc: OURSAValidationDesc = reader.read_be()?;
if print_tree {
println!(" Mode: {}", rsa_validation_desc.mode);
println!(" Field 2: {}", rsa_validation_desc._unknown);
println!(" Signature size: {}", rsa_validation_desc.signature_size);
info!(" Mode: {}", rsa_validation_desc.mode);
info!(" Field 2: {}", rsa_validation_desc._unknown);
info!(" Signature size: {}", rsa_validation_desc.signature_size);
};
}
else if descriptor.tag == 0x12 {
//OUCRC32ValidationDesc
if print_tree { println!(" OUCRC32ValidationDesc(0x12) - Size: {}", descriptor.size); };
if print_tree { info!(" OUCRC32ValidationDesc(0x12) - Size: {}", descriptor.size); };
let crc32: u32 = reader.read_be()?;
if print_tree { println!(" CRC32: {:02x}", crc32); };
if print_tree { info!(" CRC32: {:02x}", crc32); };
//crc32_hash = Some(crc32);
}
else if descriptor.tag == 0x18 {
//OUSecureHashValidationDesc
if print_tree { println!(" OUSecureHashValidationDesc(0x18) - Size: {}", descriptor.size); };
if print_tree { info!(" OUSecureHashValidationDesc(0x18) - Size: {}", descriptor.size); };
let secure_hash_validation_desc: OUSecureHashValidationDesc = reader.read_be()?;
if print_tree {
println!(" Mode: {}", secure_hash_validation_desc.mode);
println!(" Hash size: {}", secure_hash_validation_desc.hash_size);
println!(" Hash: {}", hex::encode(&secure_hash_validation_desc.hash));
info!(" Mode: {}", secure_hash_validation_desc.mode);
info!(" Hash size: {}", secure_hash_validation_desc.hash_size);
info!(" Hash: {}", hex::encode(&secure_hash_validation_desc.hash));
};
//secure_hash = Some(secure_hash_validation_desc.hash);
}
else {
//type not implemented, ignore the data
if print_tree { println!(" Unimplemented descriptor(0x{:02x}) - Size: {}", descriptor.tag, descriptor.size); };
if print_tree { info!(" Unimplemented descriptor(0x{:02x}) - Size: {}", descriptor.tag, descriptor.size); };
let _descriptor_data = common::read_exact(&mut reader, descriptor.size as usize);
}
}
@@ -196,9 +198,9 @@ pub fn parse_ouith_blob(blob: &[u8], print_tree: bool) -> Result<(Vec<MSDItem>,
//OUGroupInfoDesc
let group_info_descriptor: DescriptorHeader = reader.read_be()?;
if group_info_descriptor.tag != 0x13 {return Err(format!("Unexpected descriptor type in OUUpgradeItemDesc, Expected: 0x13, Got: 0x{:02x}!", group_info_descriptor.tag).into())}
if print_tree { println!(" OUGroupInfoDesc(0x13) - Size: {}", group_info_descriptor.size); };
if print_tree { info!(" OUGroupInfoDesc(0x13) - Size: {}", group_info_descriptor.size); };
let group_id: u32 = reader.read_be()?;
if print_tree { println!(" Group ID: {}", group_id); };
if print_tree { info!(" Group ID: {}", group_id); };
//create the msd item with all infos
let msd_item = MSDItem {
@@ -220,12 +222,12 @@ pub fn parse_ouith_blob(blob: &[u8], print_tree: bool) -> Result<(Vec<MSDItem>,
}
else if top_descriptor.tag == 0x02 {
if print_tree { println!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); };
if print_tree { info!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); };
let group_desc: OUGroupDesc = reader.read_be()?;
if print_tree {
println!(" Group ID: {}", group_desc.group_id);
println!(" Field 2: {}", group_desc.field_2);
println!(" Field 3: {}", group_desc.field_3);
info!(" Group ID: {}", group_desc.group_id);
info!(" Field 2: {}", group_desc.field_2);
info!(" Field 3: {}", group_desc.field_3);
};
//OUGroupDesc REQUIRES one of: OUSWImageVersionDesc(0x09), OUSWImageVersionExDesc(0x19), OUOptionalDataVersionDesc(0x14), OUFirmwareVersionDesc(0x15). OPTIONALLY it can also have OUDependenciesDesc
@@ -233,23 +235,23 @@ pub fn parse_ouith_blob(blob: &[u8], print_tree: bool) -> Result<(Vec<MSDItem>,
let version_descriptor: DescriptorHeader = reader.read_be()?;
if ![0x09, 0x19, 0x14, 0x15].contains(&version_descriptor.tag) {return Err(format!("Unexpected descriptor type in OUGroupDesc, Expected: one of 0x09, 0x19, 0x14, 0x15, Got: 0x{:02x}!", version_descriptor.tag).into())}
if version_descriptor.tag == 0x19 {
if print_tree { println!(" OUSWImageVersionExDesc(0x12) - Size: {}", version_descriptor.size); };
if print_tree { info!(" OUSWImageVersionExDesc(0x12) - Size: {}", version_descriptor.size); };
let sw_image_version_ex_desc: OUSWImageVersionExDesc = reader.read_be()?;
if print_tree {
println!(" Name length: {}", sw_image_version_ex_desc.name_len);
println!(" Name: {}", sw_image_version_ex_desc.name());
println!(" Major version: {}", sw_image_version_ex_desc.major_ver);
println!(" Minor version: {}", sw_image_version_ex_desc.minor_ver);
println!(" Date year: {}", sw_image_version_ex_desc.date_year);
println!(" Date month: {}", sw_image_version_ex_desc.date_month);
println!(" Date day: {}", sw_image_version_ex_desc.date_day);
info!(" Name length: {}", sw_image_version_ex_desc.name_len);
info!(" Name: {}", sw_image_version_ex_desc.name());
info!(" Major version: {}", sw_image_version_ex_desc.major_ver);
info!(" Minor version: {}", sw_image_version_ex_desc.minor_ver);
info!(" Date year: {}", sw_image_version_ex_desc.date_year);
info!(" Date month: {}", sw_image_version_ex_desc.date_month);
info!(" Date day: {}", sw_image_version_ex_desc.date_day);
};
info = Some(sw_image_version_ex_desc);
}
else {
//type not implemented, ignore the data
if print_tree { println!(" Unimplemented descriptor(0x{:02x}) - Size: {}", version_descriptor.tag, version_descriptor.size); };
if print_tree { info!(" Unimplemented descriptor(0x{:02x}) - Size: {}", version_descriptor.tag, version_descriptor.size); };
let _descriptor_data = common::read_exact(&mut reader, version_descriptor.size as usize);
}
+44 -42
View File
@@ -3,6 +3,8 @@
use std::io::{Cursor};
use binrw::{BinRead, BinReaderExt};
use log::info;
use crate::utils::common;
#[derive(BinRead)]
@@ -116,24 +118,24 @@ pub fn parse_blob_1_8(blob: &[u8], print_tree: bool) -> Result<(Vec<MSDItem>, Op
}
let top_level_descriptor_count: u32 = reader.read_be()?; //BIG ENDIAN
if print_tree { println!("\nTop level descriptor count: {}", top_level_descriptor_count); };
if print_tree { info!("\nTop level descriptor count: {}", top_level_descriptor_count); };
for _i in 0..top_level_descriptor_count {
//parse top level descriptor. it can only be ID 1(OUUpgradeItemDesc) or 2(OUGroupDesc)
let top_descriptor: DescriptorHeader = reader.read_be()?;
if top_descriptor.tag == 0x01 {
if print_tree { println!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); };
if print_tree { info!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); };
let upgrade_item_desc: OUUpgradeItemDesc = reader.read_be()?;
if print_tree {
println!(" Item ID: {}", upgrade_item_desc.item_id);
println!(" Unknown flag: {}", upgrade_item_desc.unk_flag);
println!(" Original size: {}", upgrade_item_desc.original_size);
println!(" Processed size: {}", upgrade_item_desc.processed_size);
println!(" Unknown: {}", upgrade_item_desc.unk);
info!(" Item ID: {}", upgrade_item_desc.item_id);
info!(" Unknown flag: {}", upgrade_item_desc.unk_flag);
info!(" Original size: {}", upgrade_item_desc.original_size);
info!(" Processed size: {}", upgrade_item_desc.processed_size);
info!(" Unknown: {}", upgrade_item_desc.unk);
};
let subdesc_count: u32 = reader.read_le()?; //LITTLE ENDIAN??
if print_tree { println!(" Subdescriptor count: {}", subdesc_count); };
if print_tree { info!(" Subdescriptor count: {}", subdesc_count); };
let mut name: Option<String> = None;
//let mut crc32_hash: Option<u32> = None;
@@ -143,66 +145,66 @@ pub fn parse_blob_1_8(blob: &[u8], print_tree: bool) -> Result<(Vec<MSDItem>, Op
for _i in 0..subdesc_count {
let sub_descriptor: DescriptorHeader = reader.read_be()?;
if sub_descriptor.tag == 0x0A {
if print_tree { println!(" OUPartitionVersionDesc(0x0A) - Size: {}", sub_descriptor.size); };
if print_tree { info!(" OUPartitionVersionDesc(0x0A) - Size: {}", sub_descriptor.size); };
let partition_version_desc: OUPartitionVersionDesc = reader.read_be()?;
if print_tree {
println!(" Name length: {}", partition_version_desc.name_len);
println!(" Name: {}", partition_version_desc.name());
println!(" Version: {}", partition_version_desc.version);
info!(" Name length: {}", partition_version_desc.name_len);
info!(" Name: {}", partition_version_desc.name());
info!(" Version: {}", partition_version_desc.version);
};
name = Some(partition_version_desc.name());
}
else if sub_descriptor.tag == 0x07 {
if print_tree { println!(" OUDataProcessingDesc(0x07) - Size: {}", sub_descriptor.size); };
if print_tree { info!(" OUDataProcessingDesc(0x07) - Size: {}", sub_descriptor.size); };
let data_processing_desc: OUDataProcessingDesc = reader.read_le()?; //LITTLE ENDIAN??
if print_tree { println!(" Subdescriptor count: {}", data_processing_desc.subdesc_count); };
if print_tree { info!(" Subdescriptor count: {}", data_processing_desc.subdesc_count); };
for _i in 0..data_processing_desc.subdesc_count {
let data_processing_sub_desc: DescriptorHeader = reader.read_be()?;
if data_processing_sub_desc.tag == 0x12 {
if print_tree { println!(" OUCRC32ValidationDesc(0x12) - Size: {}", data_processing_sub_desc.size); };
if print_tree { info!(" OUCRC32ValidationDesc(0x12) - Size: {}", data_processing_sub_desc.size); };
let crc32_validation_desc: OUCRC32ValidationDesc = reader.read_be()?;
if print_tree { println!(" CRC32: {:02x}", crc32_validation_desc.crc32); };
if print_tree { info!(" CRC32: {:02x}", crc32_validation_desc.crc32); };
//crc32_hash = Some(crc32_validation_desc.crc32);
}
else if data_processing_sub_desc.tag == 0x10 {
if print_tree { println!(" OURSAValidationDesc(0x10) - Size: {}", data_processing_sub_desc.size); };
if print_tree { info!(" OURSAValidationDesc(0x10) - Size: {}", data_processing_sub_desc.size); };
let rsa_validation_desc: OURSAValidationDesc = reader.read_be()?;
if print_tree {
println!(" Signature size: {}", rsa_validation_desc.signature_size);
println!(" Public key ID: {}", rsa_validation_desc.public_key_id);
println!(" Signature: {}", hex::encode(&rsa_validation_desc.signature));
info!(" Signature size: {}", rsa_validation_desc.signature_size);
info!(" Public key ID: {}", rsa_validation_desc.public_key_id);
info!(" Signature: {}", hex::encode(&rsa_validation_desc.signature));
};
}
else if data_processing_sub_desc.tag == 0x0E {
if print_tree { println!(" OUAESEncryptionDesc(0x0E) - Size: {}", data_processing_sub_desc.size); };
if print_tree { info!(" OUAESEncryptionDesc(0x0E) - Size: {}", data_processing_sub_desc.size); };
let aes_encryption_desc: OUAESEncryptionDesc = reader.read_be()?;
if print_tree {
println!(" Private key ID: {}", aes_encryption_desc.private_key_id);
println!(" Salt size: {}", aes_encryption_desc.salt_size);
println!(" Salt: {}", hex::encode(&aes_encryption_desc.salt));
println!(" Processed size: {}", aes_encryption_desc.processed_size);
info!(" Private key ID: {}", aes_encryption_desc.private_key_id);
info!(" Salt size: {}", aes_encryption_desc.salt_size);
info!(" Salt: {}", hex::encode(&aes_encryption_desc.salt));
info!(" Processed size: {}", aes_encryption_desc.processed_size);
};
aes_encryption = true;
aes_salt = Some(aes_encryption_desc.salt);
}
else {
if print_tree { println!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", data_processing_sub_desc.tag, data_processing_sub_desc.size); };
if print_tree { info!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", data_processing_sub_desc.tag, data_processing_sub_desc.size); };
let _ = common::read_exact(&mut reader, data_processing_sub_desc.size as usize - 4)?;
}
}
}
else if sub_descriptor.tag == 0x13 {
if print_tree { println!(" OUGroupInfoDesc(0x13) - Size: {}", sub_descriptor.size); };
if print_tree { info!(" OUGroupInfoDesc(0x13) - Size: {}", sub_descriptor.size); };
let group_info_desc: OUGroupInfoDesc = reader.read_be()?;
if print_tree { println!(" Group ID: {}", group_info_desc.group_id); };
if print_tree { info!(" Group ID: {}", group_info_desc.group_id); };
}
else {
if print_tree { println!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); };
if print_tree { info!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); };
let _ = common::read_exact(&mut reader, sub_descriptor.size as usize - 4)?;
}
}
@@ -224,34 +226,34 @@ pub fn parse_blob_1_8(blob: &[u8], print_tree: bool) -> Result<(Vec<MSDItem>, Op
}
}
else if top_descriptor.tag == 0x02 {
if print_tree { println!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); };
if print_tree { info!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); };
let group_desc: OUGroupDesc = reader.read_be()?;
if print_tree {
println!(" Group ID: {}", group_desc.group_id);
println!(" Unknown: {}", group_desc.unknown);
info!(" Group ID: {}", group_desc.group_id);
info!(" Unknown: {}", group_desc.unknown);
};
let subdesc_count: u32 = reader.read_le()?; //LITTLE ENDIAN??
if print_tree { println!(" Subdescriptor count: {}", subdesc_count); };
if print_tree { info!(" Subdescriptor count: {}", subdesc_count); };
for _i in 0..subdesc_count {
let sub_descriptor: DescriptorHeader = reader.read_be()?;
if sub_descriptor.tag == 0x19 {
if print_tree { println!(" OUSWImageVersionDesc(0x19) - Size: {}", sub_descriptor.size); };
if print_tree { info!(" OUSWImageVersionDesc(0x19) - Size: {}", sub_descriptor.size); };
let sw_image_version_desc: OUSWImageVersionDesc = reader.read_be()?;
if print_tree {
println!(" Name length: {}", sw_image_version_desc.name_len);
println!(" Name: {}", sw_image_version_desc.name());
println!(" Major ver: {}", sw_image_version_desc.major_ver);
println!(" Minor ver: {}", sw_image_version_desc.minor_ver);
println!(" Year: {}", sw_image_version_desc.date_year);
println!(" Month: {}", sw_image_version_desc.date_month);
println!(" Day: {}", sw_image_version_desc.date_day);
info!(" Name length: {}", sw_image_version_desc.name_len);
info!(" Name: {}", sw_image_version_desc.name());
info!(" Major ver: {}", sw_image_version_desc.major_ver);
info!(" Minor ver: {}", sw_image_version_desc.minor_ver);
info!(" Year: {}", sw_image_version_desc.date_year);
info!(" Month: {}", sw_image_version_desc.date_month);
info!(" Day: {}", sw_image_version_desc.date_day);
};
info = Some(sw_image_version_desc);
} else {
if print_tree { println!(" Unimplemented Descriptor (0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); };
if print_tree { info!(" Unimplemented Descriptor (0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); };
let _ = common::read_exact(&mut reader, sub_descriptor.size as usize - 4)?;
}
}
+40 -38
View File
@@ -3,6 +3,8 @@
use std::io::{Cursor};
use binrw::{BinRead, BinReaderExt};
use log::info;
use crate::utils::common;
#[derive(BinRead)]
@@ -121,24 +123,24 @@ pub fn parse_blob_1_9(blob: &[u8], print_tree: bool) -> Result<(Vec<MSDItem>, Op
}
let top_level_descriptor_count: u32 = reader.read_le()?;
if print_tree { println!("\nTop level descriptor count: {}", top_level_descriptor_count); };
if print_tree { info!("\nTop level descriptor count: {}", top_level_descriptor_count); };
for _i in 0..top_level_descriptor_count {
//parse top level descriptor. it can only be ID 1(OUUpgradeItemDesc) or 2(OUGroupDesc) or 0x37(OUSecureDowngradeDesc)
let top_descriptor: DescriptorHeader = reader.read_le()?;
if top_descriptor.tag == 0x01 {
if print_tree { println!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); };
if print_tree { info!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); };
let upgrade_item_desc: OUUpgradeItemDesc = reader.read_le()?;
if print_tree {
println!(" Item ID: {}", upgrade_item_desc.item_id);
println!(" Unknown flag: {}", upgrade_item_desc.unk_flag);
println!(" Original size: {}", upgrade_item_desc.original_size);
println!(" Processed size: {}", upgrade_item_desc.processed_size);
println!(" Unknown: {}", upgrade_item_desc.unk);
info!(" Item ID: {}", upgrade_item_desc.item_id);
info!(" Unknown flag: {}", upgrade_item_desc.unk_flag);
info!(" Original size: {}", upgrade_item_desc.original_size);
info!(" Processed size: {}", upgrade_item_desc.processed_size);
info!(" Unknown: {}", upgrade_item_desc.unk);
};
let subdesc_count: u32 = reader.read_le()?;
if print_tree { println!(" Subdescriptor count: {}", subdesc_count); };
if print_tree { info!(" Subdescriptor count: {}", subdesc_count); };
let mut name: Option<String> = None;
//let mut crc32_hash: Option<u32> = None;
@@ -148,64 +150,64 @@ pub fn parse_blob_1_9(blob: &[u8], print_tree: bool) -> Result<(Vec<MSDItem>, Op
for _i in 0..subdesc_count {
let sub_descriptor: DescriptorHeader = reader.read_le()?;
if sub_descriptor.tag == 0x0A {
if print_tree { println!(" OUPartitionVersionDesc(0x0A) - Size: {}", sub_descriptor.size); };
if print_tree { info!(" OUPartitionVersionDesc(0x0A) - Size: {}", sub_descriptor.size); };
let partition_version_desc: OUPartitionVersionDesc = reader.read_le()?;
if print_tree {
println!(" Name length: {}", partition_version_desc.name_len);
println!(" Name: {}", partition_version_desc.name());
info!(" Name length: {}", partition_version_desc.name_len);
info!(" Name: {}", partition_version_desc.name());
};
name = Some(partition_version_desc.name());
}
else if sub_descriptor.tag == 0x07 {
if print_tree { println!(" OUDataProcessingDesc(0x07) - Size: {}", sub_descriptor.size); };
if print_tree { info!(" OUDataProcessingDesc(0x07) - Size: {}", sub_descriptor.size); };
let data_processing_desc: OUDataProcessingDesc = reader.read_le()?;
if print_tree { println!(" Subdescriptor count: {}", data_processing_desc.subdesc_count); };
if print_tree { info!(" Subdescriptor count: {}", data_processing_desc.subdesc_count); };
for _i in 0..data_processing_desc.subdesc_count {
let data_processing_sub_desc: DescriptorHeader = reader.read_le()?;
if data_processing_sub_desc.tag == 0x12 {
if print_tree { println!(" OUCRC32ValidationDesc(0x12) - Size: {}", data_processing_sub_desc.size); };
if print_tree { info!(" OUCRC32ValidationDesc(0x12) - Size: {}", data_processing_sub_desc.size); };
let crc32_validation_desc: OUCRC32ValidationDesc = reader.read_le()?;
if print_tree { println!(" CRC32: {:02x}", crc32_validation_desc.crc32); };
if print_tree { info!(" CRC32: {:02x}", crc32_validation_desc.crc32); };
//crc32_hash = Some(crc32_validation_desc.crc32);
}
else if data_processing_sub_desc.tag == 0x10 {
if print_tree { println!(" OURSAValidationDesc(0x10) - Size: {}", data_processing_sub_desc.size); };
if print_tree { info!(" OURSAValidationDesc(0x10) - Size: {}", data_processing_sub_desc.size); };
let rsa_validation_desc: OURSAValidationDesc = reader.read_le()?;
if print_tree {
println!(" Signature size: {}", rsa_validation_desc.signature_size);
println!(" Public key ID: {}", rsa_validation_desc.public_key_id);
println!(" Signature: {}", hex::encode(&rsa_validation_desc.signature));
info!(" Signature size: {}", rsa_validation_desc.signature_size);
info!(" Public key ID: {}", rsa_validation_desc.public_key_id);
info!(" Signature: {}", hex::encode(&rsa_validation_desc.signature));
};
}
else if data_processing_sub_desc.tag == 0x0E {
if print_tree { println!(" OUAESEncryptionDesc(0x0E) - Size: {}", data_processing_sub_desc.size); };
if print_tree { info!(" OUAESEncryptionDesc(0x0E) - Size: {}", data_processing_sub_desc.size); };
let aes_encryption_desc: OUAESEncryptionDesc = reader.read_le()?;
if print_tree {
println!(" Salt size: {}", aes_encryption_desc.salt_size);
println!(" Salt: {}", hex::encode(&aes_encryption_desc.salt));
println!(" Processed size: {}", aes_encryption_desc.processed_size);
info!(" Salt size: {}", aes_encryption_desc.salt_size);
info!(" Salt: {}", hex::encode(&aes_encryption_desc.salt));
info!(" Processed size: {}", aes_encryption_desc.processed_size);
};
aes_encryption = true;
aes_salt = Some(aes_encryption_desc.salt);
}
else {
if print_tree { println!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", data_processing_sub_desc.tag, data_processing_sub_desc.size); };
if print_tree { info!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", data_processing_sub_desc.tag, data_processing_sub_desc.size); };
let _ = common::read_exact(&mut reader, data_processing_sub_desc.size as usize - 4)?;
}
}
}
else if sub_descriptor.tag == 0x13 {
if print_tree { println!(" OUGroupInfoDesc(0x13) - Size: {}", sub_descriptor.size); };
if print_tree { info!(" OUGroupInfoDesc(0x13) - Size: {}", sub_descriptor.size); };
let group_info_desc: OUGroupInfoDesc = reader.read_le()?;
if print_tree { println!(" Group ID: {}", group_info_desc.group_id); };
if print_tree { info!(" Group ID: {}", group_info_desc.group_id); };
}
else {
if print_tree { println!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); };
if print_tree { info!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); };
let _ = common::read_exact(&mut reader, sub_descriptor.size as usize - 4)?;
}
}
@@ -227,36 +229,36 @@ pub fn parse_blob_1_9(blob: &[u8], print_tree: bool) -> Result<(Vec<MSDItem>, Op
}
}
else if top_descriptor.tag == 0x02 {
if print_tree { println!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); };
if print_tree { info!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); };
let group_desc: OUGroupDesc = reader.read_le()?;
if print_tree { println!(" Group ID: {}", group_desc.group_id); };
if print_tree { info!(" Group ID: {}", group_desc.group_id); };
let subdesc_count: u32 = reader.read_le()?; //LITTLE ENDIAN??
if print_tree { println!(" Subdescriptor count: {}", subdesc_count); };
if print_tree { info!(" Subdescriptor count: {}", subdesc_count); };
for _i in 0..subdesc_count {
let sub_descriptor: DescriptorHeader = reader.read_le()?;
if sub_descriptor.tag == 0x19 {
if print_tree { println!(" OUSWImageVersionDesc(0x19) - Size: {}", sub_descriptor.size); };
if print_tree { info!(" OUSWImageVersionDesc(0x19) - Size: {}", sub_descriptor.size); };
let sw_image_version_desc: OUSWImageVersionDesc = reader.read_le()?;
if print_tree {
println!(" Name length: {}", sw_image_version_desc.name_len);
println!(" Name: {}", sw_image_version_desc.name());
println!(" Major ver: {}", sw_image_version_desc.major_ver);
println!(" Minor ver: {}", sw_image_version_desc.minor_ver);
info!(" Name length: {}", sw_image_version_desc.name_len);
info!(" Name: {}", sw_image_version_desc.name());
info!(" Major ver: {}", sw_image_version_desc.major_ver);
info!(" Minor ver: {}", sw_image_version_desc.minor_ver);
};
info = Some(sw_image_version_desc);
} else {
if print_tree { println!(" Unimplemented Descriptor (0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); };
if print_tree { info!(" Unimplemented Descriptor (0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); };
let _ = common::read_exact(&mut reader, sub_descriptor.size as usize - 4)?;
}
}
}
else if top_descriptor.tag == 0x37 {
if print_tree { println!("OUSecureDowngradeDesc(0x37) - Size: {}", top_descriptor.size); };
if print_tree { info!("OUSecureDowngradeDesc(0x37) - Size: {}", top_descriptor.size); };
let secure_downgrade_desc: OUSecureDowngradeDesc = reader.read_le()?;
if print_tree { println!(" Image generation timestamp: {}", secure_downgrade_desc.image_generation_date); };
if print_tree { info!(" Image generation timestamp: {}", secure_downgrade_desc.image_generation_date); };
}
else {
return Err(format!("Unexpected top level descriptor type 0x{:02x}!", top_descriptor.tag).into());
+16 -15
View File
@@ -14,6 +14,7 @@ use crate::formats::msd::{decrypt_aes_salted_old, decrypt_aes_salted_tizen, decr
use crate::formats::msd::msd_ouith_parser_old::{parse_ouith_blob};
use crate::formats::msd::msd_ouith_parser_tizen_1_8::{parse_blob_1_8};
use include::*;
use log::info;
pub fn is_msd10_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -30,29 +31,29 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let header: FileHeader = file.read_le()?;
println!("\nNumber of sections: {}", header.section_count);
info!("\nNumber of sections: {}", header.section_count);
let mut sections: Vec<SectionEntry> = Vec::new();
for _i in 0..header.section_count {
let section: SectionEntry = file.read_le()?;
println!("Section {}: offset: {}, size: {}", section.index, section.offset, section.size);
info!("Section {}: offset: {}, size: {}", section.index, section.offset, section.size);
sections.push(section);
}
let _zero_padding = common::read_exact(&mut file, 4)?;
let header_count: u32 = file.read_le()?;
println!("\nNumber of headers: {}", header_count);
info!("\nNumber of headers: {}", header_count);
let mut headers: Vec<HeaderEntry> = Vec::new();
for i in 0..header_count {
let header: HeaderEntry = file.read_le()?;
println!("Header {}: {}, offset: {}, size: {}", i + 1, header.name(), header.offset, header.size);
info!("Header {}: {}, offset: {}, size: {}", i + 1, header.name(), header.offset, header.size);
headers.push(header);
}
//use first header
let firmware_name = &headers[0].name();
println!("\nFirmware name: {}", firmware_name);
info!("\nFirmware name: {}", firmware_name);
let toc_offset = headers[0].offset;
let toc_size = headers[0].size;
@@ -89,7 +90,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
}
let (passphrase_bytes, firmware_type) = if let (Some(p), Some(t)) = (passphrase_bytes, firmware_type) {
println!("Using passphrase: {}", passphrase_name);
info!("Using passphrase: {}", passphrase_name);
(p, t)
} else {
return Err("No matching key found!".into());
@@ -103,7 +104,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let (items, info) = parse_blob_1_8(&toc, app_ctx.has_option("msd:print_ouith"))?;
if let Some(info) = info {
println!("\nImage info:\n{} {}.{} {}/{}/{}",
info!("\nImage info:\n{} {}.{} {}/{}/{}",
info.name(), info.major_ver, info.minor_ver, info.date_day, info.date_month, info.date_year);
}
@@ -111,7 +112,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let size = sections[i as usize].size;
let offset = sections[i as usize].offset;
println!("\n({}/{}) - {}, Size: {}",
info!("\n({}/{}) - {}, Size: {}",
i + 1, items.len(), item.name, size);
if sections[i as usize].index != item.item_id {
@@ -122,7 +123,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let out_data;
if item.aes_encryption {
println!("- Decrypting...");
info!("- Decrypting...");
let salt = item.aes_salt.as_ref().ok_or("AES salt missing!")?;
out_data = decrypt_aes_tizen(&stored_data, &passphrase_bytes, salt)?;
} else {
@@ -134,7 +135,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
@@ -145,14 +146,14 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let (items, info) = parse_ouith_blob(&toc, app_ctx.has_option("msd:print_ouith"))?;
if let Some(info) = info {
println!("\nImage info:\n{} {}.{} {}/{}/20{}",
info!("\nImage info:\n{} {}.{} {}/{}/20{}",
info.name(), info.major_ver, info.minor_ver, info.date_day, info.date_month, info.date_year);
}
for (i, item) in items.iter().enumerate() {
let offset = sections[i as usize].offset;
let type_str = if item.item_type == 0x0A {"Partition"} else if item.item_type == 0x0B {"File"} else if item.item_type == 0x11 {"CMAC Data"} else {"Unknown"};
println!("\n({}/{}) - {}, Type: {}, Size: {}",
info!("\n({}/{}) - {}, Type: {}, Size: {}",
item.item_id, items.len(), item.name, type_str, item.all_size);
if sections[i as usize].index != item.item_id {
@@ -164,7 +165,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
if app_ctx.has_option("msd10:save_cmac") {
out_filename = format!("{}.cmac", item.name); //add an additional extension, because the CMAC data has the same name as its item
} else {
println!("- Skipping CMAC Data...");
info!("- Skipping CMAC Data...");
continue
}
}
@@ -177,7 +178,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let stored_data = common::read_exact(&mut file, item.data_size as usize)?;
let out_data;
if item.aes_encryption {
println!("- Decrypting...");
info!("- Decrypting...");
out_data = decrypt_aes_salted_old(&stored_data, &passphrase_bytes)?;
} else {
out_data = stored_data;
@@ -188,7 +189,7 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
}
+11 -10
View File
@@ -13,6 +13,7 @@ use crate::keys;
use crate::formats::msd::{decrypt_aes_salted_tizen, decrypt_aes_tizen};
use crate::formats::msd::msd_ouith_parser_tizen_1_9::{parse_blob_1_9};
use include::*;
use log::info;
pub fn is_msd11_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -29,28 +30,28 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let header: FileHeader = file.read_le()?;
println!("\nNumber of sections: {}", header.section_count);
info!("\nNumber of sections: {}", header.section_count);
let mut sections: Vec<SectionEntry> = Vec::new();
for _i in 0..header.section_count {
let section: SectionEntry = file.read_le()?;
println!("Section {}: offset: {}, size: {}", section.index, section.offset, section.size);
info!("Section {}: offset: {}, size: {}", section.index, section.offset, section.size);
sections.push(section);
}
let header_count: u32 = file.read_le()?;
println!("\nNumber of headers: {}", header_count);
info!("\nNumber of headers: {}", header_count);
let mut headers: Vec<HeaderEntry> = Vec::new();
for i in 0..header_count {
let header: HeaderEntry = file.read_le()?;
println!("Header {}: {}, offset: {}, size: {}", i + 1, header.name(), header.offset, header.size);
info!("Header {}: {}, offset: {}, size: {}", i + 1, header.name(), header.offset, header.size);
headers.push(header);
}
//use first header
let firmware_name = &headers[0].name();
println!("\nFirmware name: {}", firmware_name);
info!("\nFirmware name: {}", firmware_name);
let toc_offset = headers[0].offset + 8;
let toc_size = headers[0].size - 8;
@@ -72,7 +73,7 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
}
let passphrase_bytes = if let Some(p) = passphrase_bytes {
println!("Using passphrase: {}", passphrase_name);
info!("Using passphrase: {}", passphrase_name);
p
} else {
return Err("No matching key found!".into());
@@ -84,7 +85,7 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let (items, info) = parse_blob_1_9(&toc, app_ctx.has_option("msd:print_ouith"))?;
if let Some(info) = info {
println!("\nImage info:\n{} {}.{}",
info!("\nImage info:\n{} {}.{}",
info.name(), info.major_ver, info.minor_ver);
}
@@ -92,7 +93,7 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let size = sections[i as usize].size;
let offset = sections[i as usize].offset;
println!("\n({}/{}) - {}, Size: {}",
info!("\n({}/{}) - {}, Size: {}",
i + 1, items.len(), item.name, size);
if sections[i as usize].index != item.item_id {
@@ -103,7 +104,7 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let out_data;
if item.aes_encryption {
println!("- Decrypting...");
info!("- Decrypting...");
let salt = item.aes_salt.as_ref().ok_or("AES salt missing!")?;
out_data = decrypt_aes_tizen(&stored_data, &passphrase_bytes, salt)?;
} else {
@@ -115,7 +116,7 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
Ok(())
+19 -24
View File
@@ -12,6 +12,7 @@ use crate::utils::compression::{decompress_lzma, decompress_lz4};
use crate::utils::lzop::{unlzop_to_file};
use crate::utils::sparse::{unsparse_to_file};
use include::*;
use log::info;
pub fn is_mstar_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -35,9 +36,9 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
}
let mut script_string = String::from_utf8_lossy(&script);
if script_string == "" {
if script_string.is_empty() {
//try for hisense
println!("Failed to get script at 0x0, trying 0x1000...");
info!("Failed to get script at 0x0, trying 0x1000...");
script = common::read_file(&file, 4096, 32768)?;
if let Some(pos) = script.iter().position(|x| [0x00, 0xFF].contains(x)) {
@@ -46,16 +47,15 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
script_string = String::from_utf8_lossy(&script);
if script_string == "" {
if script_string.is_empty() {
return Err("Failed to get script".into());
}
}
opt_dump_dec_hdr(app_ctx, &script, "script")?;
let lines: Vec<&str> = script_string.lines().map(|l| l.trim()).collect();
let mut i = 0;
for line in &lines {
for (i, line) in lines.iter().enumerate() {
if line.starts_with("filepartload") {
let parts: Vec<&str> = line.split_whitespace().collect();
let offset = parse_number(parts[3]).unwrap_or(0);
@@ -113,7 +113,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
}
// try to get partname from nand/mmc/ubi writes
if lines[j].starts_with("mmc write") | lines[j].starts_with("nand write") | lines[j].starts_with("ubi write"){
if lines[j].starts_with("mmc write") || lines[j].starts_with("nand write") || lines[j].starts_with("ubi write"){
let parts: Vec<&str> = lines[j].split_whitespace().collect();
if partname == "unknown" {
partname = parts[3]
@@ -123,17 +123,16 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
j += 1;
}
println!("\nPart - Offset: {}, Size: {} --> {}", offset, size, partname);
info!("\nPart - Offset: {}, Size: {} --> {}", offset, size, partname);
let data = common::read_file(&file, offset, size.try_into().unwrap())?;
let data = common::read_file(&file, offset, size.try_into().map_err(|_| "Size conversion failed")?)?;
let out_data;
let output_path = if partname == "unknown" {
if app_ctx.has_option("mstar:keep_unknown") {
println!("- Warning, unknown destination - saving to _unknown_{}.bin", offset);
info!("- Warning, unknown destination - saving to _unknown_{}.bin", offset);
Path::new(&app_ctx.output_dir).join(format!("_unknown_{}.bin", offset))
} else {
println!("- Warning, unknown destination - skipping...");
i += 1;
info!("- Warning, unknown destination - skipping...");
continue;
}
} else {
@@ -141,27 +140,25 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
};
if compression == CompressionType::Lzma {
println!("- Decompressing LZMA...");
info!("- Decompressing LZMA...");
out_data = decompress_lzma(&data)?;
} else if compression == CompressionType::DoubleLzma {
println!("- Decompressing LZMA (Pass 1)...");
info!("- Decompressing LZMA (Pass 1)...");
let pass_1 = decompress_lzma(&data)?;
println!("- Decompressing LZMA (Pass 2)...");
info!("- Decompressing LZMA (Pass 2)...");
out_data = decompress_lzma(&pass_1)?;
} else if compression == CompressionType::Lz4 {
println!("- Decompressing lz4, expected size: {}", lz4_expect_size);
info!("- Decompressing lz4, expected size: {}", lz4_expect_size);
out_data = decompress_lz4(&data, lz4_expect_size.try_into().unwrap())?;
} else if compression == CompressionType::Lzo {
println!("- Decompressing LZO..");
info!("- Decompressing LZO..");
unlzop_to_file(&data, output_path)?;
println!("-- Saved file!");
i += 1;
info!("-- Saved file!");
continue
} else if compression == CompressionType::Sparse {
println!("- Unsparsing...");
info!("- Unsparsing...");
unsparse_to_file(&data, output_path)?;
println!("-- Saved file!");
i += 1;
info!("-- Saved file!");
continue
} else {
out_data = data;
@@ -170,10 +167,8 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
i += 1;
}
Ok(())
+5 -3
View File
@@ -11,6 +11,7 @@ use crate::utils::common;
use crate::utils::aes::decrypt_aes128_ecb;
use crate::formats::mstar::{extract_mstar, is_mstar_file};
use include::*;
use log::info;
pub struct MstarSecureCtx {
dec_footer: Vec<u8>,
@@ -34,14 +35,14 @@ pub fn is_mstar_secure_old_file(app_ctx: &AppContext) -> Result<Option<Box<dyn A
pub fn extract_mstar_secure_old(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<MstarSecureCtx>().expect("Missing context");
let ctx = ctx.downcast::<MstarSecureCtx>().map_err(|_| "Invalid context type")?;
let hdr: ChunkFileFooter = Cursor::new(ctx.dec_footer).read_le()?;
println!("Info -\nSegment size: {}\nFile data offset: {}\nFile data len: {}", hdr.segment_size, hdr.file_data_offset, hdr.file_data_len);
info!("Info -\nSegment size: {}\nFile data offset: {}\nFile data len: {}", hdr.segment_size, hdr.file_data_offset, hdr.file_data_len);
let enc_data = common::read_file(&mut file, hdr.file_data_offset as u64, hdr.file_data_len as usize)?;
println!("- Decrypting...");
info!("- Decrypting...");
let dec_data = decrypt_aes128_ecb(&MSTAR_DEFAULT_UPGRADE_KEY, &enc_data)?;
let output_path = Path::new(&app_ctx.output_dir).join("_decrypted.bin");
@@ -57,6 +58,7 @@ pub fn extract_mstar_secure_old(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Resu
options: app_ctx.options.clone(),
dry_run: app_ctx.dry_run,
quiet: app_ctx.quiet,
verbose: app_ctx.verbose,
};
//do check just in case and extract
+2 -2
View File
@@ -10,7 +10,7 @@ pub static PITIT_END_MARKER: u32 = 0x69_54_49_50; //PITi - end marker of PITIT
#[derive(BinRead)]
pub struct PITITPITEntry {
pub nand_size: u32,
pub nand_size: u32,
pub pit_offset: u32,
pub pit_size: u32,
_table_id: u32,
@@ -18,7 +18,7 @@ pub struct PITITPITEntry {
#[derive(BinRead)]
pub struct PITITBITEntry {
pub bit_offset: u32,
pub bit_offset: u32,
pub bit_size: u32,
_private_data_1: u32,
_private_data_2: u32,
+12 -11
View File
@@ -9,6 +9,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use include::*;
use log::info;
pub struct MtkBdpContext {
pitit_offset: u64,
@@ -34,10 +35,10 @@ pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box
pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<MtkBdpContext>().expect("Missing context");
let ctx = ctx.downcast::<MtkBdpContext>().map_err(|_| "Invalid context type")?;
let offset = ctx.pitit_offset;
println!("\nReading PITIT at: {}", offset);
info!("\nReading PITIT at: {}", offset);
file.seek(SeekFrom::Start(offset + 8))?;
@@ -53,11 +54,11 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
if pitit_ver == 1 {
//old PITIT does not have BIT entry, because BIT appears directly after PITIT
let pitit_bit_entry: PITITBITEntry = file.read_le()?;
println!("PITIT Entry - NAND Size: {}, PIT Offset: {}, PIT Size: {}, BIT Offset: {}, BIT Size: {}",
info!("PITIT Entry - NAND Size: {}, PIT Offset: {}, PIT Size: {}, BIT Offset: {}, BIT Size: {}",
pitit_pit_entry.nand_size, pitit_pit_entry.pit_offset, pitit_pit_entry.pit_size, pitit_bit_entry.bit_offset, pitit_bit_entry.bit_size);
if bit_offset == 0 { bit_offset = pitit_bit_entry.bit_offset as u64 } //use the first entry in PITIT
} else {
println!("PITIT Entry - NAND Size: {}, PIT Offset: {}, PIT Size: {}",
info!("PITIT Entry - NAND Size: {}, PIT Offset: {}, PIT Size: {}",
pitit_pit_entry.nand_size, pitit_pit_entry.pit_offset, pitit_pit_entry.pit_size);
}
if pit_offset == 0 { pit_offset = pitit_pit_entry.pit_offset as u64 } //use the first entry in PITIT
@@ -67,24 +68,24 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
bit_offset = file.stream_position()?;
}
println!("\nReading PIT at: {}", pit_offset); //PIT is the NAND partition table.
info!("\nReading PIT at: {}", pit_offset); //PIT is the NAND partition table.
file.seek(SeekFrom::Start(pit_offset))?;
let mut pit_entries: Vec<PITEntry> = Vec::new();
let pit_header: PITHeader = file.read_le()?;
if pit_header.pit_magic != PIT_MAGIC {
return Err("Invalid PIT magic!".into());
}
println!("PIT Info - First entry offs: {}, Entry size: {}, Entry count: {}", pit_header.first_entry_offset, pit_header.entry_size, pit_header.entry_count);
info!("PIT Info - First entry offs: {}, Entry size: {}, Entry count: {}", pit_header.first_entry_offset, pit_header.entry_size, pit_header.entry_count);
file.seek(SeekFrom::Start(pit_offset + pit_header.first_entry_offset as u64))?;
for i in 0..pit_header.entry_count {
let pit_entry: PITEntry = file.read_le()?;
println!("{}. ID: {:02x}, Name: {}, NAND Offset: {}, NAND Size: {}",
info!("{}. ID: {:02x}, Name: {}, NAND Offset: {}, NAND Size: {}",
i + 1, pit_entry.partition_id, pit_entry.name(), pit_entry.offset_on_nand, pit_entry.size_on_nand);
pit_entries.push(pit_entry);
}
println!("\nReading BIT at: {}", bit_offset); //BIT is the table of objects present in the update file.
info!("\nReading BIT at: {}", bit_offset); //BIT is the table of objects present in the update file.
file.seek(SeekFrom::Start(bit_offset))?;
let mut bit_entries: Vec<BITEntry> = Vec::new();
let bit_magic = common::read_exact(&mut file, 20)?;
@@ -96,7 +97,7 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
loop {
let bit_entry: BITEntry = file.read_le()?;
if bit_entry.partition_id == BIT_END_MARKER {break};
println!("{}. ID: {:02x}, Offset: {}, Size: {}, Offset in part: {}",
info!("{}. ID: {:02x}, Offset: {}, Size: {}, Offset in part: {}",
bit_i + 1, bit_entry.partition_id, bit_entry.offset, bit_entry.size, bit_entry.offset_in_target_part);
bit_entries.push(bit_entry);
bit_i += 1;
@@ -112,7 +113,7 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
}
}
println!("\n({}/{}) - {}, Offset: {}, Size: {}, Offset in partition: {}",
info!("\n({}/{}) - {}, Offset: {}, Size: {}, Offset in partition: {}",
i + 1, bit_entries.len(), name, bit_entry.offset, bit_entry.size, bit_entry.offset_in_target_part);
let data = common::read_file(&file, bit_entry.offset as u64, bit_entry.size as usize)?;
@@ -123,7 +124,7 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
out_file.seek(SeekFrom::Start(bit_entry.offset_in_target_part as u64))?;
out_file.write_all(&data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
Ok(())
+4 -4
View File
@@ -2,8 +2,8 @@ use crate::utils::common;
use binrw::BinRead;
pub static HEADER_KEY: [u8; 16] = [
0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94,
0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94,
0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94,
0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94,
];
pub static HEADER_IV: [u8; 16] = [0x00; 16];
@@ -24,7 +24,7 @@ pub struct Header {
pub vendor_magic_bytes: [u8; 4],
_mtk_magic: [u8; 8], //#DH@FiRm
version_bytes: [u8; 60],
pub file_size: u32,
pub file_size: u32,
_flags: [u8; 4], // 3rd is possibly the compression type in new mtk pkg
product_name_bytes: [u8; 32],
}
@@ -44,7 +44,7 @@ impl Header {
#[derive(BinRead)]
pub struct PartEntry {
name_bytes: [u8; 4],
pub flags: u32,
pub flags: u32,
pub size: u32,
}
impl PartEntry {
+10 -9
View File
@@ -1,3 +1,4 @@
use log::info;
use std::fs::OpenOptions;
use std::io::{Write, Cursor, Seek, SeekFrom, Read};
use binrw::{BinRead, BinReaderExt};
@@ -39,7 +40,7 @@ pub fn decompress_mtk_to_file(data: &[u8], output_file: &PathBuf) -> Result<(),
let segment_header: LzhsHeader = data_reader.read_le()?;
let comp_header: LzhsHeader = data_reader.read_le()?;
println!("[cmp] Segment {} - Compressed size: {}, Decompressed size: {}",
info!("[cmp] Segment {} - Compressed size: {}, Decompressed size: {}",
segment_header.checksum_or_seg_idx, comp_header.compressed_size, comp_header.uncompressed_size);
let compressed_data = common::read_exact(&mut data_reader, comp_header.compressed_size as usize)?;
@@ -71,7 +72,7 @@ pub fn decompress_mtk_to_file(data: &[u8], output_file: &PathBuf) -> Result<(),
} else {
//normal variant
println!("- Decompressing {:?}...", compression_type);
info!("- Decompressing {:?}...", compression_type);
if compression_type == CompressionType::LZ4 {
out_data = decompress_lz4(&compressed_data, comp_header.uncompressed_size as i32)?;
}
@@ -84,12 +85,12 @@ pub fn decompress_mtk_to_file(data: &[u8], output_file: &PathBuf) -> Result<(),
arm_thumb_convert(&mut out_data, 0, false);
let checksum = calc_checksum(&out_data);
println!("-- Calculated checksum: 0x{:02x?}", checksum);
info!("-- Calculated checksum: 0x{:02x?}", checksum);
if u32::from(checksum) != comp_header.checksum_or_seg_idx {
println!("--- Checksum mismatch! Expected: 0x{:02x?}, Got: 0x{:02x?}!", comp_header.checksum_or_seg_idx, checksum);
info!("--- Checksum mismatch! Expected: 0x{:02x?}, Got: 0x{:02x?}!", comp_header.checksum_or_seg_idx, checksum);
return Err("LZHS checksum mismatch".into());
} else {
println!("--- Checksum OK!")
info!("--- Checksum OK!")
}
}
else {
@@ -119,7 +120,7 @@ pub fn decompress_mtk_to_file_old(data: &[u8], output_file: &PathBuf) -> Result<
let _segment_header: LzhsOldSegmentHdr = data_reader.read_le()?;
let lzhs_header: LzhsHeader = data_reader.read_le()?;
println!("[LZHS] Segment - Compressed size: {}, Decompressed size: {}, Expected Checksum: 0x{:02x?}",
info!("[LZHS] Segment - Compressed size: {}, Decompressed size: {}, Expected Checksum: 0x{:02x?}",
lzhs_header.compressed_size, lzhs_header.uncompressed_size, lzhs_header.checksum_or_seg_idx);
let compressed_data = common::read_exact(&mut data_reader, lzhs_header.compressed_size as usize)?;
@@ -129,12 +130,12 @@ pub fn decompress_mtk_to_file_old(data: &[u8], output_file: &PathBuf) -> Result<
arm_thumb_convert(&mut out_data, 0, false);
let checksum = calc_checksum(&out_data);
println!("-- Calculated checksum: 0x{:02x?}", checksum);
info!("-- Calculated checksum: 0x{:02x?}", checksum);
if u32::from(checksum) != lzhs_header.checksum_or_seg_idx {
println!("--- Checksum mismatch! Expected: 0x{:02x?}, Got: 0x{:02x?}!", lzhs_header.checksum_or_seg_idx, checksum);
info!("--- Checksum mismatch! Expected: 0x{:02x?}, Got: 0x{:02x?}!", lzhs_header.checksum_or_seg_idx, checksum);
return Err("LZHS checksum mismatch".into());
} else {
println!("--- Checksum OK!")
info!("--- Checksum OK!")
}
out_file.write_all(&out_data)?;
+12 -11
View File
@@ -15,6 +15,7 @@ use crate::utils::aes::{decrypt_aes128_cbc_nopad};
use crate::keys;
use lzhs::{decompress_mtk_to_file};
use include::*;
use log::{info, warn};
pub struct MtkPkgContext {
is_philips_variant: bool,
@@ -43,7 +44,7 @@ pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box
pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<MtkPkgContext>().expect("Missing context");
let ctx = ctx.downcast::<MtkPkgContext>().map_err(|_| "Invalid context type")?;
let file_size = file.metadata()?.len();
let header = ctx.decrypted_header;
@@ -52,7 +53,7 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
let mut hdr_reader = Cursor::new(header);
let hdr: Header = hdr_reader.read_le()?;
println!("File info:\nFile size: {}\nVendor magic: {}\nVersion info: {}\nProduct name: {}" ,
info!("File info:\nFile size: {}\nVendor magic: {}\nVersion info: {}\nProduct name: {}" ,
hdr.file_size, hdr.vendor_magic(), hdr.version(), hdr.product_name());
if ctx.is_philips_variant {
@@ -67,13 +68,13 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
let part_entry: PartEntry = file.read_le()?;
if !part_entry.is_valid() {break};
println!("\n#{} - {}, Size: {}{} {}",
info!("\n#{} - {}, Size: {}{} {}",
part_n, part_entry.name(), part_entry.size, if part_entry.is_compressed() {" [COMPRESSED]"} else {""}, if part_entry.is_encrypted() {"[ENCRYPTED]"} else {""} );
let data = common::read_exact(&mut file, part_entry.size as usize + CRYPTED_HEADER_SIZE)?;
if part_entry.size == 0 {
println!("- Empty entry, skipping!");
info!("- Empty entry, skipping!");
continue
}
@@ -91,7 +92,7 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
}
let try_decrypt = decrypt_aes128_cbc_nopad(&crypted_header, &key, &HEADER_IV)?;
if try_decrypt.starts_with(MTK_RESERVED_MAGIC) {
println!("- Decrypting with 4xVendor magic...");
info!("- Decrypting with 4xVendor magic...");
matching_key = Some(key);
matching_iv = Some(HEADER_IV);
@@ -103,7 +104,7 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
let try_decrypt = decrypt_aes128_cbc_nopad(&crypted_header, &key_array, &iv_array)?;
if try_decrypt.starts_with(MTK_RESERVED_MAGIC) {
println!("- Decrypting with key {}...", name);
info!("- Decrypting with key {}...", name);
matching_key = Some(key_array);
matching_iv = Some(iv_array);
break
@@ -121,7 +122,7 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
out_data.push(b ^ key_array[i % key_array.len()]);
}
} else {
println!("- Failed to decrypt data!");
info!("- Failed to decrypt data!");
continue
}
} else {
@@ -134,7 +135,7 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
if imtk_len != 0 && &out_data[56..60] != MTK_META_PAD_MAGIC {
let version_len = u32::from_le_bytes(out_data[56..60].try_into().unwrap());
let version = common::string_from_bytes(&out_data[60..60 + version_len as usize]);
println!("- Version: {}", version);
info!("- Version: {}", version);
}
imtk_len + 8
} else {
@@ -148,18 +149,18 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
if part_entry.is_compressed() {
match decompress_mtk_to_file(&fin_data, &output_path) {
Ok(()) => {
println!("-- Decompressed Successfully, Saved file!");
info!("-- Decompressed Successfully, Saved file!");
continue
},
Err(e) => {
eprintln!("Failed to decompress partition!, Error: {}. Saving compressed data...", e);
warn!("Failed to decompress partition!, Error: {}. Saving compressed data...", e);
}
}
}
let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?;
out_file.write_all(&fin_data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
+11 -10
View File
@@ -14,6 +14,7 @@ use crate::keys;
use crate::formats::mtk_pkg::lzhs::{decompress_mtk_to_file};
use crate::formats::mtk_pkg::include::{Header, PartEntry, MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC};
use include::*;
use log::{info, warn};
pub struct MtkPkgNewContext {
matching_key_name: String,
@@ -46,12 +47,12 @@ pub fn is_mtk_pkg_new_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>,
pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<MtkPkgNewContext>().expect("Missing context");
let ctx = ctx.downcast::<MtkPkgNewContext>().map_err(|_| "Invalid context type")?;
let file_size = file.metadata()?.len();
//the key was founf, and header was decrypted at detection stage so we can reuse
println!("Using key {}", ctx.matching_key_name);
info!("Using key {}", ctx.matching_key_name);
let key_array = ctx.matching_key_key;
let iv_array = ctx.matching_key_iv;
let header = ctx.decrypted_header;
@@ -60,7 +61,7 @@ pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
let mut hdr_reader = Cursor::new(header);
let hdr: Header = hdr_reader.read_le()?;
println!("File info:\nFile size: {}\nVendor magic: {}\nVersion info: {}\nProduct name: {}" ,
info!("File info:\nFile size: {}\nVendor magic: {}\nVersion info: {}\nProduct name: {}" ,
hdr.file_size, hdr.vendor_magic(), hdr.version(), hdr.product_name());
file.seek(SeekFrom::Start(HEADER_SIZE as u64))?;
@@ -73,19 +74,19 @@ pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
break
}
println!("\n#{} - {}, Size: {}{} {}",
info!("\n#{} - {}, Size: {}{} {}",
part_n, part_entry.name(), part_entry.size, if part_entry.is_compressed() {" [COMPRESSED]"} else {""}, if part_entry.is_encrypted() {"[ENCRYPTED]"} else {""} );
let data = common::read_exact(&mut file, part_entry.size as usize + 48)?;
if part_entry.size == 0 {
println!("- Empty entry, skipping!");
info!("- Empty entry, skipping!");
continue
}
let mut out_data;
if part_entry.is_encrypted() {
println!("- Decrypting...");
info!("- Decrypting...");
//data aligned to 16 bytes is AES encrypted. the remaining unaligned data is XORed with the key
let align_len = data.len() & !15;
let (aes_enc, xor_tail) = data.split_at(align_len);
@@ -103,7 +104,7 @@ pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
if imtk_len != 0 && &out_data[56..60] != MTK_META_PAD_MAGIC {
let version_len = u32::from_le_bytes(out_data[56..60].try_into().unwrap());
let version = common::string_from_bytes(&out_data[60..60 + version_len as usize]);
println!("- Version: {}", version);
info!("- Version: {}", version);
}
imtk_len + 8
} else {
@@ -118,18 +119,18 @@ pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
if part_entry.is_compressed() {
match decompress_mtk_to_file(&fin_data, &output_path) {
Ok(()) => {
println!("-- Decompressed Successfully, Saved file!");
info!("-- Decompressed Successfully, Saved file!");
continue
},
Err(e) => {
eprintln!("Failed to decompress partition!, Error: {}. Saving compressed data...", e);
warn!("Failed to decompress partition!, Error: {}. Saving compressed data...", e);
}
}
}
let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?;
out_file.write_all(&fin_data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
Ok(())
+9 -8
View File
@@ -14,6 +14,7 @@ use crate::formats::mtk_pkg::lzhs::{decompress_mtk_to_file_old};
use crate::formats::mtk_pkg::include::{PartEntry, MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC};
use mtk_crypto::{decrypt};
use include::*;
use log::{info, warn};
pub struct MtkPkgOldContext {
header_offset: u64,
@@ -39,7 +40,7 @@ pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>,
pub fn extract_mtk_pkg_old(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<MtkPkgOldContext>().expect("Missing context");
let ctx = ctx.downcast::<MtkPkgOldContext>().map_err(|_| "Invalid context type")?;
let file_size = file.metadata()?.len();
@@ -51,7 +52,7 @@ pub fn extract_mtk_pkg_old(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
let mut hdr_reader = Cursor::new(header);
let hdr: Header = hdr_reader.read_le()?;
println!("File info:\nFile size: {}\nVendor magic: {}\nVersion info: {}\nProduct name: {}" ,
info!("File info:\nFile size: {}\nVendor magic: {}\nVersion info: {}\nProduct name: {}" ,
hdr.file_size, hdr.vendor_magic(), hdr.version(), hdr.product_name());
let mut part_n = 0;
@@ -59,14 +60,14 @@ pub fn extract_mtk_pkg_old(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
part_n += 1;
let part_entry: PartEntry = file.read_le()?;
println!("\n#{} - {}, Size: {}{} {}",
info!("\n#{} - {}, Size: {}{} {}",
part_n, part_entry.name(), part_entry.size, if part_entry.is_compressed() {" [COMPRESSED]"} else {""}, if part_entry.is_encrypted() {"[ENCRYPTED]"} else {""} );
let data = common::read_exact(&mut file, part_entry.size as usize)?;
let out_data;
if part_entry.is_encrypted() {
//decrypt with the vendor magic
println!("- Decrypting...");
info!("- Decrypting...");
let vendor_magic_u32 = u32::from_le_bytes(hdr.vendor_magic_bytes.clone().try_into().unwrap());
out_data = decrypt(&data, vendor_magic_u32, Some(CONTENT_XOR_MASK));
} else {
@@ -79,7 +80,7 @@ pub fn extract_mtk_pkg_old(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
if imtk_len != 0 && &out_data[8..12] != MTK_META_PAD_MAGIC {
let version_len = u32::from_le_bytes(out_data[8..12].try_into().unwrap());
let version = common::string_from_bytes(&out_data[12..12 + version_len as usize]);
println!("- Version: {}", version);
info!("- Version: {}", version);
}
imtk_len + 8
} else {
@@ -93,18 +94,18 @@ pub fn extract_mtk_pkg_old(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
if part_entry.is_compressed() {
match decompress_mtk_to_file_old(&fin_data, &output_path) {
Ok(()) => {
println!("-- Decompressed Successfully, Saved file!");
info!("-- Decompressed Successfully, Saved file!");
continue
},
Err(e) => {
eprintln!("Failed to decompress partition!, Error: {}. Saving compressed data...", e);
warn!("Failed to decompress partition!, Error: {}. Saving compressed data...", e);
}
}
}
let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?;
out_file.write_all(&fin_data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
Ok(())
+4 -3
View File
@@ -9,6 +9,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use include::*;
use log::info;
pub fn is_novatek_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -25,7 +26,7 @@ pub fn extract_novatek(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let header: Header = file.read_le()?;
println!("File info:\nFirmware name: {}\nVersion: {}.{}\nData size: {}\nPart count: {}",
info!("File info:\nFirmware name: {}\nVersion: {}.{}\nData size: {}\nPart count: {}",
header.firmware_name(), header.version_major, header.version_minor, header.data_size, header.part_count);
let mut entries: Vec<PartEntry> = Vec::new();
@@ -37,7 +38,7 @@ pub fn extract_novatek(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
let mut e_i = 0;
for entry in &entries {
e_i += 1;
println!("\n({}/{}) - ID: {}, Offset: {}, Size: {}", e_i, entries.len(), entry.id, entry.offset, entry.size);
info!("\n({}/{}) - ID: {}, Offset: {}, Size: {}", e_i, entries.len(), entry.id, entry.offset, entry.size);
let data = common::read_file(&file, entry.offset as u64, entry.size as usize)?;
@@ -47,7 +48,7 @@ pub fn extract_novatek(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
println!("- Saved file!");
info!("- Saved file!");
}
Ok(())
+7 -10
View File
@@ -8,6 +8,7 @@ use std::io::Write;
use crate::utils::common;
use crate::utils::aes::decrypt_aes_ecb_auto;
use crate::keys;
use log::info;
const NVT_PARTITIONS: [(&str, u32, u32); 16] = [
("spi_boot", 0x0000020, 0x00020000),
@@ -41,10 +42,6 @@ pub fn is_novatek_raw_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>,
return Ok(Some(Box::new(())));
}
for _ in keys::NOVATEK_RAW {
return Ok(Some(Box::new(())));
}
Ok(None)
}
@@ -66,28 +63,28 @@ pub fn extract_novatek_raw(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(
let file_size = file.metadata()?.len() as usize;
let enc_data = common::read_file(&mut file, 0, file_size)?;
println!("- File size: {} bytes", file_size);
println!("- Decrypting with AES-{}...", if key.len() == 16 { "128" } else { "256" });
info!("- File size: {} bytes", file_size);
info!("- Decrypting with AES-{}...", if key.len() == 16 { "128" } else { "256" });
let dec_data = decrypt_aes_ecb_auto(&key, &enc_data)?;
println!("- Decryption complete");
info!("- Decryption complete");
fs::create_dir_all(&app_ctx.output_dir)?;
let output_path = Path::new(&app_ctx.output_dir).join("_decrypted.bin");
let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?;
out_file.write_all(&dec_data)?;
println!("- Saved decrypted image to {}", output_path.display());
info!("- Saved decrypted image to {}", output_path.display());
println!("\nExtracting partitions:");
info!("\nExtracting partitions:");
for (name, offset, size) in NVT_PARTITIONS {
if (offset + size) as usize <= dec_data.len() && size > 0 {
let part_data = &dec_data[offset as usize..(offset + size) as usize];
let part_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", name));
let mut part_file = OpenOptions::new().write(true).create(true).open(&part_path)?;
part_file.write_all(part_data)?;
println!(" {}: offset=0x{:x}, size={}", name, offset, size);
info!(" {}: offset=0x{:x}, size={}", name, offset, size);
}
}
+9 -8
View File
@@ -11,6 +11,7 @@ use crate::utils::common;
use crate::utils::compression::{decompress_gzip};
use crate::utils::sparse::{unsparse_to_file};
use include::*;
use log::info;
pub struct TimgContext {
variant: TimgVariant,
@@ -42,14 +43,14 @@ pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Bo
pub fn extract_nvt_timg(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<TimgContext>().expect("Missing context");
let ctx = ctx.downcast::<TimgContext>().map_err(|_| "Invalid context type")?;
let timg: Box<dyn TIMG> = match ctx.variant {
TimgVariant::New => Box::new(file.read_le::<TIMG64>()?),
TimgVariant::Old => Box::new(file.read_le::<TIMG32>()?),
TimgVariant::Old2 => Box::new(file.read_le::<TIMGOld2>()?),
};
println!("File info:\nVariant: {:?}\nData size: {}", ctx.variant, timg.data_size());
info!("File info:\nVariant: {:?}\nData size: {}", ctx.variant, timg.data_size());
//position after header + data size
let end = file.stream_position()? + timg.data_size() as u64;
@@ -70,27 +71,27 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), B
let data = common::read_exact(&mut file, pimg.size())?;
println!("\n#{} - {}, Size: {}, Dest: {}, Compression: {}, Comment: {}",
info!("\n#{} - {}, Size: {}, Dest: {}, Compression: {}, Comment: {}",
pimg_i, pimg.name(), pimg.size(), pimg.dest_dev(), pimg.comp_type(), pimg.comment());
let out_data;
let output_path = Path::new(&app_ctx.output_dir).join(pimg.name() + ".bin");
if pimg.comp_type() == "gzip" && data.starts_with(b"\x1F\x8B") { //additionally check for gzip header, because sometimes its deceptive
println!("- Decompressing gzip...");
info!("- Decompressing gzip...");
out_data = decompress_gzip(&data)?;
} else if pimg.comp_type() == "none" || pimg.comp_type() == "" {
out_data = data;
} else if pimg.comp_type() == "sparse" {
println!("- Unsparsing...");
info!("- Unsparsing...");
unsparse_to_file(&data, output_path)?;
println!("-- Saved file!");
info!("-- Saved file!");
continue
} else {
println!("- Warning: unsupported compression type, saving stored data!");
info!("- Warning: unsupported compression type, saving stored data!");
out_data = data;
}
@@ -98,7 +99,7 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), B
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
Ok(())
+1 -1
View File
@@ -1,6 +1,6 @@
use super::include::ONKYO_MAGIC;
pub fn ub_encrypte_block(input: &[u8], key: &[u8; 8]) -> Vec<u8> {
pub fn ub_encrypted_block(input: &[u8], key: &[u8; 8]) -> Vec<u8> {
let mut output = Vec::with_capacity(input.len());
let state_counter = 0;
let state_byte = 0;
+15 -14
View File
@@ -12,6 +12,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use include::*;
use crypto::*;
use log::info;
struct OnkyoCtx {
header_size: u32,
@@ -21,7 +22,7 @@ pub fn is_onkyo_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<d
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
let enc_inihdr = common::read_file(&file, 0, 20)?;
let dec_inihdr= ub_encrypte_block(&enc_inihdr, &HEADER_KEY);
let dec_inihdr= ub_encrypted_block(&enc_inihdr, &HEADER_KEY);
if dec_inihdr.starts_with(ONKYO_MAGIC) {
let header_size = u32::from_le_bytes(dec_inihdr[16..20].try_into().unwrap());
@@ -33,12 +34,12 @@ pub fn is_onkyo_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<d
pub fn extract_onkyo(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<OnkyoCtx>().expect("Missing context");
let ctx = ctx.downcast::<OnkyoCtx>().map_err(|_| "Invalid context type")?;
println!("Header size: {}", ctx.header_size);
info!("Header size: {}", ctx.header_size);
let enc_hdr = common::read_exact(&mut file, ctx.header_size as usize)?;
let dec_hdr = ub_encrypte_block(&enc_hdr, &HEADER_KEY);
let dec_hdr = ub_encrypted_block(&enc_hdr, &HEADER_KEY);
opt_dump_dec_hdr(app_ctx, &dec_hdr, "header")?;
let mut hdr_rdr = Cursor::new(dec_hdr);
@@ -47,7 +48,7 @@ pub fn extract_onkyo(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<
//read info section
hdr_rdr.seek(SeekFrom::Start(hdr.pack_info_offset as u64))?;
let info: PackInfo = hdr_rdr.read_le()?;
println!("Info -\nPackage ID: {}\nVersion: {}\nEntry count: {}\nEntries in file: {}\nPack: {}/{}",
info!("Info -\nPackage ID: {}\nVersion: {}\nEntry count: {}\nEntries in file: {}\nPack: {}/{}",
info.package_id(), info.package_version(), info.entry_count, info.entries_in_file, info.pack_id, info.pack_count,);
if info.entries_in_file == 0 {
@@ -80,7 +81,7 @@ pub fn extract_onkyo(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<
}
act_ei += 1;
println!("\n({}/{}) - {}, Size: {}, Offset: {}, Pack location: {}",
info!("\n({}/{}) - {}, Size: {}, Offset: {}, Pack location: {}",
act_ei, info.entries_in_file, entry.id(), data_entry.size, data_entry.offset, entry.pack_location);
let data = common::read_file(&mut file, data_entry.offset as u64, data_entry.size as usize)?;
@@ -91,33 +92,33 @@ pub fn extract_onkyo(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<
let mut is_pack = false;
//try using standard data key
if ub_encrypte_block(&data[..16], &DATA_KEY).starts_with(ONKYO_MAGIC) {
if ub_encrypted_block(&data[..16], &DATA_KEY).starts_with(ONKYO_MAGIC) {
dec_key = Some(DATA_KEY);
//try header key, if success it means this is a package inside a package, and it should not be decrypted
} else if ub_encrypte_block(&data[..16], &HEADER_KEY).starts_with(ONKYO_MAGIC) {
} else if ub_encrypted_block(&data[..16], &HEADER_KEY).starts_with(ONKYO_MAGIC) {
is_pack = true;
//if not matched with data key, try to calc key
} else {
let calced_key = calc_key(&data[..8]);
if ub_encrypte_block(&data[..16], &calced_key).starts_with(ONKYO_MAGIC) {
if ub_encrypted_block(&data[..16], &calced_key).starts_with(ONKYO_MAGIC) {
dec_key = Some(calced_key);
}
}
if let Some(key) = dec_key {
println!("- Detected encrypted data, decrypting...");
out_data = ub_encrypte_block(&data, &key);
info!("- Detected encrypted data, decrypting...");
out_data = ub_encrypted_block(&data, &key);
out_data.drain(0..16); //remove ONKYO Encryption heading
} else if is_pack {
println!("- Inner pack detected!"); //maybe handle this...
info!("- Inner pack detected!"); //maybe handle this...
out_data = data;
} else {
println!("- Failed to decrypt data or entry is not encrypted, saving raw data...");
info!("- Failed to decrypt data or entry is not encrypted, saving raw data...");
out_data = data;
}
@@ -126,7 +127,7 @@ pub fn extract_onkyo(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
+26 -24
View File
@@ -1,3 +1,5 @@
use log::info;
mod include;
mod pana_dvd_crypto;
mod lzss;
@@ -62,7 +64,7 @@ pub fn is_pana_dvd_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Bo
pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let context = ctx.downcast::<PanaDvdContext>().expect("Missing context");
let context = ctx.downcast::<PanaDvdContext>().map_err(|_| "Invalid context type")?;
let matching_key = context.matching_key;
let mut file_entries: Vec<FileEntry> = Vec::new();
@@ -70,7 +72,7 @@ pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), B
//AES files can contain multiple firmwares inside of itself
if context.is_aes {
let (aes_key, aes_iv) = (context.aes_key.unwrap(), context.aes_iv.unwrap());
println!("Using key: {} + AES key: {}, IV: {}", hex::encode_upper(matching_key), hex::encode_upper(aes_key), hex::encode_upper(aes_iv));
info!("Using key: {} + AES key: {}, IV: {}", hex::encode_upper(matching_key), hex::encode_upper(aes_key), hex::encode_upper(aes_iv));
//read inner file table
let file_table = common::read_exact(&mut file, 48)?;
@@ -89,11 +91,11 @@ pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), B
}
} else {
println!("Using key: {}", hex::encode_upper(matching_key));
info!("Using key: {}", hex::encode_upper(matching_key));
file_entries.push(FileEntry { offset: 0, size: file.metadata()?.len() as u32, header_size: context.base_hdr_size });
}
println!("File contains {} sub-files...", file_entries.len());
info!("File contains {} sub-files...", file_entries.len());
for (i, file_entry ) in file_entries.iter().enumerate() {
let data = common::read_file(&mut file, file_entry.offset as u64, file_entry.size as usize)?;
let dec_data = if context.is_aes {
@@ -111,7 +113,7 @@ pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), B
&app_ctx.output_dir.join(format!("file_{}", i + 1))
};
println!("\nExtracting file {}/{} - Offset: {}, Size: {}, Header size: {}",
info!("\nExtracting file {}/{} - Offset: {}, Size: {}, Header size: {}",
i + 1, file_entries.len(), file_entry.offset, file_entry.size, file_entry.header_size);
extract_file(app_ctx, &mut file_reader, file_entry.header_size as u64, matching_key, output_dir)?;
@@ -133,10 +135,10 @@ fn extract_file(app_ctx: &AppContext, file_reader: &mut Cursor<Vec<u8>>, header_
for i in 0..100 {
let entry: ModuleEntry = list_reader.read_le()?;
if !entry.is_valid() {break};
println!("Module {} - Name: {}, Version: {}, Model ID: {}, ID: {}, Offset: {}, Size: {}",
info!("Module {} - Name: {}, Version: {}, Model ID: {}, ID: {}, Offset: {}, Size: {}",
i + 1, entry.name(), entry.version(), entry.model_id(), entry.id(), entry.offset, entry.size);
if modules.iter().any(|m| m.offset == entry.offset ){
println!("- Duplicate module, skipping!");
info!("- Duplicate module, skipping!");
continue
}
@@ -146,7 +148,7 @@ fn extract_file(app_ctx: &AppContext, file_reader: &mut Cursor<Vec<u8>>, header_
let mut mod_i = 0;
for module in &modules {
mod_i += 1;
println!("\n({}/{}) - {}, Offset: {}, Size: {}, Checksum: {:#010x}",
info!("\n({}/{}) - {}, Offset: {}, Size: {}, Checksum: {:#010x}",
mod_i, modules.len(), module.name(), module.offset, module.size, module.data_checksum);
//if there is multiple modules with the same name, add the module ID to the outptut file to prevent collision
@@ -160,10 +162,10 @@ fn extract_file(app_ctx: &AppContext, file_reader: &mut Cursor<Vec<u8>>, header_
//special treatment of MAIN
if module.name() == "MAIN" {
println!("- Extracting MAIN...");
info!("- Extracting MAIN...");
extract_main(file_reader, key, &output_path)?;
if app_ctx.has_option("pana_dvd:split_main") {
println!("\n- Splitting MAIN...");
info!("\n- Splitting MAIN...");
split_main_file(&output_path, output_folder)?;
}
continue
@@ -171,12 +173,12 @@ fn extract_file(app_ctx: &AppContext, file_reader: &mut Cursor<Vec<u8>>, header_
let data = common::read_exact(file_reader, (module.size as usize + 7) & !7)?; //read to the nearest multiple of 8 (needed for unalinged data decryption)
println!("- Decrypting...");
info!("- Decrypting...");
let mut dec_data = decrypt_data(&data, &key);
dec_data.truncate(module.size as usize); //discard padding
if module.name().starts_with("DRV") {
println!("- Extracting DRIVE firmware...");
info!("- Extracting DRIVE firmware...");
dec_data = extract_drv(dec_data, &key)?;
}
@@ -184,7 +186,7 @@ fn extract_file(app_ctx: &AppContext, file_reader: &mut Cursor<Vec<u8>>, header_
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&dec_data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
Ok(())
@@ -193,15 +195,15 @@ fn extract_file(app_ctx: &AppContext, file_reader: &mut Cursor<Vec<u8>>, header_
fn extract_main(file_reader: &mut Cursor<Vec<u8>>, key: [u8; 8], output_path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
let main_list_hdr: MainListHeader = file_reader.read_le()?;
if main_list_hdr.entry_count() > 200 {
println!("Unsupported MAIN data, skipping!");
info!("Unsupported MAIN data, skipping!");
return Ok(())
}
println!("MAIN - Entry count: {}, Decompressed part size: {}", main_list_hdr.entry_count(), main_list_hdr.decompressed_part_size);
info!("MAIN - Entry count: {}, Decompressed part size: {}", main_list_hdr.entry_count(), main_list_hdr.decompressed_part_size);
let mut main_entries: Vec<MainListEntry> = Vec::new();
for i in 0..main_list_hdr.entry_count() {
let main_entry: MainListEntry = file_reader.read_le()?;
println!("- Entry {}/{} - Size: {}, Checksum: {:#010x}",
info!("- Entry {}/{} - Size: {}, Checksum: {:#010x}",
i + 1, main_list_hdr.entry_count(), main_entry.size, main_entry.checksum);
main_entries.push(main_entry);
}
@@ -225,12 +227,12 @@ fn extract_main(file_reader: &mut Cursor<Vec<u8>>, key: [u8; 8], output_path: &P
data.copy_from_slice(&decrypted);
}
print!("\nMAIN ({}/{}) - ", maine_i, main_entries.len());
info!("MAIN ({}/{}) - ", maine_i, main_entries.len());
let decompressed_data = decompress_data(&data)?;
main_out_file.write_all(&decompressed_data)?;
println!("-- Saved to MAIN!");
info!("-- Saved to MAIN!");
}
Ok(())
@@ -241,22 +243,22 @@ fn decompress_data(data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let header: CompressedFileHeader = data_reader.read_le()?;
let compression_type = CompressionType::from(header.compression_type);
println!("Compressed size: {}, Decompressed size: {}, Compression type: {:?}({})",
info!("Compressed size: {}, Decompressed size: {}, Compression type: {:?}({})",
header.src_size, header.dest_size, compression_type, header.compression_type);
let compressed_data = common::read_exact(&mut data_reader, header.src_size as usize)?;
let mut decompressed_data;
if compression_type == CompressionType::Gzip {
println!("- Decompressing GZIP...");
info!("- Decompressing GZIP...");
let (decompressed_gzip, gzip_filename) = decompress_gzip_get_filename(&compressed_data)?;
if let Some(gzip_filename) = gzip_filename {
println!("- GZIP filename: {}", gzip_filename);
info!("- GZIP filename: {}", gzip_filename);
}
decompressed_data = decompressed_gzip;
} else if compression_type == CompressionType::Lzss {
println!("- Decompressing LZSS...");
info!("- Decompressing LZSS...");
decompressed_data = decompress_lzss(&compressed_data);
if decompressed_data.len() != header.dest_size as usize {
return Err("Decompressed size does not match size in header, decompression failed!".into());
@@ -267,7 +269,7 @@ fn decompress_data(data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
//GzipAndLzss is not used in this context.
} else {
println!("- Unknown compression method!");
info!("- Unknown compression method!");
decompressed_data = compressed_data;
}
@@ -293,7 +295,7 @@ fn extract_drv(mut data: Vec<u8>, key: &[u8; 8]) -> Result<Vec<u8>, Box<dyn std:
let mut reader = Cursor::new(&data);
let header: DriveHeader = reader.read_le()?;
println!("- DRIVE info:\n-- Manufacturer ID: {}\n-- Model: {}\n-- Version: {}", header.manufacturer(), header.model(), header.version());
info!("- DRIVE info:\n-- Manufacturer ID: {}\n-- Model: {}\n-- Version: {}", header.manufacturer(), header.model(), header.version());
//can be compressed
let out_data = if data[header_size..].starts_with(COMPRESSED_FILE_MAGIC) {
+5 -3
View File
@@ -1,3 +1,5 @@
use log::info;
use crate::utils::common;
use std::{fs::{self, File, OpenOptions}, io::{Read, Write}, path::{Path, PathBuf}};
@@ -48,7 +50,7 @@ pub fn split_main_file(path: &PathBuf, out_path: &PathBuf) -> Result<(), Box<dyn
}
let root = root.ok_or("Failed to get root partition!")?;
println!("Root - {}", root);
info!("Root - {}", root);
let root_index = parts.iter().position(|(n, _s)| n == &root).ok_or("Root partition not found in partition list!")?;
let mut tsize: u64 = 0;
@@ -60,7 +62,7 @@ pub fn split_main_file(path: &PathBuf, out_path: &PathBuf) -> Result<(), Box<dyn
break
}
println!("- {} - Size: {}", part_name, part_size);
info!("- {} - Size: {}", part_name, part_size);
tsize += part_size;
let data = common::read_exact(&mut file, *part_size as usize)?;
@@ -76,7 +78,7 @@ pub fn split_main_file(path: &PathBuf, out_path: &PathBuf) -> Result<(), Box<dyn
if has_swup_addon {
let mut data = Vec::new();
file.read_to_end(&mut data)?;
println!("- SWUP_ADDON - Size: {}", data.len());
info!("- SWUP_ADDON - Size: {}", data.len());
let output_path = Path::new(&output_folder).join("SWUP_ADDON.bin");
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
+7 -7
View File
@@ -39,11 +39,11 @@ pub struct Header {
_magic_bytes: [u8; 8],
pub header_size: u32, //data start
pub data_size: u32,
_crc32: u32,
pub mask: u32,
_data_size_decompressed: u32,
_padding2: u32,
description_bytes: [u8; 512],
_crc32: u32,
pub mask: u32,
_data_size_decompressed: u32,
_padding2: u32,
description_bytes: [u8; 512],
}
impl Header {
pub fn description(&self) -> String {
@@ -58,8 +58,8 @@ impl Header {
pub struct FileHeader {
file_name_bytes: [u8; 60],
pub real_size: u32,
pub stored_size: u32,
pub header_size: u32,
pub stored_size: u32,
pub header_size: u32,
pub attributes: [u8; 4],
}
impl FileHeader {
+13 -11
View File
@@ -10,6 +10,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use crate::utils::aes::decrypt_aes256_ecb;
use include::*;
use log::info;
pub fn is_pfl_upg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -32,24 +33,24 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
let version_bytes = common::read_exact(&mut file, 28)?;
let version = common::string_from_bytes(&version_bytes);
println!("\nVersion: {}", version);
info!("\nVersion: {}", version);
if header.description() != "" { //look ugly when empty
println!("--- Description --- \n{}", header.description());
println!("-------------------");
info!("--- Description --- \n{}", header.description());
info!("-------------------");
}
println!("Data size: {}", header.data_size);
info!("Data size: {}", header.data_size);
file.seek(SeekFrom::Start(header.header_size as u64))?;
let mut data;
if header.is_encrypted() {
println!("\nFile is encrypted.");
info!("\nFile is encrypted.");
//get some data as test ciphertext for key finding
let ciphertext = common::read_file(&mut file, header.header_size as u64, 64)?;
let aes_key;
if let Some((key_name, key)) = try_find_key(&signature, &ciphertext)? {
println!("Matched pubkey: {}, AES key: {}", key_name, hex::encode(key));
info!("Matched pubkey: {}, AES key: {}", key_name, hex::encode(key));
aes_key = key;
} else {
return Err("Matching key not found, cannot decrypt data".into());
@@ -58,7 +59,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
//need to align to 16 bytes for AES blocksize
let encrypted_data = common::read_exact(&mut file, (header.data_size as usize + 0xf) & !0xf)?;
println!("Decrypting data...");
info!("Decrypting data...");
data = decrypt_aes256_ecb(&aes_key, &encrypted_data)?;
data.truncate(header.data_size as usize); //discard padding
@@ -76,7 +77,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
let ex_header_bytes = common::read_exact(&mut data_reader, ex_header_size as usize)?;
if file_header.is_folder() {
println!("\nFolder - {}", file_header.file_name());
info!("\nFolder - {}", file_header.file_name());
let output_path = Path::new(&app_ctx.output_dir).join(file_header.file_name().trim_start_matches('/'));
fs::create_dir_all(output_path)?;
continue
@@ -88,7 +89,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
file_header.file_name()
};
println!("\nFile - {}, Size: {}", file_name, file_header.real_size);
info!("\nFile - {}, Size: {}", file_name, file_header.real_size);
let data = common::read_exact(&mut data_reader, file_header.stored_size as usize)?;
let output_path = Path::new(&app_ctx.output_dir).join(file_name.trim_start_matches('/'));
@@ -100,7 +101,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
//pfl upg inside pfl upg! DUMB code!
if file_header.is_package() && !app_ctx.has_option("pfl_upg:no_extract_inner_upg"){
println!("- Extracting inner UPG...");
info!("- Extracting inner UPG...");
//save this as temp file
let temp_path = Path::new(&app_ctx.output_dir).join("inner_upg_temp");
@@ -115,6 +116,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
options: app_ctx.options.clone(),
dry_run: app_ctx.dry_run,
quiet: app_ctx.quiet,
verbose: app_ctx.verbose,
};
//do check just in case and extract
@@ -132,7 +134,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data[..file_header.real_size as usize])?;
println!("- Saved file!");
info!("- Saved file!");
}
Ok(())
+11 -10
View File
@@ -9,6 +9,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use include::*;
use log::info;
struct PhilipsBdpCtx {
header_type: HeaderType,
@@ -31,7 +32,7 @@ pub fn is_philips_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>,
pub fn extract_philips_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<PhilipsBdpCtx>().expect("Missing context");
let ctx = ctx.downcast::<PhilipsBdpCtx>().map_err(|_| "Invalid context type")?;
let header: Box<dyn UpgHeader> = match ctx.header_type {
HeaderType::Old => Box::new(file.read_le::<UpgHeaderOld>()?),
@@ -40,7 +41,7 @@ pub fn extract_philips_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
let header_size = file.stream_position()?;
println!("File info -\nName: {}\nVersion: {}\nTarget size: {}\nEntry count: {}\nHeader type: {:?}\nHeader size: {}",
info!("File info -\nName: {}\nVersion: {}\nTarget size: {}\nEntry count: {}\nHeader type: {:?}\nHeader size: {}",
header.name(), header.version(), header.target_size(), header.target_num(), ctx.header_type, header_size);
for (i, entry) in header.entries().iter().enumerate() {
@@ -48,14 +49,14 @@ pub fn extract_philips_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
break
}
println!("\n#{} - ID: {:x}, IIC: {:x}, Version: {}, Offset: {}, Size: {}",
info!("\n#{} - ID: {:x}, IIC: {:x}, Version: {}, Offset: {}, Size: {}",
i+1, entry.id, entry.iic, entry.version(), entry.offset, entry.size);
let data = common::read_file(&file, entry.offset as u64 + header_size, entry.size as usize)?;
let out_data;
if entry.id == 0 && app_ctx.has_option("philips_bdp:decrypt") {
println!("- Decrypting...");
info!("- Decrypting...");
out_data = bebin_decrypt_aes256cfb(&data, &KEY1, &IV1);
} else {
out_data = data;
@@ -67,23 +68,23 @@ pub fn extract_philips_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<()
let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?;
out_file.write_all(&out_data)?;
println!("- Saved file!");
info!("- Saved file!");
//ID 0 should be the main MTK bdp file, since this is just an extra container for that format (like Sony BDP), so we can try to extract it here.
if entry.id == 0 {
println!("Checking if it's also MTK BDP...");
info!("Checking if it's also MTK BDP...");
let new_file = File::open(&output_path)?;
let mtk_ctx: AppContext = AppContext { input: InputTarget::File(new_file), output_dir: app_ctx.output_dir.join("0"), options: app_ctx.options.clone(), dry_run: app_ctx.dry_run, quiet: app_ctx.quiet };
let mtk_ctx: AppContext = AppContext { input: InputTarget::File(new_file), output_dir: app_ctx.output_dir.join("0"), options: app_ctx.options.clone(), dry_run: app_ctx.dry_run, quiet: app_ctx.quiet, verbose: app_ctx.verbose };
if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&mtk_ctx)? {
println!("- MTK BDP file detected!\n");
info!("- MTK BDP file detected!\n");
formats::mtk_bdp::extract_mtk_bdp(&mtk_ctx, result)?;
} else {
if app_ctx.has_option("philips_bdp:decrypt") {
println!("- Not an MTK BDP file");
info!("- Not an MTK BDP file");
} else {
println!("- Not an MTK BDP file (try with decrypt?)");
info!("- Not an MTK BDP file (try with decrypt?)");
}
}
}
+12 -11
View File
@@ -10,6 +10,7 @@ use std::io::{Write, Seek, SeekFrom};
use crate::utils::common;
use crate::utils::compression::{decompress_zlib};
use include::*;
use log::info;
pub fn is_pup_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -26,7 +27,7 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let header: Header = file.read_le()?;
println!("File info:\nFile size: {}\nEntry count: {}",
info!("File info:\nFile size: {}\nEntry count: {}",
header.file_size, header.entry_count);
let mut entries: Vec<Entry> = Vec::new();
@@ -42,7 +43,7 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
let mut e_i = 0;
for entry in &entries {
println!("\n({}/{}) - ID: {} Offset: {}, Compressed Size: {}, Uncompressed Size: {}\nCompressed: {}, Blocked: {}, Block table: {}",
info!("\n({}/{}) - ID: {} Offset: {}, Compressed Size: {}, Uncompressed Size: {}\nCompressed: {}, Blocked: {}, Block table: {}",
e_i + 1, entries.len(), entry.id(), entry.offset, entry.compressed_size, entry.uncompressed_size, entry.is_compressed(), entry.is_blocked(), entry.is_block_table());
if !entry.is_block_table () {
@@ -51,17 +52,17 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
let block_count = (block_size + entry.uncompressed_size as u32 - 1) / block_size;
let last_block_size = entry.uncompressed_size % block_size as u64;
let mut my_block_table: Option<Entry> = None;
println!("Block size: {}, Block count: {}", block_size, block_count);
info!("Block size: {}, Block count: {}", block_size, block_count);
for block_table in &block_tables {
if block_table.id() == e_i {
my_block_table = Some(block_table.clone());
println!("Found block table: Offset: {}, Size: {}", block_table.offset, block_table.compressed_size);
info!("Found block table: Offset: {}, Size: {}", block_table.offset, block_table.compressed_size);
break
}
}
if my_block_table.is_none() {
println!("Failed to find block table!");
info!("Failed to find block table!");
continue
}
@@ -84,13 +85,13 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
let ac_block_size = if i == block_count - 1 {last_block_size as u32} else {block_size};
let compressed = if data_size == ac_block_size {false} else {true};
println!("Block {}/{}: Offset: {}, Data Size: {}, Padding: {}, Compressed: {}", i + 1, block_count, block.offset, data_size, padding, compressed);
info!("Block {}/{}: Offset: {}, Data Size: {}, Padding: {}, Compressed: {}", i + 1, block_count, block.offset, data_size, padding, compressed);
file.seek(std::io::SeekFrom::Start(initial_offset + block.offset as u64))?;
let out_data;
let data = common::read_exact(&mut file, data_size as usize)?;
if compressed {
println!("- Decompressing...");
info!("- Decompressing...");
out_data = decompress_zlib(&data)?;
} else {
out_data = data;
@@ -102,7 +103,7 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?;
println!("-- Saved!");
info!("-- Saved!");
file.seek(std::io::SeekFrom::Start(current_pos))?;
}
@@ -112,7 +113,7 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
let out_data;
if entry.is_compressed() {
println!("- Decompressing...");
info!("- Decompressing...");
out_data = decompress_zlib(&data)?;
} else {
out_data = data;
@@ -124,10 +125,10 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
} else {
println!("- Skipping block table..")
info!("- Skipping block table..")
}
e_i += 1;
+8 -7
View File
@@ -11,6 +11,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use crate::utils::aes::{decrypt_aes128_cbc_nopad, decrypt_aes128_cbc_pcks7};
use include::*;
use log::info;
pub fn is_roku_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -31,7 +32,7 @@ pub fn extract_roku(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
let mut encrypted_data = Vec::new();
file.read_to_end(&mut encrypted_data)?;
println!("\nDecrypting...\n");
info!("\nDecrypting...\n");
let tar_data = decrypt_aes128_cbc_pcks7(&encrypted_data, &FILE_KEY, &FILE_IV)?;
let tar_reader = Cursor::new(tar_data);
let mut tar_archive = Archive::new(tar_reader);
@@ -47,20 +48,20 @@ pub fn extract_roku(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
entry.read_to_end(&mut contents)?;
let text = String::from_utf8_lossy(&contents[..size - 256]); //dont display signature
println!("Manifest file:\n{}", text);
info!("Manifest file:\n{}", text);
} else {
let mut contents = Vec::new();
entry.read_to_end(&mut contents)?; //entry cant seek
if contents.starts_with(b"\x00\x00\x00\x00\x00\x00\x00\x00imgARMcC") {
println!("\nImage file: {:?}:", path);
info!("\nImage file: {:?}:", path);
let size = entry.header().size()? as usize;
let mut image_reader = Cursor::new(contents);
let mut i = 1;
while image_reader.stream_position()? < size as u64 {
let image: AImageHeader = image_reader.read_le()?;
println!(" #{} - Type: {}(0x{:x}), Length: {}, encmode: {}",
info!(" #{} - Type: {}(0x{:x}), Length: {}, encmode: {}",
i, image.image_type_str(), image.image_type, image.length, image.encmode_str());
let data =
@@ -80,20 +81,20 @@ pub fn extract_roku(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
println!(" - Saved file!\n");
info!(" - Saved file!\n");
i += 1;
}
} else {
println!("\nOther/Unknown file: {:?}", path);
info!("\nOther/Unknown file: {:?}", path);
let output_path = Path::new(&app_ctx.output_dir).join(&path);
fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&contents)?;
println!("- Saved file!");
info!("- Saved file!");
}
}
}
+3 -3
View File
@@ -5,7 +5,7 @@ use binrw::BinRead;
pub struct RufHeader {
_magic_bytes: [u8; 6],
_upgrade_type_bytes: [u8; 2],
_unk1: u32,
_unk1: u32,
date_time_bytes: [u8; 32],
buyer_bytes: [u8; 8],
model_bytes: [u8; 32],
@@ -33,7 +33,7 @@ impl RufHeader {
common::string_from_bytes(&self.region_info_bytes)
}
pub fn is_dual_ruf(&self) -> bool {
if self.dual_ruf_flag == 0x44 {true} else {false}
self.dual_ruf_flag == 0x44
}
}
@@ -41,7 +41,7 @@ impl RufHeader {
pub struct RufEntry {
_metadata: [u8; 32],
pub payload_type_bytes: u32,
pub size: u32,
pub size: u32,
_unk1: u32,
_unk2: [u8; 20],
}
+10 -9
View File
@@ -11,6 +11,7 @@ use crate::utils::common;
use crate::keys;
use crate::utils::aes::{decrypt_aes128_cbc_pcks7};
use include::*;
use log::info;
pub fn is_ruf_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -28,9 +29,9 @@ pub fn extract_ruf(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<d
let header: RufHeader = file.read_be()?;
if header.is_dual_ruf() {
println!("\nDual RUF detected! Extracting 1st RUF...\n");
info!("\nDual RUF detected! Extracting 1st RUF...\n");
actually_extract_ruf(file, &app_ctx.output_dir.join("RUF_1"), 0)?;
println!("\nExtracting 2nd RUF...\n");
info!("\nExtracting 2nd RUF...\n");
actually_extract_ruf(file, &app_ctx.output_dir.join("RUF_2"), 41943088)?;
} else {
actually_extract_ruf(file, &app_ctx.output_dir, 0)?;
@@ -43,10 +44,10 @@ fn actually_extract_ruf(mut file: &File, output_folder: &PathBuf, start_offset:
file.seek(SeekFrom::Start(start_offset))?;
let header: RufHeader = file.read_be()?;
println!("File info:\nBuyer: {} \nModel: {} \nRegion Info: {} \nDateTime: {}\nVersion:{:02x?} \nData Size: {} \nDual RUF: {}",
info!("File info:\nBuyer: {} \nModel: {} \nRegion Info: {} \nDateTime: {}\nVersion:{:02x?} \nData Size: {} \nDual RUF: {}",
header.buyer(), header.model(), header.region_info(), header.date_time(), header.version_bytes, header.data_size, header.is_dual_ruf());
println!("\nPayload count: {}", header.payload_count);
info!("\nPayload count: {}", header.payload_count);
file.seek(SeekFrom::Start(start_offset + header.payloads_start_offset as u64))?;
let mut entries: Vec<RufEntry> = Vec::new();
@@ -64,7 +65,7 @@ fn actually_extract_ruf(mut file: &File, output_folder: &PathBuf, start_offset:
vi += 1
}
println!("{}/{}: Type: {}({}), Size: {}",
info!("{}/{}: Type: {}({}), Size: {}",
vi, header.payload_count, entry.payload_type_bytes, entry.payload_type(), entry.size);
entries.push(entry);
@@ -82,7 +83,7 @@ fn actually_extract_ruf(mut file: &File, output_folder: &PathBuf, start_offset:
}
}
if let Some(k) = key {
println!("\nKey: {}", k);
info!("\nKey: {}", k);
key_bytes = hex::decode(k)?.as_slice().try_into()?;
} else {
return Err("This firmware is not supported!".into());
@@ -90,14 +91,14 @@ fn actually_extract_ruf(mut file: &File, output_folder: &PathBuf, start_offset:
file.seek(SeekFrom::Start(start_offset + 2048))?;
let encrypted_data = common::read_exact(&mut file, header.data_size as usize)?;
println!("Decrypting data...");
info!("Decrypting data...");
let decrypted_data = decrypt_aes128_cbc_pcks7(&encrypted_data, &key_bytes, &iv_bytes)?;
let mut data_reader = Cursor::new(decrypted_data);
let mut ei = 1;
for entry in entries {
println!("\n({}/{}) - {}({}), Size: {}",
info!("\n({}/{}) - {}({}), Size: {}",
ei, header.payload_count, entry.payload_type_bytes, entry.payload_type(), entry.size);
let data = common::read_exact(&mut data_reader, entry.size as usize)?;
@@ -108,7 +109,7 @@ fn actually_extract_ruf(mut file: &File, output_folder: &PathBuf, start_offset:
out_file.write_all(&data)?;
println!("- Saved file!");
info!("- Saved file!");
ei += 1;
}
+12 -11
View File
@@ -9,6 +9,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use include::*;
use log::info;
pub struct RvpContext {
header_type: HeaderType,
@@ -35,11 +36,11 @@ pub fn is_rvp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn
pub fn extract_rvp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<RvpContext>().expect("Missing context");
let ctx = ctx.downcast::<RvpContext>().map_err(|_| "Invalid context type")?;
if ctx.header_type == HeaderType::RVP {
let header: RVPHeader = file.read_be()?;
println!("RVP Info -\nVersion: {}\nYear: {:x}\nForce: {}", header.version_info(), header.year, header.force);
info!("RVP Info -\nVersion: {}\nYear: {:x}\nForce: {}", header.version_info(), header.year, header.force);
} else if ctx.header_type == HeaderType::MVP {
file.seek(std::io::SeekFrom::Start(36))?;
@@ -47,14 +48,14 @@ pub fn extract_rvp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dy
let mut obf_data = Vec::new();
file.read_to_end(&mut obf_data)?;
println!("DeXORing data..");
info!("DeXORing data..");
let data = decrypt_xor(&obf_data);
let data_size = data.len();
let mut data_reader = Cursor::new(data);
let module_count: u32 = data_reader.read_le()?; //little endian??
println!("Module count: {}", module_count);
info!("Module count: {}", module_count);
//follows table of sizes of modules, structure is static for given module
let mut module_names: Vec<&str> = Vec::new();
@@ -82,8 +83,8 @@ pub fn extract_rvp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dy
};
let header_size_bytes = common::read_exact(&mut data_reader, 4)?;
let header_size = u32::from_be_bytes(header_size_bytes.try_into().unwrap());
println!("\n({}/{}) - {}, Offset: {}, Header size: {}", i+1, module_count, module_name, data_reader.position() - 4, header_size);
let header_size = u32::from_be_bytes(header_size_bytes.try_into().map_err(|_| "Conversion failed")?);
info!("\n({}/{}) - {}, Offset: {}, Header size: {}", i+1, module_count, module_name, data_reader.position() - 4, header_size);
let hdr = common::read_exact(&mut data_reader, header_size as usize)?;
let size;
@@ -93,7 +94,7 @@ pub fn extract_rvp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dy
let lines: Vec<String> = hdr_string.lines().map(|l| l.trim().to_string()).collect();
//BEAUTIFUL
println!("ModelName: {}\nFileName: {}\nModelID: {}\nNewUpdate: {}\nNewMajorVer: {}\nNewMinorVer: {}\nForcedFlag: {}\nStartAddress: {}\nJumpAddress: {}\nMagicAddress: {}\nTotalSize: {}\nTotalSum: {}\nTotalCrc: {}",
info!("ModelName: {}\nFileName: {}\nModelID: {}\nNewUpdate: {}\nNewMajorVer: {}\nNewMinorVer: {}\nForcedFlag: {}\nStartAddress: {}\nJumpAddress: {}\nMagicAddress: {}\nTotalSize: {}\nTotalSum: {}\nTotalCrc: {}",
lines[0], lines[1], lines[2], lines[3], lines[4], lines[5], lines[6], lines[7], lines[8], lines[9], lines[10], lines[11], lines[12]);
size = lines[10].parse().unwrap();
@@ -116,7 +117,7 @@ pub fn extract_rvp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dy
//5. - unknown - like "00011200" -- this line is not present when size is 40 but were not using it anyway so whatever
name = lines[0].clone();
size = u32::from_str_radix(&lines[1], 16).unwrap();
println!("Name: {}", name);
info!("Name: {}", name);
} else if header_size == 16 {
// 4 bytes CRC32
@@ -126,11 +127,11 @@ pub fn extract_rvp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dy
size = u32::from_be_bytes(hdr[8..12].try_into().unwrap());
} else {
println!("Unsupported header size!");
info!("Unsupported header size!");
break
}
println!("Size: {}", size);
info!("Size: {}", size);
let data = common::read_exact(&mut data_reader, size as usize)?;
let output_path = Path::new(&app_ctx.output_dir).join(if name=="" {format!("{}_{}.bin", i+1, module_name)} else {format!("{}_{}_{}", i+1, module_name, name)});
@@ -138,7 +139,7 @@ pub fn extract_rvp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dy
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
println!("- Saved file!");
info!("- Saved file!");
}
Ok(())
+9 -8
View File
@@ -5,7 +5,7 @@ use crate::AppContext;
use std::fs;
use std::path::{Path};
use std::fs::{File, OpenOptions};
use std::io::{Write};
use std::io::Write;
use hex::decode;
use sha1::{Digest, Sha1};
use md5;
@@ -14,11 +14,12 @@ use crate::utils::common;
use crate::keys;
use crate::utils::aes::{decrypt_aes128_cbc_pcks7};
use include::decrypt_xor;
use log::info;
pub fn is_samsung_old_dir(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let dir = match app_ctx.dir() {Some(d) => d, None => return Ok(None)};
if Path::new(&dir).join("image").is_dir() & Path::new(&dir).join("image/info.txt").exists(){
if Path::new(&dir).join("image").is_dir() && Path::new(&dir).join("image/info.txt").exists(){
Ok(Some(Box::new(())))
} else {
Ok(None)
@@ -29,7 +30,7 @@ pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(
let path = app_ctx.dir().ok_or("Extractor expected directory")?;
let fw_info = fs::read_to_string(Path::new(&path).join("image/info.txt"))?;
println!("Firmware info: {}", fw_info);
info!("Firmware info: {}", fw_info);
let image_path = Path::new(&path).join("image");
@@ -43,7 +44,7 @@ pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(
}
}
if let Some(p) = secret {
println!("Secret: {}", p);
info!("Secret: {}", p);
} else {
return Err("This firmware is not supported!".into());
}
@@ -58,7 +59,7 @@ pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(
let file = File::open(&path)?;
let filename = path.file_name().unwrap().to_str().unwrap();
let file_size = file.metadata()?.len();
println!("\nFile - {}", filename);
info!("\nFile - {}", filename);
let data = common::read_file(&file, 0, file_size.try_into().unwrap())?;
let salt = &data[8..16];
@@ -103,10 +104,10 @@ pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(
}
let end = file_size - 260;
println!("- Decrypting file...");
info!("- Decrypting file...");
let decrypted_data = decrypt_aes128_cbc_pcks7(&data[16..end.try_into().unwrap()], &key_md5, &iv_md5)?;
println!("-- DeXORing file...");
info!("-- DeXORing file...");
let xor_key = fw_info.split_whitespace().next().unwrap();
let out_data = decrypt_xor(&decrypted_data, xor_key);
@@ -120,7 +121,7 @@ pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(
out_file.write_all(&out_data)?;
println!("--- Saved file!");
info!("--- Saved file!");
}
}
}
+10 -9
View File
@@ -12,10 +12,11 @@ use crate::utils::common;
use crate::formats::sddl_sec::include::*;
use crate::utils::compression::decompress_zlib;
use include::*;
use log::info;
pub fn is_sdboot_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
let header = common::read_file(&file, 0, 32).expect("Failed to read from file.");
let header = common::read_file(&file, 0, 32)?;
let deciph_header = decipher(&header);
let chk = deciph_header.iter().all(|&b| {
@@ -59,7 +60,7 @@ pub fn extract_sdboot(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Bo
}
let key: KeyEntry = KeyEntry::AES(KEYS[key_id as usize]);
println!("File info -\nKey ID: {}\nFile count: {}", secfile_header.key_id(), secfile_header.num_files());
info!("File info -\nKey ID: {}\nFile count: {}", secfile_header.key_id(), secfile_header.num_files());
//create file list
let mut file_list: Vec<FileEntry> = Vec::new();
@@ -84,7 +85,7 @@ pub fn extract_sdboot(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Bo
let target = s.next().unwrap(); //"nand"
let target_filename = s.next().unwrap(); //"nandall.img"
println!("\nSaving {} to {}...", target, target_filename);
info!("\nSaving {} to {}...", target, target_filename);
let output_path = Path::new(&app_ctx.output_dir).join(&target_filename);
let mut out_file = OpenOptions::new().write(true).create(true).truncate(true).open(&output_path)?;
@@ -95,18 +96,18 @@ pub fn extract_sdboot(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Bo
let info_header: InfoListHeader = infofile_reader.read_le()?;
for i in 0..info_header.part_count {
let part_entry: InfoListEntry = infofile_reader.read_le()?;
println!("- ({}/{}) Size: {}, Compressed?: {}", i+1, info_header.part_count, part_entry.out_size, part_entry.is_compressed());
info!("- ({}/{}) Size: {}, Compressed?: {}", i+1, info_header.part_count, part_entry.out_size, part_entry.is_compressed());
let part_file_name = format!("{}{:02x}", target_filename, i); //not sure what happens if it goes over 255
let mut part_data = get_file(&file, &part_file_name, &file_list, &key)?;
if part_entry.is_ciphered() {
println!("-- Deciphering...");
info!("-- Deciphering...");
part_data = decipher(&part_data);
}
if part_entry.is_compressed() {
println!("-- Decompressing...");
info!("-- Decompressing...");
part_data = decompress_zlib(&part_data)?;
}
@@ -114,7 +115,7 @@ pub fn extract_sdboot(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Bo
processed_image_files.insert(part_file_name);
}
println!("--- Saved file!");
info!("--- Saved file!");
}
//extract the rest of the files
@@ -123,13 +124,13 @@ pub fn extract_sdboot(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Bo
continue;
}
println!("\nFile: {} - Size: {}", entry.name, entry.size);
info!("\nFile: {} - Size: {}", entry.name, entry.size);
let file_data = get_file(&file, &entry.name, &file_list, &key)?;
let output_path = Path::new(&app_ctx.output_dir).join(&entry.name);
let mut out_file = OpenOptions::new().read(true).write(true).create(true).open(output_path)?;
out_file.write_all(&file_data)?;
println!("- Saved file!");
info!("- Saved file!");
}
Ok(())
+5 -5
View File
@@ -50,7 +50,7 @@ pub fn decipher(s: &[u8]) -> Vec<u8> {
pub fn decrypt_3des(encrypted_data: &[u8], key_entry: &DesKeyEntry) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut data = encrypted_data.to_vec();
let decryptor = Decryptor::<TdesEde3>::new_from_slices(&key_entry.key, &key_entry.iv).unwrap();
let decryptor = Decryptor::<TdesEde3>::new_from_slices(&key_entry.key, &key_entry.iv).map_err(|e| format!("Invalid 3DES key: {}", e))?;
let out_data = decryptor.decrypt_padded_mut::<NoPadding>(&mut data)
.map_err(|e| format!("!!Decryption error!!: {:?}", e))?;
@@ -142,15 +142,15 @@ pub struct SecHeader {
impl SecHeader {
pub fn key_id(&self) -> u32 {
let string = string_from_bytes(&self.key_id_str_bytes);
string.parse().unwrap()
string.parse::<u32>().expect("Invalid key_id in sec header")
}
pub fn grp_num(&self) -> u32 {
let string = string_from_bytes(&self.grp_num_str_bytes);
string.parse().unwrap()
string.parse::<u32>().expect("Invalid grp_num in sec header")
}
pub fn prg_num(&self) -> u32 {
let string = string_from_bytes(&self.prg_num_str_bytes);
string.parse().unwrap()
string.parse::<u32>().expect("Invalid prg_num in sec header")
}
}
@@ -167,7 +167,7 @@ impl FileHeader {
}
pub fn size(&self) -> u64 {
let string = string_from_bytes(&self.size_str_bytes);
string.parse().unwrap()
string.parse::<u64>().expect("Invalid size in file header")
}
}
+22 -21
View File
@@ -15,10 +15,11 @@ use crate::utils::aes::{decrypt_aes128_cbc_nopad, decrypt_aes128_cbc_pcks7};
use crate::utils::compression::{decompress_zlib};
use include::*;
use util::split_peaks_file;
use log::info;
pub fn is_sddl_sec_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
let header = common::read_file(&file, 0, 32).expect("Failed to read from file.");
let header = common::read_file(&file, 0, 32)?;
let deciph_header = decipher(&header);
if deciph_header.starts_with(b"\x11\x22\x33\x44") {
Ok(Some(Box::new(())))
@@ -43,7 +44,7 @@ fn get_sec_file(mut file: &File, key_entry: &KeyEntry) -> Result<(FileHeader, Ve
} else {
//extra ciphered data before encrypted data, prefixed by size, like "0021XXXXXX PEAKS.T00/12900002"
//this counts into file size but not decrypt size
let extra_size: usize = string_from_bytes(&read_exact(&mut file, 4)?).parse().unwrap();
let extra_size: usize = string_from_bytes(&read_exact(&mut file, 4)?).parse::<usize>().map_err(|e| format!("Parse error: {}", e))?;
let _extra_data = read_exact(&mut file, extra_size)?;
file_header.size() as usize - (extra_size + 4)
@@ -57,10 +58,10 @@ fn get_sec_file(mut file: &File, key_entry: &KeyEntry) -> Result<(FileHeader, Ve
//extra info in enc data, like "0021XXXXXX PEAKS.T00/12900002000000571800"
//part before size looks to be a duplicate of previous extra data, probably for signing purpose, size used for unpad
let extra_size: usize = string_from_bytes(&read_exact(&mut data_rdr, 4)?).parse().unwrap();
let extra_size: usize = string_from_bytes(&read_exact(&mut data_rdr, 4)?).parse::<usize>().map_err(|e| format!("Parse error: {}", e))?;
let _extra_data = read_exact(&mut data_rdr, extra_size + 4)?;
let data_size: usize = string_from_bytes(&read_exact(&mut data_rdr, 12)?).parse().unwrap();
let data_size: usize = string_from_bytes(&read_exact(&mut data_rdr, 12)?).parse::<usize>().map_err(|e| format!("Parse error: {}", e))?;
read_exact(&mut data_rdr, data_size)?
};
@@ -77,16 +78,16 @@ fn parse_tdi_to_modules(tdi_data: Vec<u8>) -> Result<Vec<TdiTgtInf>, Box<dyn std
return Err(format!("Unsupported TDI format version {}! (The supported version is {})", tdi_header.format_version, SUPPORTED_TDI_VERSION).into());
}
println!("[TDI] Group count: {}", tdi_header.num_of_group);
info!("[TDI] Group count: {}", tdi_header.num_of_group);
let mut modules: Vec<TdiTgtInf> = Vec::new();
for _i in 0..tdi_header.num_of_group {
let group_head: TdiGroupHead = tdi_reader.read_be()?;
println!("[TDI] Group ID: {}, Target count: {}", group_head.group_id, group_head.num_of_target);
info!("[TDI] Group ID: {}, Target count: {}", group_head.group_id, group_head.num_of_target);
for _i in 0..group_head.num_of_target {
let tgt_inf: TdiTgtInf = tdi_reader.read_be()?;
println!("[TDI] - {}, Target ID: {}, Segment count: {}, Version: {}",
info!("[TDI] - {}, Target ID: {}, Segment count: {}, Version: {}",
tgt_inf.module_name(), tgt_inf.target_id, tgt_inf.num_of_txx, tgt_inf.version_string());
//push unique modules
@@ -106,7 +107,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
let mut secfile_hdr_reader = Cursor::new(decipher(&read_exact(&mut file, 32)?));
let secfile_header: SecHeader = secfile_hdr_reader.read_be()?;
println!("File info -\nKey ID: {}\nGroup count: {}\nModule file count: {}", secfile_header.key_id(), secfile_header.grp_num(), secfile_header.prg_num());
info!("File info -\nKey ID: {}\nGroup count: {}\nModule file count: {}", secfile_header.key_id(), secfile_header.grp_num(), secfile_header.prg_num());
//by knowing that the first file is always SDIT.FDI, find key(and mode)
let try_hdr = read_exact(&mut file, 0x20)?;
@@ -116,7 +117,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
//for new, key will always be the same
if let Ok(dec) = decrypt_aes128_cbc_pcks7(&try_hdr, &NEW_KEY.key, &NEW_KEY.iv) {
if dec.starts_with(TDI_FILENAME.as_bytes()) {
println!("- New type detected\n");
info!("- New type detected\n");
key = Some(KeyEntry::AESPcks7(NEW_KEY));
}
}
@@ -125,7 +126,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
for key_entry in OLD_KEYS_AES {
let dec = decrypt_aes128_cbc_nopad(&try_hdr, &key_entry.key, &key_entry.iv)?;
if dec.starts_with(TDI_FILENAME.as_bytes()) {
println!("- Old type detected with AES key={}, iv={}\n", hex::encode(key_entry.key) ,hex::encode(key_entry.iv));
info!("- Old type detected with AES key={}, iv={}\n", hex::encode(key_entry.key) ,hex::encode(key_entry.iv));
key = Some(KeyEntry::AES(key_entry));
break
}
@@ -136,7 +137,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
for key_entry in OLD_KEYS_DES {
let dec = decrypt_3des(&try_hdr, &key_entry)?;
if dec.starts_with(TDI_FILENAME.as_bytes()) {
println!("- Old type detected with DES key={}, iv={}\n", hex::encode(key_entry.key) ,hex::encode(key_entry.iv));
info!("- Old type detected with DES key={}, iv={}\n", hex::encode(key_entry.key) ,hex::encode(key_entry.iv));
key = Some(KeyEntry::DES(key_entry));
break
}
@@ -154,7 +155,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
file.seek(SeekFrom::Start(0x20))?;
let (tdi_file, tdi_data) = get_sec_file(&file, &key)?;
println!("[TDI] Name: {}, Size: {}", tdi_file.name(), tdi_file.size());
info!("[TDI] Name: {}, Size: {}", tdi_file.name(), tdi_file.size());
if save_extra { //Save SDIT
let mut out_file = OpenOptions::new().write(true).create(true).open(Path::new(&app_ctx.output_dir).join(tdi_file.name()))?;
out_file.write_all(&tdi_data)?;
@@ -168,7 +169,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
//get info files, each info file belongs to its respecitve group in the TDI
for i in 0..secfile_header.grp_num() {
let (info_file, info_data) = get_sec_file(&file, &key)?;
println!("\n[INFO] ID: {}, Name: {}, Size: {}", i, info_file.name(), info_file.size());
info!("\n[INFO] ID: {}, Name: {}, Size: {}", i, info_file.name(), info_file.size());
if !info_file.name().ends_with(INFO_FILE_EXTENSION) {
return Err(format!("Info file {} does not have the expected extension {}!", info_file.name(), INFO_FILE_EXTENSION).into());
}
@@ -177,12 +178,12 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
out_file.write_all(&info_data)?;
}
//print info file
println!("{}", String::from_utf8_lossy(&info_data));
info!("{}", String::from_utf8_lossy(&info_data));
}
//parse module data
for (i, module) in modules.iter().enumerate(){
println!("\nModule #{}/{} - {}, Target ID: {}, Segment count: {}, Version: {}",
info!("\nModule #{}/{} - {}, Target ID: {}, Segment count: {}, Version: {}",
i+1, &modules.len(), module.module_name(), module.target_id, module.num_of_txx, module.version_string());
let mut final_out_path: Option<PathBuf> = None;
@@ -192,7 +193,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
if !module_file.name().starts_with(&module.module_name()) {
return Err(format!("Module file {} does not start with the module's name: {}!", module_file.name(), module.module_name()).into());
}
println!(" Segment #{}/{} - Name: {}, Size: {}", i+1, module.num_of_txx, module_file.name(), module_file.size());
info!(" Segment #{}/{} - Name: {}, Size: {}", i+1, module.num_of_txx, module_file.name(), module_file.size());
let mut module_reader = Cursor::new(module_data);
let _com_header: ModuleComHeader = module_reader.read_be()?;
@@ -203,23 +204,23 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
let module_header: ModuleHeader = module_reader.read_be()?;
let mut module_data = read_exact(&mut module_reader, module_header.cmp_size as usize)?;
if module_header.is_ciphered() {
println!(" - Deciphering...");
info!(" - Deciphering...");
module_data = decipher(&module_data);
}
if module_header.is_compressed() {
println!(" - Decompressing...");
info!(" - Decompressing...");
module_data = decompress_zlib(&module_data)?;
}
let mut content_reader = Cursor::new(module_data);
let content_header: ContentHeader = content_reader.read_be()?;
println!(" --> 0x{:X} @ 0x{:X}", content_header.size, content_header.dest_offset());
info!(" --> 0x{:X} @ 0x{:X}", content_header.size, content_header.dest_offset());
let output_path: PathBuf;
if content_header.has_subfile() {
let sub_filename_bytes = read_exact(&mut content_reader, 0x100)?;
let sub_filename = common::string_from_bytes(&sub_filename_bytes);
println!(" --> {}", sub_filename);
info!(" --> {}", sub_filename);
let sub_folder_path = Path::new(&app_ctx.output_dir).join(module.module_name());
fs::create_dir_all(&sub_folder_path)?;
@@ -238,7 +239,7 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
}
if app_ctx.has_option("sddl_sec:split_peaks") && module.module_name() == "PEAKS" {
println!("\n- Splitting PEAKS");
info!("\n- Splitting PEAKS");
if let Some(ref path) = final_out_path {
split_peaks_file(path, &app_ctx.output_dir, !app_ctx.has_option("sddl_sec:no_decomp_peaks"))?;
}
+7 -9
View File
@@ -3,6 +3,7 @@ use crate::utils::common;
use std::{fs::{self, File, OpenOptions}, io::{Cursor, Seek, SeekFrom, Write}, path::{Path, PathBuf}};
use crate::utils::compression::decompress_zlib;
use log::info;
pub fn split_peaks_file(path: &PathBuf, out_path: &PathBuf, do_decomp: bool) -> Result<(), Box<dyn std::error::Error>> {
let mut file = File::open(path)?;
@@ -11,7 +12,7 @@ pub fn split_peaks_file(path: &PathBuf, out_path: &PathBuf, do_decomp: bool) ->
let args_bytes = common::read_file(&mut file, 0, 0x210)?;
if !args_bytes.starts_with(b"D50 ") {
println!("- Splitting PEAKS is not supported on this file...");
info!("- Splitting PEAKS is not supported on this file...");
return Ok(());
}
let args = common::string_from_bytes(&args_bytes[16..]);
@@ -57,7 +58,7 @@ pub fn split_peaks_file(path: &PathBuf, out_path: &PathBuf, do_decomp: bool) ->
}
let root = root.ok_or("Failed to get root partition!")?;
println!("Root - {}", root);
info!("Root - {}", root);
let root_index = parts.iter().position(|(n, _s, _f)| n == &root).ok_or("Root partition not found in partition list!")?;
let mut tsize: u64 = 0;
@@ -69,7 +70,7 @@ pub fn split_peaks_file(path: &PathBuf, out_path: &PathBuf, do_decomp: bool) ->
break
}
println!("- {} - Size: {}{}", part_name, part_size, if let Some(part_flag) = part_flag {format!(", Flag: {}", part_flag)} else {format!("")});
info!("- {} - Size: {}{}", part_name, part_size, if let Some(part_flag) = part_flag {format!(", Flag: {}", part_flag)} else {format!("")});
tsize += part_size;
@@ -79,7 +80,7 @@ pub fn split_peaks_file(path: &PathBuf, out_path: &PathBuf, do_decomp: bool) ->
let data = common::read_exact(&mut file, *part_size as usize)?;
if do_decomp && let Some(part_flag) = part_flag && *part_flag == "c" {
println!("-- Decompressing ...");
info!("-- Decompressing ...");
decompress_part_to_file(&data, &output_path)?;
continue
}
@@ -108,7 +109,7 @@ fn decompress_part_to_file(data: &[u8], out_path: &PathBuf) -> Result<(), Box<dy
let first_blk: u32 = reader.read_type(endianness)?;
let count_blks = (first_blk - 4) / 4;
println!("[INFO] Endianness: {}, Uncompressed size: {}, First block loc: {}, Block count: {}",
info!("[INFO] Endianness: {}, Uncompressed size: {}, First block loc: {}, Block count: {}",
endianness, uncompressed_size, first_blk, count_blks);
reader.seek(SeekFrom::Start(4))?;
@@ -123,9 +124,8 @@ fn decompress_part_to_file(data: &[u8], out_path: &PathBuf) -> Result<(), Box<dy
for (n, blk) in block_locs.iter().enumerate() {
reader.seek(SeekFrom::Start(*blk as u64))?;
print!("\rDecompressing... {}/{}",
info!("Decompressing... {}/{}",
n + 1, count_blks);
std::io::stdout().flush()?;
//special handling for last block since there is no next block to check, just assume 16k
let blk_size = if n == count_blks as usize - 1 {16384} else {block_locs[n + 1] - blk};
@@ -136,7 +136,5 @@ fn decompress_part_to_file(data: &[u8], out_path: &PathBuf) -> Result<(), Box<dy
out_file.write_all(&decompressed)?;
}
println!();
Ok(())
}
+4 -3
View File
@@ -8,6 +8,7 @@ use binrw::BinReaderExt;
use include::*;
use crate::utils::common;
use log::info;
pub fn is_sdimage_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -28,14 +29,14 @@ pub fn extract_sdimage(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
let mut e_i = 0;
while file.stream_position()? < file.metadata()?.len() as u64 {
let entry: EntryHeader = file.read_le()?;
println!("\n#{} - {} ({}), Size: {}, Version: {}.{}.{}.{}, Model ID: {}, Info: {}{} {}",
info!("\n#{} - {} ({}), Size: {}, Version: {}.{}.{}.{}, Model ID: {}, Info: {}{} {}",
e_i+1, entry.target_name(), entry.target_id, entry.size1, entry.version[3], entry.version[2], entry.version[1], entry.version[0], entry.model_id, entry.info(),
if entry.is_encrypted() {" [ENCRYPTED]"} else {""}, if entry.is_empty() {"[EMPTY]"} else {""});
if !entry.is_empty() {
let info = entry.info();
let filename = info.split("FN=\"").nth(1).and_then(|s| s.split('"').next()).unwrap();
println!("- Filename: {}", filename);
info!("- Filename: {}", filename);
let data = common::read_exact(&mut file, entry.size1 as usize)?;
@@ -47,7 +48,7 @@ pub fn extract_sdimage(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), B
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
e_i += 1;
+11 -10
View File
@@ -9,6 +9,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use include::*;
use log::info;
pub struct SlpContext {
variant: SlpVariant,
@@ -39,10 +40,10 @@ pub fn is_slp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn
pub fn extract_slp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<SlpContext>().expect("Missing context");
let ctx = ctx.downcast::<SlpContext>().map_err(|_| "Invalid context type")?;
let meta_header: CommonMetaHeader = file.read_le()?;
println!("File info:\nType: {:?}\nProject name: {}\nFirmware Version: {}\nFirmware Version(USER): {}",
info!("File info:\nType: {:?}\nProject name: {}\nFirmware Version: {}\nFirmware Version(USER): {}",
ctx.variant, meta_header.project_name(), meta_header.firmware_version(), meta_header.user_version());
let num_image;
@@ -50,21 +51,21 @@ pub fn extract_slp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dy
if ctx.variant == SlpVariant::New {
let meta_header_ext: MetaHeaderExtNew = file.read_le()?;
if meta_header_ext.snapshot_included == 0x01 {
println!("Snapshot Image: Included");
println!("S/S Img. Board Version: {}", meta_header_ext.snapshot_board_version())
info!("Snapshot Image: Included");
info!("S/S Img. Board Version: {}", meta_header_ext.snapshot_board_version())
} else {
println!("Snapshot Image: Excluded");
info!("Snapshot Image: Excluded");
}
num_image = meta_header_ext.num_image;
}
else if ctx.variant == SlpVariant::Old {
let meta_header_ext: MetaHeaderExtOld = file.read_le()?;
if meta_header_ext.snapshot_included == 0x01 {
println!("Snapshot Image: Included");
println!("Snapshot Image offset: {}", meta_header_ext.snapshot_entry_offset);
info!("Snapshot Image: Included");
info!("Snapshot Image offset: {}", meta_header_ext.snapshot_entry_offset);
snapshot_entry_offset = Some(meta_header_ext.snapshot_entry_offset);
} else {
println!("Snapshot Image: Excluded");
info!("Snapshot Image: Excluded");
}
num_image = 5; //hardcoded for old variant
}
@@ -95,7 +96,7 @@ pub fn extract_slp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dy
}
for (i, entry) in entries.iter().enumerate() {
println!("\n({}/{}) - Offset: {}, Size: {}, Magic: 0x{:02X}", i+1, &entries.len(), entry.offset, entry.size, entry.magic);
info!("\n({}/{}) - Offset: {}, Size: {}, Magic: 0x{:02X}", i+1, &entries.len(), entry.offset, entry.size, entry.magic);
let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", i+1));
@@ -106,7 +107,7 @@ pub fn extract_slp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dy
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
println!("- Saved file!");
info!("- Saved file!");
}
Ok(())
+10 -8
View File
@@ -12,6 +12,7 @@ use crate::utils::common;
use crate::formats;
use crate::keys;
use include::*;
use log::info;
struct SonyBdpCtx {
encryption_type: EncryptionType,
@@ -50,7 +51,7 @@ pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Bo
pub fn extract_sony_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<SonyBdpCtx>().expect("Missing context");
let ctx = ctx.downcast::<SonyBdpCtx>().map_err(|_| "Invalid context type")?;
//need to decrypt entire file of new aes enc
let mut enc_data = Vec::new();
@@ -58,11 +59,11 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), B
let dec_data = match ctx.encryption_type {
EncryptionType::HexSubst => {
println!("Decrypting with hex substitution...");
info!("Decrypting with hex substitution...");
hex_substitute(&enc_data)
},
EncryptionType::AesOfb((key, iv, key_name)) => {
println!("Decrypting with AES key: {}...", key_name);
info!("Decrypting with AES key: {}...", key_name);
ver_up_decrypt_aes128ofb(&key, &iv, &enc_data)
}
};
@@ -72,7 +73,7 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), B
let mut hdr_reader = Cursor::new(header);
let hdr: Header = hdr_reader.read_le()?;
println!("File info:\nFirmware: {}\nVersion: {}\nDate: {}\nFile size: {}",
info!("File info:\nFirmware: {}\nVersion: {}\nDate: {}\nFile size: {}",
hdr.firmware_name(), hdr.firmware_version(), hdr.date(), hdr.file_size);
let mut last_file_path: Option<PathBuf> = None;
@@ -89,7 +90,7 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), B
continue
}
println!("\n#{} - Offset: {}, Size: {}", i, entry.offset, entry.size);
info!("\n#{} - Offset: {}, Size: {}", i, entry.offset, entry.size);
if i == 0 {
first_entry_offset = entry.offset as u64;
}
@@ -104,7 +105,7 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), B
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
println!("- Saved file!");
info!("- Saved file!");
i += 1;
}
@@ -119,13 +120,14 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), B
options: app_ctx.options.clone(),
dry_run: app_ctx.dry_run,
quiet: app_ctx.quiet,
verbose: app_ctx.verbose,
};
if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&ctx)? {
println!("- MTK BDP file detected!\n");
info!("- MTK BDP file detected!\n");
formats::mtk_bdp::extract_mtk_bdp(&ctx, result)?;
} else {
println!("- Not an MTK BDP file.");
info!("- Not an MTK BDP file.");
}
}
+8 -7
View File
@@ -13,6 +13,7 @@ use binrw::BinReaderExt;
use crate::utils::common;
use include::*;
use tsb_des::decrypt;
use log::info;
struct TsbBinCtx {
key: Option<[u8; 8]>
@@ -44,11 +45,11 @@ pub fn is_tsb_bin_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box
pub fn extract_tsb_bin(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
let ctx = ctx.downcast::<TsbBinCtx>().expect("Missing context");
let ctx = ctx.downcast::<TsbBinCtx>().map_err(|_| "Invalid context type")?;
let mut header = common::read_file(&mut file, 0, 0x400)?;
if let Some(key) = ctx.key { //decrypt header
println!("File is encrypted, using key: {}", hex::encode(&key));
info!("File is encrypted, using key: {}", hex::encode(&key));
header = decrypt(&header, &key);
opt_dump_dec_hdr(app_ctx, &header, "header")?;
}
@@ -56,17 +57,17 @@ pub fn extract_tsb_bin(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
let mut hdr_rdr = Cursor::new(header);
let hdr: Header = hdr_rdr.read_be()?;
println!("File info -\nSize: {}\nEntry count: {}\nBuild no.: {}\nEntry address: 0x{:02x}"
info!("File info -\nSize: {}\nEntry count: {}\nBuild no.: {}\nEntry address: 0x{:02x}"
,hdr.length, hdr.entry_count, hdr.build_no(), hdr.entry_addr);
for (i, entry) in hdr.entries.iter().enumerate() {
println!("\n({}/{}) - {}, Size: {}, Offset: {}, Load address: 0x{:02x}",
info!("\n({}/{}) - {}, Size: {}, Offset: {}, Load address: 0x{:02x}",
i+1, hdr.entry_count, entry.name(), entry.size, entry.offset, entry.load_addr);
let mut data;
if let Some(key) = ctx.key {
let enc_data = common::read_file(&mut file, entry.offset as u64, (entry.size as usize + 7) & !7)?; //read aligned to 8b blocks for decryption
println!("- Decrypting...");
info!("- Decrypting...");
data = decrypt(&enc_data, &key);
data.truncate(entry.size as usize); //discard alignment
@@ -75,7 +76,7 @@ pub fn extract_tsb_bin(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
}
if entry.is_compressed() {
println!("- Decompressing...");
info!("- Decompressing...");
data = decompress_zlib(&data)?;
}
@@ -85,7 +86,7 @@ pub fn extract_tsb_bin(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Bo
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
println!("-- Saved file!");
info!("-- Saved file!");
}
Ok(())
+15 -14
View File
@@ -8,6 +8,7 @@ use crate::utils::aes::decrypt_aes128_ecb;
use crate::keys;
use include::*;
use log::info;
pub struct VestelCtx {
pub is_encrypted: bool,
@@ -91,7 +92,7 @@ pub fn extract_vestel(
app_ctx: &AppContext,
ctx: Box<dyn Any>,
) -> Result<(), Box<dyn std::error::Error>> {
let ctx = ctx.downcast::<VestelCtx>().expect("Missing context");
let ctx = ctx.downcast::<VestelCtx>().map_err(|_| "Invalid context type")?;
match ctx.variant {
VestelVariant::Mb230 => extract_mb230(app_ctx, &ctx),
@@ -116,8 +117,8 @@ fn extract_standard(
let key: [u8; 16] = key_vec.try_into().map_err(|_| "Invalid AES key length")?;
let (final_data, decrypted_path) = if ctx.is_encrypted {
println!("Encrypted Vestel firmware detected");
println!("Using key: {} ({})", "VESTEL", key_hex);
info!("Encrypted Vestel firmware detected");
info!("Using key: {} ({})", "VESTEL", key_hex);
let dec = decrypt_aes128_ecb(&key, &data)?;
@@ -129,12 +130,12 @@ fn extract_standard(
(dec, Some(output_path))
} else {
println!("Detected Vestel firmware (unencrypted)");
info!("Detected Vestel firmware (unencrypted)");
(data, None)
};
let partitions = VESTEL_PARTITIONS;
println!("\nPartition count: {}", partitions.len());
info!("\nPartition count: {}", partitions.len());
fs::create_dir_all(&app_ctx.output_dir)?;
@@ -143,14 +144,14 @@ fn extract_standard(
let end = start.saturating_add(*size);
if start >= final_data.len() {
println!("Skipping {} (out of range)", name);
info!("Skipping {} (out of range)", name);
continue;
}
let end = end.min(final_data.len());
let segment = &final_data[start..end];
println!(
info!(
"\n{} - Offset: 0x{:X}, Size: 0x{:X}",
name, offset, size
);
@@ -160,7 +161,7 @@ fn extract_standard(
fs::write(&output_path, segment)?;
println!("- Saved {}.bin", name);
info!("- Saved {}.bin", name);
}
if let Some(path) = decrypted_path {
@@ -181,11 +182,11 @@ fn extract_mb230(
let file_size = file.metadata()?.len() as usize;
let data = common::read_exact(&mut file, file_size)?;
println!("Detected Vestel MB230 firmware (Novatek NT72673)");
println!("File size: {} bytes ({:.2} MB)", file_size, file_size as f64 / 1024.0 / 1024.0);
info!("Detected Vestel MB230 firmware (Novatek NT72673)");
info!("File size: {} bytes ({:.2} MB)", file_size, file_size as f64 / 1024.0 / 1024.0);
let partitions = MB230_PARTITIONS;
println!("\nPartition count: {}", partitions.len());
info!("\nPartition count: {}", partitions.len());
fs::create_dir_all(&app_ctx.output_dir)?;
@@ -194,14 +195,14 @@ fn extract_mb230(
let end = start.saturating_add(*size);
if start >= file_size {
println!("Skipping {} (out of range)", name);
info!("Skipping {} (out of range)", name);
continue;
}
let end = end.min(file_size);
let segment = &data[start..end];
println!(
info!(
"\n{} - Offset: 0x{:X}, Size: 0x{:X} ({} bytes)",
name, offset, size, segment.len()
);
@@ -211,7 +212,7 @@ fn extract_mb230(
fs::write(&output_path, segment)?;
println!("- Saved {}.bin", name);
info!("- Saved {}.bin", name);
}
Ok(())
+42 -23
View File
@@ -5,7 +5,7 @@ mod utils;
use clap::Parser;
use std::path::PathBuf;
use std::io::{self, Seek, SeekFrom};
use std::io::{self, Seek, SeekFrom, Write};
use std::fs::{self, File};
use crate::formats::{Format, get_registry};
use crate::error::UnixtractError;
@@ -43,7 +43,7 @@ struct Args {
#[arg(short, long)]
quiet: bool,
/// Verbose mode — show detailed progress information
/// Verbose mode — show detailed progress information (use -vv for trace)
#[arg(short, long, action = clap::ArgAction::Count)]
verbose: u8,
}
@@ -59,6 +59,7 @@ pub struct AppContext {
pub options: Vec<String>,
pub dry_run: bool,
pub quiet: bool,
pub verbose: u8,
}
impl AppContext {
@@ -81,15 +82,31 @@ impl AppContext {
}
}
/// Clear the terminal screen
fn clear_terminal() {
// ANSI escape sequence to clear screen and move cursor to top-left
eprint!("\x1B[2J\x1B[H");
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();
// Clear the terminal on execution
if !args.quiet {
clear_terminal();
}
// Initialize logger based on verbosity level
// Default (no flags): show info-level output (normal progress)
// -q: errors only
// -v: info (same as default, explicit)
// -vv: debug level (more details)
// -vvv: trace level (everything)
let log_level = if args.quiet {
"error"
} else {
match args.verbose {
0 => "warn",
0 => "info",
1 => "info",
2 => "debug",
_ => "trace",
@@ -98,22 +115,34 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(log_level))
.format_timestamp(None)
.format_target(false)
.format_module_path(false)
.format(|buf, record| {
// Clean format: just the message for info/debug/trace
// Add level prefix for warn/error so they stand out
match record.level() {
log::Level::Error => writeln!(buf, "[ERROR] {}", record.args()),
log::Level::Warn => writeln!(buf, "[WARN] {}", record.args()),
log::Level::Info => writeln!(buf, "{}", record.args()),
log::Level::Debug => writeln!(buf, "[DBG] {}", record.args()),
log::Level::Trace => writeln!(buf, "[TRC] {}", record.args()),
}
})
.init();
// Handle --list-formats
if args.list_formats {
let formats = get_registry();
println!("Supported formats ({} total):", formats.len());
eprintln!("Supported formats ({} total):", formats.len());
for (i, fmt) in formats.iter().enumerate() {
println!(" {:2}. {}", i + 1, fmt.name);
eprintln!(" {:2}. {}", i + 1, fmt.name);
}
return Ok(());
}
log::info!("unixtract Firmware extractor v{}", env!("CARGO_PKG_VERSION"));
log::info!("unixtract v{}", env!("CARGO_PKG_VERSION"));
let target_path_str = args.input_target.as_deref().unwrap_or("");
log::info!("Input target: {}", target_path_str);
let target_path = PathBuf::from(target_path_str);
let output_path_str = if let Some(ref out) = args.output_directory {
@@ -123,7 +152,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.and_then(|s| s.to_str())
.ok_or_else(|| UnixtractError::Other("Invalid input file name".to_string()))?)
};
log::info!("Output directory: {}", output_path_str);
let output_directory_path = PathBuf::from(&output_path_str);
if output_directory_path.exists() {
@@ -131,7 +159,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let is_empty = fs::read_dir(&output_directory_path)?.next().is_none();
if !is_empty && !args.quiet {
log::warn!("Output folder already exists and is NOT empty! Files may be overwritten.");
eprintln!("Warning: Output folder already exists and is NOT empty! Files may be overwritten!");
eprintln!("Press Enter if you want to continue...");
io::stdin().read_line(&mut String::new())?;
}
@@ -149,6 +176,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
options: args.options,
dry_run: args.dry_run,
quiet: args.quiet,
verbose: args.verbose,
};
} else if target_path.is_dir() {
app_ctx = AppContext {
@@ -157,25 +185,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
options: args.options,
dry_run: args.dry_run,
quiet: args.quiet,
verbose: args.verbose,
};
} else {
return Err("Invalid input path!".into());
}
let formats: Vec<Format> = get_registry();
log::info!("Loaded {} formats", formats.len());
for format in formats {
if let Some(ctx) = (format.detector_func)(&app_ctx)? {
log::info!("{} detected!", format.name);
if !app_ctx.quiet {
println!("\n{} detected!", format.name);
}
log::info!("\n{} detected!", format.name);
if app_ctx.dry_run {
if !app_ctx.quiet {
println!("Dry run — skipping extraction.");
}
log::info!("Dry run — skipping extraction.");
return Ok(());
}
@@ -187,15 +210,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
(format.extractor_func)(&app_ctx, ctx)?;
// Extractor returned with no error
if !app_ctx.quiet {
println!("\nExtraction finished! Saved extracted files to {}", output_path_str);
}
log::info!("\nExtraction finished! Saved extracted files to {}", output_path_str);
return Ok(());
}
}
if !app_ctx.quiet {
println!("\nInput format not recognized!");
}
log::warn!("\nInput format not recognized!");
Err("Unrecognized input format".into())
}
+5 -4
View File
@@ -1,12 +1,12 @@
use std::fs::{File};
use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom};
pub fn read_file(mut file: &File, offset: u64, size: usize) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
file.seek(SeekFrom::Start(offset))?;
let mut buffer = vec![0u8; size];
let _bytes_read = file.read(&mut buffer)?;
file.read_exact(&mut buffer)?;
// reset seek (!
// Reset seek position back to the read offset
file.seek(SeekFrom::Start(offset))?;
Ok(buffer)
}
@@ -20,4 +20,5 @@ pub fn read_exact<R: Read>(reader: &mut R, size: usize) -> io::Result<Vec<u8>> {
pub fn string_from_bytes(buf: &[u8]) -> String {
let end = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
String::from_utf8_lossy(&buf[..end]).to_string()
}
}
+1 -1
View File
@@ -29,7 +29,7 @@ pub fn decompress_gzip_get_filename(compressed_data: &[u8]) -> Result<(Vec<u8>,
let mut decoder = GzDecoder::new(compressed_data);
let mut filename: Option<String> = None;
if let Some(filename_bytes) = decoder.header().unwrap().filename() {
if let Some(filename_bytes) = decoder.header().and_then(|h| h.filename()) {
filename = Some(common::string_from_bytes(filename_bytes));
}
+2 -1
View File
@@ -1,6 +1,7 @@
use std::{fs::{self, OpenOptions}, io::Write, path::Path};
use crate::AppContext;
use log::info;
pub fn opt_dump_dec_hdr(app_ctx: &AppContext, data: &[u8], name: &str) -> Result<(), Box<dyn std::error::Error>> {
if !app_ctx.has_option("dump_dec_hdrs") {
@@ -14,7 +15,7 @@ pub fn opt_dump_dec_hdr(app_ctx: &AppContext, data: &[u8], name: &str) -> Result
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?;
println!("[i] Saved {} to {}", name, filename);
info!("[i] Saved {} to {}", name, filename);
Ok(())
}
+2 -1
View File
@@ -5,6 +5,7 @@ use std::io::{Cursor, Write};
use simd_adler32::adler32;
use crate::utils::common;
use log::info;
#[derive(BinRead)]
struct LzopHeader {
@@ -48,7 +49,7 @@ pub fn unlzop_to_file(data: &[u8], file_path: PathBuf) -> Result<(), Box<dyn std
}
let segment_header: SegmentHeader = data_reader.read_be()?;
if segment_header.compressed_size > segment_header.uncompressed_size {
println!("{:?}", segment_header);
info!("{:?}", segment_header);
}
if segment_header.uncompressed_size == 0 {
break