mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-15 21:00:03 +02:00
+ fix vestel
This commit is contained in:
@@ -1 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
|
__pycache__/
|
||||||
|
.vscode
|
||||||
+93
-53
@@ -2,76 +2,107 @@ mod include;
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use crate::AppContext;
|
use crate::AppContext;
|
||||||
|
|
||||||
use std::fs::{self, OpenOptions};
|
use std::fs;
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
use crate::utils::common;
|
use crate::utils::common;
|
||||||
|
use crate::utils::aes::decrypt_aes128_ecb;
|
||||||
use crate::keys;
|
use crate::keys;
|
||||||
use crate::utils::aes::{decrypt_aes128_ecb};
|
|
||||||
use include::*;
|
use include::*;
|
||||||
|
|
||||||
pub fn is_vestel_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
pub fn is_vestel_file(
|
||||||
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
|
app_ctx: &AppContext,
|
||||||
|
) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||||
|
let mut file = match app_ctx.file() {
|
||||||
|
Some(f) => f,
|
||||||
|
None => return Ok(None),
|
||||||
|
};
|
||||||
|
|
||||||
let file_size = file.metadata()?.len() as usize;
|
let file_size = file.metadata()?.len() as usize;
|
||||||
|
|
||||||
let header = common::read_file(&file, 0, 32)?;
|
// 1. FAST PATH: plaintext check
|
||||||
if header.windows(6).any(|w| w == b"VESTEL") {
|
let header = common::read_file(&file, 0, 64)?;
|
||||||
return Ok(Some(Box::new(VestelCtx { is_encrypted: false })));
|
|
||||||
|
if header.windows(5).any(|w| w == b"OPTEE") {
|
||||||
|
return Ok(Some(Box::new(VestelCtx {
|
||||||
|
is_encrypted: false,
|
||||||
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
let vestel_key = keys::VESTEL.first();
|
// 2. KEY CHECK
|
||||||
if vestel_key.is_none() {
|
let (_, key_hex) = match keys::VESTEL.first() {
|
||||||
return Ok(None);
|
Some(k) => k,
|
||||||
}
|
None => return Ok(None),
|
||||||
let (_key_name, key_hex) = vestel_key.unwrap();
|
};
|
||||||
|
|
||||||
let key = hex::decode(key_hex)?;
|
let key_vec = hex::decode(key_hex)?;
|
||||||
if key.len() != 16 {
|
if key_vec.len() != 16 {
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
let key_bytes: [u8; 16] = key.try_into().map_err(|_| "Invalid key length")?;
|
|
||||||
|
|
||||||
if file_size % 16 != 0 {
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = common::read_exact(&mut file.try_clone()?, file_size)?;
|
let key: [u8; 16] = match key_vec.try_into() {
|
||||||
let decrypted = decrypt_aes128_ecb(&key_bytes, &data)?;
|
Ok(k) => k,
|
||||||
|
Err(_) => return Ok(None),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3. SIZE CHECK
|
||||||
|
if file_size < 32 {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Read file
|
||||||
|
let data = common::read_exact(&mut file, file_size)?;
|
||||||
|
|
||||||
|
// 5. Python equivalent trimming
|
||||||
|
let cut_len = data.len().saturating_sub(16) & !0xF;
|
||||||
|
|
||||||
|
if cut_len == 0 {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. decrypt candidate
|
||||||
|
let decrypted = decrypt_aes128_ecb(&key, &data[..cut_len])?;
|
||||||
|
|
||||||
|
// 7. verify OPTEE in decrypted output
|
||||||
|
if decrypted.windows(5).any(|w| w == b"OPTEE") {
|
||||||
|
return Ok(Some(Box::new(VestelCtx {
|
||||||
|
is_encrypted: true,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
if decrypted.windows(6).any(|w| w == b"VESTEL") {
|
|
||||||
Ok(Some(Box::new(VestelCtx { is_encrypted: true })))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_vestel(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn extract_vestel(
|
||||||
|
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 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");
|
||||||
|
|
||||||
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)?;
|
||||||
|
|
||||||
let (key_name, key_hex) = keys::VESTEL.first()
|
let (_, key_hex) = keys::VESTEL
|
||||||
|
.first()
|
||||||
.ok_or("VESTEL key not found!")?;
|
.ok_or("VESTEL key not found!")?;
|
||||||
|
|
||||||
let key = hex::decode(key_hex)?;
|
let key_vec = hex::decode(key_hex)?;
|
||||||
let key_len = key.len();
|
let key: [u8; 16] = key_vec.try_into().map_err(|_| "Invalid AES key length")?;
|
||||||
let key_bytes = match key.try_into() {
|
|
||||||
Ok(bytes) => bytes,
|
|
||||||
Err(_) => return Err(format!("Invalid VESTEL key length: expected 16 bytes, got {}", key_len).into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let (decrypted_data, decrypted_path) = if ctx.is_encrypted {
|
// decrypt if needed
|
||||||
println!("Detected encrypted Vestel firmware, using key: {} ({})", key_name, key_hex);
|
let (final_data, decrypted_path) = if ctx.is_encrypted {
|
||||||
println!("Decrypting firmware...");
|
println!("Encrypted Vestel firmware detected");
|
||||||
let dec_data = decrypt_aes128_ecb(&key_bytes, &data)?;
|
println!("Using key: {} ({})", "VESTEL", key_hex);
|
||||||
|
|
||||||
|
let dec = decrypt_aes128_ecb(&key, &data)?;
|
||||||
|
|
||||||
|
let output_path = std::path::Path::new(&app_ctx.output_dir)
|
||||||
|
.join("_decrypted.bin");
|
||||||
|
|
||||||
let output_path = std::path::Path::new(&app_ctx.output_dir).join("_decrypted.bin");
|
|
||||||
fs::create_dir_all(&app_ctx.output_dir)?;
|
fs::create_dir_all(&app_ctx.output_dir)?;
|
||||||
let mut out_file = OpenOptions::new().write(true).create(true).truncate(true).open(&output_path)?;
|
fs::write(&output_path, &dec)?;
|
||||||
out_file.write_all(&dec_data)?;
|
|
||||||
(dec_data, Some(output_path))
|
(dec, Some(output_path))
|
||||||
} else {
|
} else {
|
||||||
println!("Detected Vestel firmware (unencrypted)");
|
println!("Detected Vestel firmware (unencrypted)");
|
||||||
(data, None)
|
(data, None)
|
||||||
@@ -80,28 +111,37 @@ pub fn extract_vestel(app_ctx: &AppContext, ctx: Box<dyn Any>) -> Result<(), Box
|
|||||||
let partitions = VESTEL_PARTITIONS;
|
let partitions = VESTEL_PARTITIONS;
|
||||||
println!("\nPartition count: {}", partitions.len());
|
println!("\nPartition count: {}", partitions.len());
|
||||||
|
|
||||||
|
fs::create_dir_all(&app_ctx.output_dir)?;
|
||||||
|
|
||||||
for (name, (offset, size)) in partitions.iter() {
|
for (name, (offset, size)) in partitions.iter() {
|
||||||
let end = offset + size;
|
let start = *offset;
|
||||||
if end > decrypted_data.len() {
|
let end = start.saturating_add(*size);
|
||||||
println!("\nWarning: Partition {} extends beyond firmware bounds (offset=0x{:X}, size=0x{:X})", name, offset, size);
|
|
||||||
|
if start >= final_data.len() {
|
||||||
|
println!("Skipping {} (out of range)", name);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let segment = &decrypted_data[*offset..std::cmp::min(end, decrypted_data.len())];
|
let end = end.min(final_data.len());
|
||||||
|
let segment = &final_data[start..end];
|
||||||
|
|
||||||
println!("\n{} - Offset: 0x{:X}, Size: 0x{:X}", name, offset, size);
|
println!(
|
||||||
|
"\n{} - Offset: 0x{:X}, Size: 0x{:X}",
|
||||||
|
name, offset, size
|
||||||
|
);
|
||||||
|
|
||||||
let output_path = std::path::Path::new(&app_ctx.output_dir).join(format!("{}.bin", name));
|
let output_path = std::path::Path::new(&app_ctx.output_dir)
|
||||||
|
.join(format!("{}.bin", name));
|
||||||
|
|
||||||
fs::create_dir_all(&app_ctx.output_dir)?;
|
fs::write(&output_path, segment)?;
|
||||||
let mut out_file = OpenOptions::new().write(true).create(true).truncate(true).open(output_path)?;
|
|
||||||
out_file.write_all(segment)?;
|
|
||||||
|
|
||||||
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") {
|
||||||
fs::remove_file(&path)?;
|
let _ = fs::remove_file(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user