mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-15 21:00:03 +02:00
add funai_mstar extractor, mstar: fix BUG from previous commit (not increment j counter when skipping by unknown dest), auto delete decrypted file in mstar_secure_old (new option to not do so)
This commit is contained in:
@@ -63,6 +63,10 @@ Options:
|
|||||||
**Used in:** Funai & Funai-made Philips Blu-Ray player/HTS (USA market)
|
**Used in:** Funai & Funai-made Philips Blu-Ray player/HTS (USA market)
|
||||||
**Notes:** N/A
|
**Notes:** N/A
|
||||||
|
|
||||||
|
## Funai MStar
|
||||||
|
**Used in:** MStar-based Funai & Funai-made Philips TVs (USA market)
|
||||||
|
**Notes:** Inner SoC part is extracted with mstar_secure_old
|
||||||
|
|
||||||
## Funai UPG
|
## Funai UPG
|
||||||
**Used in:** Some Funai TVs
|
**Used in:** Some Funai TVs
|
||||||
**Notes:** **Depends on keys** - see keys.rs.
|
**Notes:** **Depends on keys** - see keys.rs.
|
||||||
@@ -107,6 +111,8 @@ Tip: if you have split ROM (.ROM-00 and .ROM-01), extract both into the same fol
|
|||||||
## MStar upgrade bin (Secure, old)
|
## MStar upgrade bin (Secure, old)
|
||||||
**Used in:** Older MStar-based TVs with Secure upgrade mode (encrypted+signed)
|
**Used in:** Older MStar-based TVs with Secure upgrade mode (encrypted+signed)
|
||||||
**Notes:** Only default upgrade key is supported. This use the extractor above after decrypting.
|
**Notes:** Only default upgrade key is supported. This use the extractor above after decrypting.
|
||||||
|
**Options:**
|
||||||
|
`mstar_secure_old:keep_decrypted` - Keep decrypted file (it will be deleted by default).
|
||||||
|
|
||||||
## MediaTek BDP
|
## MediaTek BDP
|
||||||
**Used in:** Many MediaTek-based Blu-Ray players (LG, Samsung, Philips, Panasonic...)
|
**Used in:** Many MediaTek-based Blu-Ray players (LG, Samsung, Philips, Panasonic...)
|
||||||
|
|||||||
+13
-7
@@ -23,6 +23,7 @@ pub mod rvp;
|
|||||||
pub mod funai_upg;
|
pub mod funai_upg;
|
||||||
pub mod funai_upg_phl;
|
pub mod funai_upg_phl;
|
||||||
pub mod funai_bdp;
|
pub mod funai_bdp;
|
||||||
|
pub mod funai_mstar;
|
||||||
pub mod pana_dvd;
|
pub mod pana_dvd;
|
||||||
pub mod android_ota_payload;
|
pub mod android_ota_payload;
|
||||||
pub mod bdl;
|
pub mod bdl;
|
||||||
@@ -59,11 +60,6 @@ pub fn get_registry() -> Vec<Format> {
|
|||||||
detector_func: crate::formats::mstar::is_mstar_file,
|
detector_func: crate::formats::mstar::is_mstar_file,
|
||||||
extractor_func: crate::formats::mstar::extract_mstar,
|
extractor_func: crate::formats::mstar::extract_mstar,
|
||||||
},
|
},
|
||||||
Format {
|
|
||||||
name: "mstar_secure_old",
|
|
||||||
detector_func: crate::formats::mstar_secure_old::is_mstar_secure_old_file,
|
|
||||||
extractor_func: crate::formats::mstar_secure_old::extract_mstar_secure_old,
|
|
||||||
},
|
|
||||||
Format {
|
Format {
|
||||||
name: "samsung_old",
|
name: "samsung_old",
|
||||||
detector_func: crate::formats::samsung_old::is_samsung_old_dir,
|
detector_func: crate::formats::samsung_old::is_samsung_old_dir,
|
||||||
@@ -144,6 +140,11 @@ pub fn get_registry() -> Vec<Format> {
|
|||||||
detector_func: crate::formats::funai_bdp::is_funai_bdp_file,
|
detector_func: crate::formats::funai_bdp::is_funai_bdp_file,
|
||||||
extractor_func: crate::formats::funai_bdp::extract_funai_bdp,
|
extractor_func: crate::formats::funai_bdp::extract_funai_bdp,
|
||||||
},
|
},
|
||||||
|
Format {
|
||||||
|
name: "funai_mstar", // ORDER: needs to be placed BELOW mstar_secure_old
|
||||||
|
detector_func: crate::formats::funai_mstar::is_funai_mstar_file, //because, it can end with mstar_secure_old payload, but because it is not aligned to the start of the file, extraction will fail
|
||||||
|
extractor_func: crate::formats::funai_mstar::extract_funai_mstar,
|
||||||
|
},
|
||||||
Format {
|
Format {
|
||||||
name: "pana_dvd",
|
name: "pana_dvd",
|
||||||
detector_func: crate::formats::pana_dvd::is_pana_dvd_file,
|
detector_func: crate::formats::pana_dvd::is_pana_dvd_file,
|
||||||
@@ -221,8 +222,8 @@ pub fn get_registry() -> Vec<Format> {
|
|||||||
},
|
},
|
||||||
Format {
|
Format {
|
||||||
name: "philips_bdp",
|
name: "philips_bdp",
|
||||||
detector_func: crate::formats::philips_bdp::is_philips_bdp_file,
|
detector_func: crate::formats::philips_bdp::is_philips_bdp_file, //ORDER: needs to be placed below mtk_bdp
|
||||||
extractor_func: crate::formats::philips_bdp::extract_philips_bdp,
|
extractor_func: crate::formats::philips_bdp::extract_philips_bdp, //because, it can end with mtk_bdp payload, but because it is not aligned to the start of the file, extraction will fail
|
||||||
},
|
},
|
||||||
Format {
|
Format {
|
||||||
name: "mtk_bdp",
|
name: "mtk_bdp",
|
||||||
@@ -244,5 +245,10 @@ pub fn get_registry() -> Vec<Format> {
|
|||||||
detector_func: crate::formats::onkyo::is_onkyo_file,
|
detector_func: crate::formats::onkyo::is_onkyo_file,
|
||||||
extractor_func: crate::formats::onkyo::extract_onkyo,
|
extractor_func: crate::formats::onkyo::extract_onkyo,
|
||||||
},
|
},
|
||||||
|
Format {
|
||||||
|
name: "mstar_secure_old",
|
||||||
|
detector_func: crate::formats::mstar_secure_old::is_mstar_secure_old_file,
|
||||||
|
extractor_func: crate::formats::mstar_secure_old::extract_mstar_secure_old,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InfoStruct {
|
||||||
|
pub file_code: String,
|
||||||
|
pub brand_name: String,
|
||||||
|
pub model_name: String,
|
||||||
|
pub soc_version: String,
|
||||||
|
pub frc_version: String,
|
||||||
|
pub soc_size: usize,
|
||||||
|
pub frc60_size: usize,
|
||||||
|
pub frc120_size: usize,
|
||||||
|
}
|
||||||
|
impl InfoStruct {
|
||||||
|
pub fn from_str(str: String) -> Option<Self> {
|
||||||
|
let lines: Vec<&str> = str.lines().map(|l| l.trim()).collect();
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
|
for line in lines {
|
||||||
|
//skip markers
|
||||||
|
if line == "#@INFO" || line == "#@END" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//split KEY=VALUE
|
||||||
|
let (key, value) = line.split_once('=')?;
|
||||||
|
map.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
file_code: map.get("FileCode")?.to_string(),
|
||||||
|
brand_name: map.get("BrandName")?.to_string(),
|
||||||
|
model_name: map.get("ModelName")?.to_string(),
|
||||||
|
soc_version: map.get("SoC_Version")?.to_string(),
|
||||||
|
frc_version: map.get("FRC_Version")?.to_string(),
|
||||||
|
|
||||||
|
soc_size: map.get("SoC_Size")?.parse().ok()?,
|
||||||
|
frc60_size: map.get("FRC60_Size")?.parse().ok()?,
|
||||||
|
frc120_size: map.get("FRC120_Size")?.parse().ok()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
mod include;
|
||||||
|
use std::any::Any;
|
||||||
|
use crate::{AppContext, InputTarget};
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
use std::fs::{self, File, OpenOptions};
|
||||||
|
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::*;
|
||||||
|
|
||||||
|
struct FunaiMstarCtx {
|
||||||
|
data_offset: u64,
|
||||||
|
info_str: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_funai_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)};
|
||||||
|
|
||||||
|
let mut info = common::read_file(&file, 0, 0x1000)?; //try at start of file
|
||||||
|
if info.starts_with(b"#@INFO") {
|
||||||
|
return Ok(Some(Box::new(FunaiMstarCtx {data_offset: 0x1000, info_str: common::string_from_bytes(&info)})))
|
||||||
|
}
|
||||||
|
|
||||||
|
//try at end of file (variant 2)
|
||||||
|
let file_size = file.metadata()?.len();
|
||||||
|
if file_size < 0x1000 {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
info = common::read_file(&file, file_size - 0x1000, 0x1000)?;
|
||||||
|
if info.starts_with(b"#@INFO") {
|
||||||
|
return Ok(Some(Box::new(FunaiMstarCtx {data_offset: 0, info_str: common::string_from_bytes(&info)})))
|
||||||
|
} else {
|
||||||
|
return Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 info = InfoStruct::from_str(ctx.info_str).unwrap();
|
||||||
|
println!("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)];
|
||||||
|
|
||||||
|
file.seek(SeekFrom::Start(ctx.data_offset))?;
|
||||||
|
|
||||||
|
let mut p_i = 0;
|
||||||
|
for (name, size) in payloads {
|
||||||
|
if size == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
println!("\n#{} - {}, Size: {}", p_i+1, name, size);
|
||||||
|
|
||||||
|
let data = common::read_exact(&mut file, size)?;
|
||||||
|
|
||||||
|
let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", name));
|
||||||
|
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!("- 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" {
|
||||||
|
let r_out_file = File::open(&output_path)?;
|
||||||
|
let in_ctx: AppContext = AppContext {
|
||||||
|
input: InputTarget::File(r_out_file),
|
||||||
|
output_dir: app_ctx.output_dir.join("SoC"),
|
||||||
|
options: app_ctx.options.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
//do check and extarct
|
||||||
|
if let Some(result) = is_mstar_secure_old_file(&in_ctx)? {
|
||||||
|
println!("- Extracting mstar_secure_old...");
|
||||||
|
extract_mstar_secure_old(&in_ctx, result)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
p_i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -134,6 +134,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box
|
|||||||
Path::new(&app_ctx.output_dir).join(format!("_unknown_{}.bin", offset))
|
Path::new(&app_ctx.output_dir).join(format!("_unknown_{}.bin", offset))
|
||||||
} else {
|
} else {
|
||||||
println!("- Warning, unknown destination - skipping...");
|
println!("- Warning, unknown destination - skipping...");
|
||||||
|
i += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ pub fn extract_mstar_secure_old(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Resu
|
|||||||
|
|
||||||
let enc_data = common::read_file(&mut file, hdr.file_data_offset as u64, hdr.file_data_len as usize)?;
|
let enc_data = common::read_file(&mut file, hdr.file_data_offset as u64, hdr.file_data_len as usize)?;
|
||||||
|
|
||||||
println!("Decrypting...");
|
println!("- Decrypting...");
|
||||||
let dec_data = decrypt_aes128_ecb(&MSTAR_DEFAULT_UPGRADE_KEY, &enc_data)?;
|
let dec_data = decrypt_aes128_ecb(&MSTAR_DEFAULT_UPGRADE_KEY, &enc_data)?;
|
||||||
|
|
||||||
let output_path = Path::new(&app_ctx.output_dir).join("_decrypted.bin");
|
let output_path = Path::new(&app_ctx.output_dir).join("_decrypted.bin");
|
||||||
@@ -49,8 +49,6 @@ pub fn extract_mstar_secure_old(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Resu
|
|||||||
let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?;
|
let mut out_file = OpenOptions::new().write(true).create(true).open(&output_path)?;
|
||||||
out_file.write_all(&dec_data)?;
|
out_file.write_all(&dec_data)?;
|
||||||
|
|
||||||
println!("- Saved decrypted file!");
|
|
||||||
|
|
||||||
//run standard mstar ext into same directory
|
//run standard mstar ext into same directory
|
||||||
let r_out_file = File::open(&output_path)?;
|
let r_out_file = File::open(&output_path)?;
|
||||||
let in_ctx: AppContext = AppContext {
|
let in_ctx: AppContext = AppContext {
|
||||||
@@ -66,5 +64,10 @@ pub fn extract_mstar_secure_old(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Resu
|
|||||||
return Err("detection failed on decrypted data".into());
|
return Err("detection failed on decrypted data".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//delete decrypted file unless asked not to
|
||||||
|
if !app_ctx.has_option("mstar_secure_old:keep_decrypted") {
|
||||||
|
fs::remove_file(&output_path)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user