mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-15 21:00:03 +02:00
+ added mb230 (novatek)
This commit is contained in:
@@ -8,6 +8,13 @@ pub const VESTEL_PARTITIONS: &[(&str, (usize, usize))] = &[
|
|||||||
("buf", (0xEF04800, 0x400000)),
|
("buf", (0xEF04800, 0x400000)),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub struct VestelCtx {
|
pub const MB230_PARTITIONS: &[(&str, (usize, usize))] = &[
|
||||||
pub is_encrypted: bool,
|
("xboot", (0x000000, 0x0C0000)),
|
||||||
}
|
("xboot_code", (0x0C0000, 0x200000)),
|
||||||
|
("uboot", (0x380000, 0x140000)),
|
||||||
|
("kernel_dtb", (0x4C0000, 0xD20000)),
|
||||||
|
("ubi_volume", (0x1280000, 0x9B40000)),
|
||||||
|
("ubifs", (0xADC0000, 0x1BC0000)),
|
||||||
|
("squashfs_area", (0xC980000, 0x680000)),
|
||||||
|
("app_modules", (0xD000000, 0x1600000)),
|
||||||
|
];
|
||||||
+79
-10
@@ -9,6 +9,16 @@ use crate::keys;
|
|||||||
|
|
||||||
use include::*;
|
use include::*;
|
||||||
|
|
||||||
|
pub struct VestelCtx {
|
||||||
|
pub is_encrypted: bool,
|
||||||
|
pub variant: VestelVariant,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum VestelVariant {
|
||||||
|
Standard,
|
||||||
|
Mb230,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_vestel_file(
|
pub fn is_vestel_file(
|
||||||
app_ctx: &AppContext,
|
app_ctx: &AppContext,
|
||||||
) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||||
@@ -19,16 +29,25 @@ pub fn is_vestel_file(
|
|||||||
|
|
||||||
let file_size = file.metadata()?.len() as usize;
|
let file_size = file.metadata()?.len() as usize;
|
||||||
|
|
||||||
// 1. FAST PATH: plaintext check
|
|
||||||
let header = common::read_file(&file, 0, 64)?;
|
let header = common::read_file(&file, 0, 64)?;
|
||||||
|
|
||||||
if header.windows(5).any(|w| w == b"OPTEE") {
|
if header.windows(5).any(|w| w == b"OPTEE") {
|
||||||
return Ok(Some(Box::new(VestelCtx {
|
return Ok(Some(Box::new(VestelCtx {
|
||||||
is_encrypted: false,
|
is_encrypted: false,
|
||||||
|
variant: VestelVariant::Standard,
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. KEY CHECK
|
if header.starts_with(b"\xaa\xaa\x55\x55\x55\x55\xaa\xaa") {
|
||||||
|
let magic_str = &header[0x10..0x14];
|
||||||
|
if magic_str == b"spi\x00" {
|
||||||
|
return Ok(Some(Box::new(VestelCtx {
|
||||||
|
is_encrypted: false,
|
||||||
|
variant: VestelVariant::Mb230,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let (_, key_hex) = match keys::VESTEL.first() {
|
let (_, key_hex) = match keys::VESTEL.first() {
|
||||||
Some(k) => k,
|
Some(k) => k,
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
@@ -44,28 +63,24 @@ pub fn is_vestel_file(
|
|||||||
Err(_) => return Ok(None),
|
Err(_) => return Ok(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 3. SIZE CHECK
|
|
||||||
if file_size < 32 {
|
if file_size < 32 {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Read file
|
|
||||||
let data = common::read_exact(&mut file, file_size)?;
|
let data = common::read_exact(&mut file, file_size)?;
|
||||||
|
|
||||||
// 5. Python equivalent trimming
|
|
||||||
let cut_len = data.len().saturating_sub(16) & !0xF;
|
let cut_len = data.len().saturating_sub(16) & !0xF;
|
||||||
|
|
||||||
if cut_len == 0 {
|
if cut_len == 0 {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. decrypt candidate
|
|
||||||
let decrypted = decrypt_aes128_ecb(&key, &data[..cut_len])?;
|
let decrypted = decrypt_aes128_ecb(&key, &data[..cut_len])?;
|
||||||
|
|
||||||
// 7. verify OPTEE in decrypted output
|
|
||||||
if decrypted.windows(5).any(|w| w == b"OPTEE") {
|
if decrypted.windows(5).any(|w| w == b"OPTEE") {
|
||||||
return Ok(Some(Box::new(VestelCtx {
|
return Ok(Some(Box::new(VestelCtx {
|
||||||
is_encrypted: true,
|
is_encrypted: true,
|
||||||
|
variant: VestelVariant::Standard,
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,9 +91,20 @@ pub fn extract_vestel(
|
|||||||
app_ctx: &AppContext,
|
app_ctx: &AppContext,
|
||||||
ctx: Box<dyn Any>,
|
ctx: Box<dyn Any>,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
|
|
||||||
let ctx = ctx.downcast::<VestelCtx>().expect("Missing context");
|
let ctx = ctx.downcast::<VestelCtx>().expect("Missing context");
|
||||||
|
|
||||||
|
match ctx.variant {
|
||||||
|
VestelVariant::Mb230 => extract_mb230(app_ctx, &ctx),
|
||||||
|
VestelVariant::Standard => extract_standard(app_ctx, &ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_standard(
|
||||||
|
app_ctx: &AppContext,
|
||||||
|
ctx: &VestelCtx,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
|
||||||
|
|
||||||
let file_size = file.metadata()?.len() as usize;
|
let file_size = file.metadata()?.len() as usize;
|
||||||
let data = common::read_exact(&mut file, file_size)?;
|
let data = common::read_exact(&mut file, file_size)?;
|
||||||
|
|
||||||
@@ -89,7 +115,6 @@ pub fn extract_vestel(
|
|||||||
let key_vec = hex::decode(key_hex)?;
|
let key_vec = hex::decode(key_hex)?;
|
||||||
let key: [u8; 16] = key_vec.try_into().map_err(|_| "Invalid AES key length")?;
|
let key: [u8; 16] = key_vec.try_into().map_err(|_| "Invalid AES key length")?;
|
||||||
|
|
||||||
// decrypt if needed
|
|
||||||
let (final_data, decrypted_path) = if ctx.is_encrypted {
|
let (final_data, decrypted_path) = if ctx.is_encrypted {
|
||||||
println!("Encrypted Vestel firmware detected");
|
println!("Encrypted Vestel firmware detected");
|
||||||
println!("Using key: {} ({})", "VESTEL", key_hex);
|
println!("Using key: {} ({})", "VESTEL", key_hex);
|
||||||
@@ -138,12 +163,56 @@ pub fn extract_vestel(
|
|||||||
println!("- Saved {}.bin", name);
|
println!("- Saved {}.bin", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup decrypted temp file if not needed
|
|
||||||
if let Some(path) = decrypted_path {
|
if let Some(path) = decrypted_path {
|
||||||
if !app_ctx.has_option("vestel:keep_decrypted") {
|
if !app_ctx.has_option("vestel:keep_decrypted") {
|
||||||
let _ = fs::remove_file(path);
|
let _ = fs::remove_file(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_mb230(
|
||||||
|
app_ctx: &AppContext,
|
||||||
|
_ctx: &VestelCtx,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
let partitions = MB230_PARTITIONS;
|
||||||
|
println!("\nPartition count: {}", partitions.len());
|
||||||
|
|
||||||
|
fs::create_dir_all(&app_ctx.output_dir)?;
|
||||||
|
|
||||||
|
for (name, (offset, size)) in partitions.iter() {
|
||||||
|
let start = *offset;
|
||||||
|
let end = start.saturating_add(*size);
|
||||||
|
|
||||||
|
if start >= file_size {
|
||||||
|
println!("Skipping {} (out of range)", name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let end = end.min(file_size);
|
||||||
|
let segment = &data[start..end];
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"\n{} - Offset: 0x{:X}, Size: 0x{:X} ({} bytes)",
|
||||||
|
name, offset, size, segment.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
let output_path = std::path::Path::new(&app_ctx.output_dir)
|
||||||
|
.join(format!("{}.bin", name));
|
||||||
|
|
||||||
|
fs::write(&output_path, segment)?;
|
||||||
|
|
||||||
|
println!("- Saved {}.bin", name);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
+1
-1
@@ -171,5 +171,5 @@ pub static EPK: &[(&str, &str)] = &[
|
|||||||
|
|
||||||
//Vestel keys
|
//Vestel keys
|
||||||
pub static VESTEL: &[(&str, &str)] = &[
|
pub static VESTEL: &[(&str, &str)] = &[
|
||||||
("VESTEL-MB180/MB130/MB181", "12345678123456781234567812345678"),
|
("VESTEL-MB180/MB130/MB181/MB230/MB211/MB281", "12345678123456781234567812345678"),
|
||||||
];
|
];
|
||||||
Reference in New Issue
Block a user