mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-15 21:00:03 +02:00
+ added mb211 (novatek)
This commit is contained in:
+8
-1
@@ -54,6 +54,7 @@ pub mod philips_bdp;
|
||||
pub mod tsb_bin;
|
||||
pub mod pup;
|
||||
pub mod vestel;
|
||||
pub mod novatek_raw;
|
||||
|
||||
pub mod msd;
|
||||
pub mod msd10;
|
||||
@@ -281,5 +282,11 @@ pub fn get_registry() -> Vec<Format> {
|
||||
name: "vestel",
|
||||
detector_func: crate::formats::vestel::is_vestel_file,
|
||||
extractor_func: crate::formats::vestel::extract_vestel,
|
||||
}, ]
|
||||
},
|
||||
Format {
|
||||
name: "novatek_raw",
|
||||
detector_func: crate::formats::novatek_raw::is_novatek_raw_file,
|
||||
extractor_func: crate::formats::novatek_raw::extract_novatek_raw,
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
use std::any::Any;
|
||||
use crate::AppContext;
|
||||
|
||||
use std::path::Path;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::utils::common;
|
||||
use crate::utils::aes::decrypt_aes_ecb_auto;
|
||||
use crate::keys;
|
||||
|
||||
const NVT_PARTITIONS: [(&str, u32, u32); 16] = [
|
||||
("spi_boot", 0x0000020, 0x00020000),
|
||||
("nand_xbootdat", 0x000a0020, 0x0001800),
|
||||
("nand_secos", 0x000c0020, 0x000f5800),
|
||||
("nand_uboot", 0x001c0020, 0x00082800),
|
||||
("nand_fdt0", 0x00260020, 0x0004000),
|
||||
("nand_fdt1", 0x00280020, 0x0004000),
|
||||
("nand_ker0", 0x002a0020, 0x00353000),
|
||||
("nand_ker1", 0x00600020, 0x00db9000),
|
||||
("nand_fs0", 0x013c0020, 0x00f40000),
|
||||
("nand_ap0", 0x02320020, 0x09400000),
|
||||
("nand_apdat0", 0x0b740020, 0x00440000),
|
||||
("nand_buffer", 0x11be0020, 0x003c0000),
|
||||
("nand_logo", 0x05f80020, 0x0060000),
|
||||
("spi_env", 0x0000000, 0x0000000),
|
||||
("nand_ddrcfg", 0x0000000, 0x0000000),
|
||||
("nand_xboot", 0x0000000, 0x0000000),
|
||||
];
|
||||
|
||||
pub fn is_novatek_raw_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 file_size = file.metadata()?.len();
|
||||
|
||||
if file_size < 0x10000 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if app_ctx.has_option("nvt_raw:key") {
|
||||
return Ok(Some(Box::new(())));
|
||||
}
|
||||
|
||||
for _ in keys::NOVATEK_RAW {
|
||||
return Ok(Some(Box::new(())));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn extract_novatek_raw(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 key_hex = app_ctx.options.iter()
|
||||
.find(|o| o.starts_with("nvt_raw:key="))
|
||||
.map(|o| o.strip_prefix("nvt_raw:key=").unwrap())
|
||||
.or(keys::NOVATEK_RAW.first().map(|(_, k)| *k));
|
||||
|
||||
let key_hex = match key_hex {
|
||||
Some(k) => k,
|
||||
None => return Err("nvt_raw:key=<hex_key> option required for decryption".into()),
|
||||
};
|
||||
|
||||
let key = hex::decode(key_hex).map_err(|e| format!("Invalid hex key: {}", e))?;
|
||||
|
||||
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" });
|
||||
|
||||
let dec_data = decrypt_aes_ecb_auto(&key, &enc_data)?;
|
||||
|
||||
println!("- 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());
|
||||
|
||||
println!("\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);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -172,4 +172,9 @@ pub static EPK: &[(&str, &str)] = &[
|
||||
//Vestel keys
|
||||
pub static VESTEL: &[(&str, &str)] = &[
|
||||
("VESTEL-MB180/MB130/MB181/MB230/MB211/MB281", "12345678123456781234567812345678"),
|
||||
];
|
||||
|
||||
//Novatek/MStar custom keys
|
||||
pub static NOVATEK_RAW: &[(&str, &str)] = &[
|
||||
("MB211", "A9A05FC6F986254382DF2C93DEDF7B98"),
|
||||
];
|
||||
+2
-2
@@ -20,8 +20,8 @@ use crate::error::UnixtractError;
|
||||
with built-in decryption and decompression capabilities."
|
||||
)]
|
||||
struct Args {
|
||||
/// The target file or directory to analyze/extract
|
||||
#[arg(required_unless_present = "list-formats")]
|
||||
/// The target file or directory to analyze/extract
|
||||
#[arg(required_unless_present = "list_formats")]
|
||||
input_target: Option<String>,
|
||||
|
||||
/// Folder to save extracted files to (default: _<INPUT_TARGET>)
|
||||
|
||||
Reference in New Issue
Block a user