diff --git a/README.md b/README.md index 90e234e..f0f34f8 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Arguments: If an output folder is not provided, extracted files will be saved in folder `_`. Options: `-o, --options ` - Format specific or global(for all formats that implement it) options, see the list below for format specific options. You can use this multiple times to activate multiple options. +## Global options +`dump_dec_hdrs` - For formats with an encrypted header - dump the decrypted header(s). # Supported formats ## Amlogic burning image @@ -39,12 +41,16 @@ Options: ## EPK v2 **Used in:** LG TVs since ~2010 **Notes:** **Depends on keys** - see keys.rs (most common keys should be included) -**Thanks to:** https://github.com/openlgtv/epk2extract +**Thanks to:** https://github.com/openlgtv/epk2extract +**Options:** +※ Support `dump_dec_hdrs` option ## EPK v3 **Used in:** LG webOS-based TVs **Notes:** **Depends on keys** - see keys.rs -**Thanks to:** https://github.com/openlgtv/epk2extract +**Thanks to:** https://github.com/openlgtv/epk2extract +**Options:** +※ Support `dump_dec_hdrs` option ## Funai BDP **Used in:** Funai & Funai-made Philips Blu-Ray player/HTS (USA market) @@ -69,6 +75,7 @@ Options: **Options:** `msd10:save_cmac` - Save CMAC data for files that is skipped by default. `msd:print_ouith` - Prints the entire parsed OUITH header. +※ Support `dump_dec_hdrs` option ## MSD 1.1 **Used in:** Samsung TVs 2016+ @@ -76,10 +83,13 @@ Options: **Thanks to:** https://github.com/bugficks/msddecrypt **Options:** `msd:print_ouith` - Prints the entire parsed OUITH header. +※ Support `dump_dec_hdrs` option ## MStar upgrade bin **Used in:** Many MStar-based TVs (Hisense, Toshiba...) **Notes:** All files should be supported, includes lzop, lz4, lzma, sparse_write support +**Options:** +※ Support `dump_dec_hdrs` option (will save the script) ## MediaTek BDP **Used in:** Many MediaTek-based Blu-Ray players (LG, Samsung, Philips, Panasonic...) @@ -90,12 +100,14 @@ Options: **Notes:** **Depends on keys** - see keys.rs (Keys for Philips and Sony included) **Options:** `mtk_pkg:no_del_comp` - Don't delete LZHS compressed partition file after decompressing. +※ Support `dump_dec_hdrs` option ## MediaTek PKG (Old) **Used in:** Older MediaTek-based TVs (Philips, Sony, Hisense...) **Notes:** All files should be supported, decryption + decompression **Options:** `mtk_pkg:no_del_comp` - Don't delete LZHS compressed partition file after decompressing. +※ Support `dump_dec_hdrs` option ## MediaTek PKG **Used in:** MediaTek-based TVs (Sony, Philips, Panasonic, Sharp...) @@ -103,6 +115,7 @@ Options: **Thanks to:** https://github.com/openlgtv/epk2extract **Options:** `mtk_pkg:no_del_comp` - Don't delete LZHS compressed partition file after decompressing. +※ Support `dump_dec_hdrs` option ## Novatek PKG (NFWB) **Used in:** Some older Novatek-based TVs (LG, Philips) @@ -116,7 +129,8 @@ Options: **Used in:** Panasonic Blu-Ray Players and Recorders **Notes:** **Depends on keys** - see keys.rs (Included keys should work for 99% of players released in and before 2014, and some released in 2018), Note that there is currently an issue with MAIN in some very ancient files not extracting correctly. **Options:** -`pana_dvd:split_main` - Automatically split the MAIN module into seperate partitions. +`pana_dvd:split_main` - Automatically split the MAIN module into seperate partitions. +※ Support `dump_dec_hdrs` option ## Philips UPG (Autorun.upg, 2SWU3TXV) **Used in:** Philips pre-TPVision TVs 200?-2013 diff --git a/src/formats/epk2/mod.rs b/src/formats/epk2/mod.rs index ca1148f..62f5996 100644 --- a/src/formats/epk2/mod.rs +++ b/src/formats/epk2/mod.rs @@ -8,6 +8,7 @@ use std::io::{Write, Seek, SeekFrom, Cursor}; use binrw::BinReaderExt; use crate::utils::common; +use crate::utils::global::opt_dump_dec_hdr; use crate::keys; use crate::formats::epk::{decrypt_aes_ecb_auto, find_key}; use include::*; @@ -45,6 +46,8 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< println!("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")?; + } else { return Err("No valid key found!".into()); } diff --git a/src/formats/epk3/mod.rs b/src/formats/epk3/mod.rs index 804b252..00a09d3 100644 --- a/src/formats/epk3/mod.rs +++ b/src/formats/epk3/mod.rs @@ -8,6 +8,7 @@ use std::io::{Write, Cursor}; use binrw::BinReaderExt; use crate::utils::common; +use crate::utils::global::opt_dump_dec_hdr; use crate::keys; use crate::formats::epk::{decrypt_aes_ecb_auto, find_key}; use include::*; @@ -33,6 +34,7 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< matching_key = Some(key_bytes); _header_signature = &stored_header[..128]; header = decrypt_aes_ecb_auto(matching_key.as_ref().unwrap(), &stored_header[128..])?; + opt_dump_dec_hdr(app_ctx, &header, "header")?; //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")? { @@ -40,6 +42,7 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< matching_key = Some(key_bytes); _header_signature = &stored_header[..256]; header = decrypt_aes_ecb_auto(matching_key.as_ref().unwrap(), &stored_header[256..])?; + opt_dump_dec_hdr(app_ctx, &header, "header")?; new_type = true; } else { @@ -72,6 +75,8 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< //PKG INFO let pkg_info_encrypted = common::read_exact(&mut file, hdr.package_info_size as usize)?; let pkg_info = decrypt_aes_ecb_auto(matching_key_bytes, &pkg_info_encrypted)?; + opt_dump_dec_hdr(app_ctx, &pkg_info, "pkg_info")?; + let mut pkg_info_reader = Cursor::new(pkg_info); let pkg_info_hdr: PkgInfoHeader = pkg_info_reader.read_le()?; diff --git a/src/formats/msd10/mod.rs b/src/formats/msd10/mod.rs index 5511267..e71241c 100644 --- a/src/formats/msd10/mod.rs +++ b/src/formats/msd10/mod.rs @@ -8,6 +8,7 @@ use std::io::{Write, Seek, SeekFrom}; use binrw::BinReaderExt; use crate::utils::common; +use crate::utils::global::opt_dump_dec_hdr; use crate::keys; use crate::formats::msd::{decrypt_aes_salted_old, decrypt_aes_salted_tizen, decrypt_aes_tizen}; use crate::formats::msd::msd_ouith_parser_old::{parse_ouith_blob}; @@ -82,6 +83,8 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box //parse TOC if firmware_type == "tizen" { let toc = decrypt_aes_salted_tizen(&toc_data, &passphrase_bytes)?; + opt_dump_dec_hdr(app_ctx, &toc, "toc")?; + let (items, info) = parse_blob_1_8(&toc, print_ouith_tree)?; if let Some(info) = info { @@ -122,6 +125,8 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box } else if firmware_type == "old" { let toc = decrypt_aes_salted_old(&toc_data, &passphrase_bytes)?; + opt_dump_dec_hdr(app_ctx, &toc, "toc")?; + let (items, info) = parse_ouith_blob(&toc, print_ouith_tree)?; if let Some(info) = info { diff --git a/src/formats/msd11/mod.rs b/src/formats/msd11/mod.rs index 8325965..8da41bc 100644 --- a/src/formats/msd11/mod.rs +++ b/src/formats/msd11/mod.rs @@ -8,6 +8,7 @@ use std::io::Write; use binrw::BinReaderExt; use crate::utils::common; +use crate::utils::global::opt_dump_dec_hdr; 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}; @@ -74,6 +75,8 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let toc_data = common::read_file(&file, toc_offset as u64, toc_size as usize)?; let toc = decrypt_aes_salted_tizen(&toc_data, &passphrase_bytes)?; + opt_dump_dec_hdr(app_ctx, &toc, "toc")?; + let (items, info) = parse_blob_1_9(&toc, print_ouith_tree)?; if let Some(info) = info { diff --git a/src/formats/mstar/mod.rs b/src/formats/mstar/mod.rs index 28cb790..54d94ca 100644 --- a/src/formats/mstar/mod.rs +++ b/src/formats/mstar/mod.rs @@ -7,6 +7,7 @@ use std::path::Path; use std::io::Write; use crate::utils::common; +use crate::utils::global::opt_dump_dec_hdr; use crate::utils::compression::{decompress_lzma, decompress_lz4}; use crate::utils::lzop::{unlzop_to_file}; use crate::utils::sparse::{unsparse_to_file}; @@ -50,6 +51,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box 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; diff --git a/src/formats/mtk_pkg/mod.rs b/src/formats/mtk_pkg/mod.rs index 6743cf9..99538b6 100644 --- a/src/formats/mtk_pkg/mod.rs +++ b/src/formats/mtk_pkg/mod.rs @@ -10,6 +10,7 @@ use std::io::{Write, Cursor, Seek, SeekFrom}; use binrw::BinReaderExt; use crate::utils::common; +use crate::utils::global::opt_dump_dec_hdr; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::keys; use lzhs::{decompress_lzhs_fs_file2file}; @@ -47,6 +48,8 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Box) -> Result<(), Bo let file_size = file.metadata()?.len(); let header = ctx.decrypted_header; + opt_dump_dec_hdr(app_ctx, &header, "header")?; + let mut hdr_reader = Cursor::new(header); let hdr: Header = hdr_reader.read_le()?; diff --git a/src/formats/mtk_pkg_new/mod.rs b/src/formats/mtk_pkg_new/mod.rs index 7b926ce..324f88c 100644 --- a/src/formats/mtk_pkg_new/mod.rs +++ b/src/formats/mtk_pkg_new/mod.rs @@ -8,6 +8,7 @@ use std::io::{Write, Cursor, Seek, SeekFrom}; use binrw::BinReaderExt; use crate::utils::common; +use crate::utils::global::opt_dump_dec_hdr; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::keys; use crate::formats::mtk_pkg::lzhs::{decompress_lzhs_fs_file2file}; @@ -55,6 +56,7 @@ pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Box) -> Result<() let key_array = ctx.matching_key_key; let iv_array = ctx.matching_key_iv; let header = ctx.decrypted_header; + opt_dump_dec_hdr(app_ctx, &header, "header")?; let mut hdr_reader = Cursor::new(header); let hdr: Header = hdr_reader.read_le()?; diff --git a/src/formats/mtk_pkg_old/mod.rs b/src/formats/mtk_pkg_old/mod.rs index 9e7ca9f..35022f6 100644 --- a/src/formats/mtk_pkg_old/mod.rs +++ b/src/formats/mtk_pkg_old/mod.rs @@ -5,42 +5,50 @@ use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; -use std::io::{Write, Cursor, Seek}; +use std::io::{Cursor, Seek, SeekFrom, Write}; use binrw::BinReaderExt; use crate::utils::common; +use crate::utils::global::opt_dump_dec_hdr; use crate::formats::mtk_pkg::lzhs::{decompress_lzhs_fs_file2file_old}; use crate::formats::mtk_pkg::include::{PartEntry, MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC}; use mtk_crypto::{decrypt}; use include::*; +pub struct MtkPkgOldContext { + header_offset: u64, +} + pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result>, Box> { - let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; + let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?; let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK)); if &header[4..12] == MTK_HEADER_MAGIC { - Ok(Some(Box::new(()))) + Ok(Some(Box::new(MtkPkgOldContext {header_offset: 0}))) } else if &header[68..76] == MTK_HEADER_MAGIC { - //check for 64 byte additional header used in some Sony and Philips firmwares and skip it - file.seek(std::io::SeekFrom::Start(64))?; - Ok(Some(Box::new(()))) + //check for 64 byte additional header used in some Sony and Philips firmwares + Ok(Some(Box::new(MtkPkgOldContext {header_offset: 64}))) } else if &header[132..140] == MTK_HEADER_MAGIC { - //check for 128 byte additional header used in some Philips firmwares and skip it - file.seek(std::io::SeekFrom::Start(128))?; - Ok(Some(Box::new(()))) + //check for 128 byte additional header used in some Philips firmwares + Ok(Some(Box::new(MtkPkgOldContext {header_offset: 128}))) } else { Ok(None) } } -pub fn extract_mtk_pkg_old(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { +pub fn extract_mtk_pkg_old(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; + let ctx = ctx.downcast::().expect("Missing context"); let no_del_comp = app_ctx.options.iter().any(|e| e == "mtk_pkg:no_del_comp"); let file_size = file.metadata()?.len(); + + file.seek(SeekFrom::Start(ctx.header_offset))?; let encrypted_header = common::read_exact(&mut file, HEADER_SIZE)?; let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK)); + opt_dump_dec_hdr(app_ctx, &header, "header")?; + let mut hdr_reader = Cursor::new(header); let hdr: Header = hdr_reader.read_le()?; diff --git a/src/formats/pana_dvd/mod.rs b/src/formats/pana_dvd/mod.rs index a509b99..1f28271 100644 --- a/src/formats/pana_dvd/mod.rs +++ b/src/formats/pana_dvd/mod.rs @@ -12,6 +12,7 @@ use binrw::BinReaderExt; use crate::keys; use crate::utils::common; +use crate::utils::global::opt_dump_dec_hdr; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::utils::compression::{decompress_gzip_get_filename}; use pana_dvd_crypto::{decrypt_data}; @@ -114,7 +115,10 @@ fn extract_file(app_ctx: &AppContext, file_reader: &mut Cursor>, offset: file_reader.seek(SeekFrom::Start(offset + base_offset))?; let enc_header = common::read_exact(file_reader, MAX_HEADER_SIZE)?; - let mut hdr_reader = Cursor::new(decrypt_data(&enc_header, &key)); + let dec_header = decrypt_data(&enc_header, &key); + opt_dump_dec_hdr(app_ctx, &dec_header, "header")?; + + let mut hdr_reader = Cursor::new(dec_header); let mut modules: Vec = Vec::new(); let split_main = app_ctx.options.iter().any(|e| e == "pana_dvd:split_main"); diff --git a/src/utils.rs b/src/utils.rs index 5eec0cd..b5ccb24 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,4 +2,5 @@ pub mod common; pub mod aes; pub mod lzop; pub mod sparse; -pub mod compression; \ No newline at end of file +pub mod compression; +pub mod global; \ No newline at end of file diff --git a/src/utils/global.rs b/src/utils/global.rs new file mode 100644 index 0000000..65820dd --- /dev/null +++ b/src/utils/global.rs @@ -0,0 +1,20 @@ +use std::{fs::{self, OpenOptions}, io::Write, path::Path}; + +use crate::AppContext; + +pub fn opt_dump_dec_hdr(app_ctx: &AppContext, data: &[u8], name: &str) -> Result<(), Box> { + if !app_ctx.options.iter().any(|e| e == "dump_dec_hdrs") { + return Ok(()) + } + + let filename = format!("_{}.bin", name); + let output_path = Path::new(&app_ctx.output_dir).join(&filename); + 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(&data)?; + + println!("[i] Saved {} to {}", name, filename); + + Ok(()) +} \ No newline at end of file