From 22c865817380a170eeeb1286e83278ef18bee6ef Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Tue, 7 Oct 2025 16:00:32 +0200 Subject: [PATCH] Consistency cleanup + add MSD11 and SDDL.SEC extractors --- src/formats.rs | 4 +- src/formats/epk1.rs | 20 ++-- src/formats/msd10.rs | 24 +++-- src/formats/msd11.rs | 194 +++++++++++++++++++++++++++++++++ src/formats/mstar.rs | 16 +-- src/formats/novatek.rs | 10 +- src/formats/pfl_upg.rs | 18 ++-- src/formats/samsung_old.rs | 9 +- src/formats/sddl_sec.rs | 214 +++++++++++++++++++++++++++++++++++++ src/formats/tpv_timg.rs | 35 ++---- src/main.rs | 19 ++-- 11 files changed, 478 insertions(+), 85 deletions(-) create mode 100644 src/formats/msd11.rs create mode 100644 src/formats/sddl_sec.rs diff --git a/src/formats.rs b/src/formats.rs index 3b69a6f..e6950be 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -4,4 +4,6 @@ pub mod tpv_timg; pub mod pfl_upg; pub mod epk1; pub mod novatek; -pub mod msd10; \ No newline at end of file +pub mod msd10; +pub mod msd11; +pub mod sddl_sec; \ No newline at end of file diff --git a/src/formats/epk1.rs b/src/formats/epk1.rs index a37c2e7..f5d02a4 100644 --- a/src/formats/epk1.rs +++ b/src/formats/epk1.rs @@ -7,9 +7,7 @@ use crate::common; pub fn is_epk1_file(file: &File) -> bool { let header = common::read_file(&file, 0, 4).expect("Failed to read from file."); - let header_string = String::from_utf8_lossy(&header); - - if header_string == "epak"{ + if header == b"epak" { true } else { false @@ -28,15 +26,15 @@ pub fn extract_epk1(mut file: &File, output_folder: &str) -> Result<(), Box 256 { - println!("Big endian EPK1 detected."); + println!("\nBig endian EPK1 detected."); epk1_type = "be"; } else if init_pak_count < 21 { - println!("Little endian EPK1 detected."); + println!("\nLittle endian EPK1 detected."); epk1_type = "le"; } else { - //println!("EPK1(new) detected."); + //println!("\nEPK1(new) detected."); //epk1_type = "new"; - println!("Not supported!"); + println!("\nNot supported!"); return Ok(()); } @@ -71,7 +69,7 @@ pub fn extract_epk1(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box bool { let header = common::read_file(&file, 0, 6).expect("Failed to read from file."); - if header == b"MSDU10" { true } else { @@ -28,14 +27,15 @@ struct Section { //fw name, type, key static KEYS: &[(&str, &str, &str)] = &[ - ("T-NT14M", "old", "95d01e0bae861a05695bc8a6edb2ea835a09accd"), - ("T-HKM", "tizen", "1ac8989ff57db5e75ea67b033050871c"), + ("T-NT14M", "old", "95d01e0bae861a05695bc8a6edb2ea835a09accd"), + ("T-HKM", "tizen", "1ac8989ff57db5e75ea67b033050871c"), + ("T-HKP", "tizen", "cce8a3ef92f3e94895999e928f4dd6c3"), ]; fn decrypt_aes_salted_old(encrypted_data: &[u8], passphrase_bytes: &Vec) -> Result, Box> { let mut data = encrypted_data.to_vec(); - assert!(String::from_utf8(data[0..8].to_vec())? == "Salted__", "invalid encrypted data!"); + assert!(data[0..8].to_vec() == b"Salted__", "invalid encrypted data!"); let salt = &data[8..16]; //key = md5 of (passphrase + salt) @@ -61,7 +61,7 @@ fn decrypt_aes_salted_old(encrypted_data: &[u8], passphrase_bytes: &Vec) -> fn decrypt_aes_salted_tizen(encrypted_data: &[u8], passphrase_bytes: &Vec) -> Result, Box> { let mut data = encrypted_data.to_vec(); - assert!(String::from_utf8(data[0..8].to_vec())? == "Salted__", "invalid encrypted data!"); + assert!(data[0..8].to_vec() == b"Salted__", "invalid encrypted data!"); let salt = &data[8..16]; //iv = md5 of salt @@ -93,7 +93,7 @@ pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box = Vec::new(); @@ -158,7 +158,7 @@ pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Result<(), Box; + +use crate::common; + +pub fn is_msd11_file(file: &File) -> bool { + let header = common::read_file(&file, 0, 6).expect("Failed to read from file."); + if header == b"MSDU11" { + true + } else { + false + } +} + +struct Section { + index: u32, + offset: u64, + size: u64, + name: String, +} + +static KEYS: &[(&str, &str)] = &[ + ("T-JZM", "9b1d077c0d137d406c79ddacb6b159fe"), //2015 + ("T-HKMFK", "c7097975e8ab994beb5eaae57e0ba77c"), //2016 + ("T-KTM2L", "46b04f5e794ca4377a20951c9ea00427"), //2018 + ("T-KTM2", "29110e0ce940b3a9b67d3e158f3f1342"), //2018 + ("T-KTM", "d0d49d5f36f5c0da50062fbf32168f5b"), //2017 + ("T-KTSU", "19e1ba41163f03735e692d9daa2cbb47"), //2018 + ("T-KTSD", "39332605ff47a0aea999b10ce9087389"), //2018 + ("T-NKL", "5bab1098dab48792619ebd63650d929f"), //2020 +]; + +fn decrypt_aes_salted_tizen(encrypted_data: &[u8], passphrase_bytes: &Vec) -> Result, Box> { + let mut data = encrypted_data.to_vec(); + + assert!(data[0..8].to_vec() == b"Salted__", "invalid encrypted data!"); + let salt = &data[8..16]; + + //iv = md5 of salt + let iv = md5::compute(&salt); + + let decryptor = Aes128CbcDec::new((&(**passphrase_bytes)).into(), (&iv.0).into()); + let decrypted = decryptor.decrypt_padded_mut::(&mut data[16..]) + .map_err(|e| format!("Decryption error!!: {:?}", e))?; + + Ok(decrypted.to_vec()) +} + +fn decrypt_aes_tizen(encrypted_data: &[u8], passphrase_bytes: &Vec, salt: &[u8]) -> Result, Box> { + let mut data = encrypted_data.to_vec(); + + //iv = md5 of salt + let iv = md5::compute(&salt); + + let decryptor = Aes128CbcDec::new((&(**passphrase_bytes)).into(), (&iv.0).into()); + let decrypted = decryptor.decrypt_padded_mut::(&mut data) + .map_err(|e| format!("Decryption error!!: {:?}", e))?; + + Ok(decrypted.to_vec()) +} + +pub fn extract_msd11(mut file: &File, output_folder: &str) -> Result<(), Box> { + + let _magic = common::read_exact(&mut file, 6)?; //MSDU11 magic + + let _ = common::read_exact(&mut file, 12)?; + + let section_count_bytes = common::read_exact(&mut file, 4)?; + let section_count = u32::from_le_bytes(section_count_bytes.try_into().unwrap()); + println!("\nNumber of sections: {}", section_count); + + let mut sections: Vec
= Vec::new(); + + //parse sections + for _i in 0..section_count { + let index_bytes = common::read_exact(&mut file, 4)?; + let index = u32::from_le_bytes(index_bytes.try_into().unwrap()); + + let offset_bytes = common::read_exact(&mut file, 8)?; + let offset = u64::from_le_bytes(offset_bytes.try_into().unwrap()); + + let size_bytes = common::read_exact(&mut file, 8)?; + let size = u64::from_le_bytes(size_bytes.try_into().unwrap()); + + println!("Section {}: offset: {}, size: {}", index, offset, size); + sections.push(Section { index, offset, size , name: "".to_string() }); + } + + let header_count_bytes = common::read_exact(&mut file, 4)?; + let header_count = u32::from_le_bytes(header_count_bytes.try_into().unwrap()); + println!("\nNumber of headers: {}", header_count); + + let mut headers: Vec
= Vec::new(); + + //parse headers + for i in 0..header_count { + let offset_bytes = common::read_exact(&mut file, 8)?; + let offset = u64::from_le_bytes(offset_bytes.try_into().unwrap()); + + let size_bytes = common::read_exact(&mut file, 4)?; + let size = u32::from_le_bytes(size_bytes.try_into().unwrap()); + + let name_length_byte = common::read_exact(&mut file, 1)?; + let name_length = u8::from_le_bytes(name_length_byte.try_into().unwrap()); + + let name_bytes = common::read_exact(&mut file, name_length as usize)?; + let name = String::from_utf8(name_bytes)?; + + println!("Header {}: {}, offset: {}, size: {}", i + 1, name, offset, size); + + headers.push(Section { index: 0, offset, size: size as u64 , name }); + } + + //use first header + let firmware_name = &headers[0].name; + println!("\nFirmware name: {}", firmware_name); + + let mut passphrase: Option<&str> = None; + let passphrase_bytes; + + //find passphrase + for (prefix, value) in KEYS { + if firmware_name.starts_with(prefix) { + passphrase = Some(value); + break; + } + } + if let Some(p) = passphrase { + println!("Passphrase: {}", p); + passphrase_bytes = hex::decode(p)?; + } else { + println!("Sorry, this firmware is not supported!"); + std::process::exit(1); + } + + let toc_offset = headers[0].offset + 8; + let toc_size = headers[0].size - 8; + let toc_data = common::read_file(&file, toc_offset as u64, toc_size as usize)?; + + //parse TOC + let toc = decrypt_aes_salted_tizen(&toc_data, &passphrase_bytes)?; + let mut toc_reader = Cursor::new(toc); + + toc_reader.seek(SeekFrom::Current(262))?; // probably signature + toc_reader.seek(SeekFrom::Current(50))?; // Tizen Software Upgrade Tree Binary Format ver. 1.9 + + for i in 0..section_count { + toc_reader.seek(SeekFrom::Current(74))?; //unknown + + let name_length_byte = common::read_exact(&mut toc_reader, 1)?; + let name_length = u8::from_le_bytes(name_length_byte.try_into().unwrap()); + + let name_bytes = common::read_exact(&mut toc_reader, name_length as usize)?; + let name = String::from_utf8(name_bytes)?; + + toc_reader.seek(SeekFrom::Current(39))?; //unknown + + let salt = common::read_exact(&mut toc_reader, 8)?; + + toc_reader.seek(SeekFrom::Current(267))?; //unknown + + println!("\nSection {}: {}", sections[i as usize].index, name); + + let offset = sections[i as usize].offset; + let size = sections[i as usize].size; + let encrypted_data = common::read_file(&file, offset as u64, size as usize)?; + + println!("- Decrypting..."); + let decrypted_data = decrypt_aes_tizen(&encrypted_data, &passphrase_bytes, &salt)?; + + let output_path = Path::new(&output_folder).join(name); + + fs::create_dir_all(&output_folder)?; + let mut out_file = OpenOptions::new() + .write(true) + .create(true) + .open(output_path)?; + + out_file.write_all(&decrypted_data)?; + + println!("-- Saved file!"); + + } + + println!("\nExtraction finished!"); + + Ok(()) +} \ No newline at end of file diff --git a/src/formats/mstar.rs b/src/formats/mstar.rs index de3b6f5..5503b1b 100644 --- a/src/formats/mstar.rs +++ b/src/formats/mstar.rs @@ -132,28 +132,28 @@ pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box {}", offset, size, partname); + println!("\nPart: Offset: {}, Size: {} --> {}", offset, size, partname); if partname == "unknown"{ - println!("-- Unknown destination, skipping!"); + println!("- Unknown destination, skipping!"); } else { let data = common::read_file(&file, offset, size.try_into().unwrap())?; let out_data; if compression == "lzma" { - println!("-- Decompressing LZMA..."); + println!("- Decompressing LZMA..."); out_data = decompress_lzma(&data)?; } else if compression == "double_lzma" { - println!("-- Decompressing LZMA (Pass 1)..."); + println!("- Decompressing LZMA (Pass 1)..."); let pass_1 = decompress_lzma(&data)?; - println!("-- Decompressing LZMA (Pass 2)..."); + println!("- Decompressing LZMA (Pass 2)..."); out_data = decompress_lzma(&pass_1)?; } else if compression == "lz4" { - println!("-- Decompressing lz4, expected size: {}", lz4_expect_size); + println!("- Decompressing lz4, expected size: {}", lz4_expect_size); out_data = decompress_lz4(&data, lz4_expect_size.try_into().unwrap())?; } else if compression == "lzo" { // nothing in rust to parse lzo file with header - println!("-- lzo compression is not supported yet!!..."); + println!("- lzo compression is not supported yet!"); out_data = data; }else { out_data = data; @@ -168,6 +168,8 @@ pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box bool { let header = common::read_file(&file, 0, 4).expect("Failed to read from file."); - let header_string = String::from_utf8_lossy(&header); - - if header_string == "NFWB"{ + if header == b"NFWB" { true } else { false @@ -27,7 +25,7 @@ pub fn extract_novatek(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Result<(), Box bool { let header = common::read_file(&file, 0, 8).expect("Failed to read from file."); - let header_string = String::from_utf8_lossy(&header); - - if header_string == "2SWU3TXV"{ + if header == b"2SWU3TXV" { true } else { false @@ -88,7 +86,7 @@ pub fn extract_pfl_upg(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box Result<(), Box Result<(), Bo let file = File::open(&path)?; let filename = path.file_name().unwrap().to_str().unwrap(); let file_size = file.metadata()?.len(); - println!("- File: {}", filename); + println!("\nFile: {}", filename); let data = common::read_file(&file, 0, file_size.try_into().unwrap())?; let salt = &data[8..16]; @@ -126,7 +126,7 @@ pub fn extract_samsung_old(path: &PathBuf, output_folder: &str) -> Result<(), Bo //println!("IV: {:02x?}", iv_md5); let end = file_size - 260; - println!("-- Decrypting file..."); + println!("- Decrypting file..."); let decrypted_data = decrypt_aes(&data[16..end.try_into().unwrap()], &key_md5, &iv_md5)?; println!("-- DeXORing file..."); @@ -142,13 +142,14 @@ pub fn extract_samsung_old(path: &PathBuf, output_folder: &str) -> Result<(), Bo .open(output_path)?; out_file.write_all(&out_data)?; + + println!("--- Saved file!"); } } } } - println!(); - println!("Extraction finished!"); + println!("\nExtraction finished!"); Ok(()) } \ No newline at end of file diff --git a/src/formats/sddl_sec.rs b/src/formats/sddl_sec.rs new file mode 100644 index 0000000..47d33eb --- /dev/null +++ b/src/formats/sddl_sec.rs @@ -0,0 +1,214 @@ +use std::path::{Path, PathBuf}; +use std::fs::{self, File, OpenOptions}; +use std::io::{self, Read, Seek, SeekFrom, Write}; + +use aes::Aes128; +use flate2::read::ZlibDecoder; +use cbc::{Decryptor, cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit}}; + +type Aes128CbcDec = Decryptor; + +use crate::common; + +pub fn is_sddl_sec_file(file: &File) -> bool { + let header = common::read_file(&file, 0, 8).expect("Failed to read from file."); + if header == b"\x12\xB8\x02\x8C\x6F\xC6\xBD\x15" { + true + } else { + false + } +} + +fn decrypt(encrypted_data: &[u8], key: &[u8; 16], iv: &[u8; 16]) -> Result, Box> { + let mut data = encrypted_data.to_vec(); + let decryptor = Aes128CbcDec::new(key.into(), iv.into()); + let decrypted = decryptor.decrypt_padded_mut::(&mut data) + .map_err(|e| format!("!!Decryption error!!: {:?}", e))?; + + Ok(decrypted.to_vec()) +} + +//ported from original from https://nese.team/posts/justctf/ +fn decipher(s: &[u8], len_: usize) -> Vec { + let mut v3: u32 = 904; + let mut out = s.to_vec(); + let mut cnt = 0; + + if len_ > 0 { + let true_len = if len_ >= 0x80 { + 128 + } else { + len_ + }; + + if true_len > 0 { + let mut i = 0; + let mut j: u8 = 0; + + while i < s.len() { + let iter_ = s[i]; + i += 1; + j = j.wrapping_add(1); + + let v11 = (iter_ as u32).wrapping_add(38400) & 0xffffffff; + let v12 = iter_ ^ ((v3 & 0xff00) >> 8) as u8; + v3 = v3.wrapping_add(v11).wrapping_add(163) & 0xffffffff; + + if j == 0 { + v3 = 904; + } + + if cnt < out.len() { + out[cnt] = v12; + cnt += 1; + } + } + } + } + + out +} + +fn decompress_zlib(data: &[u8]) -> io::Result> { + let mut decoder = ZlibDecoder::new(data); + let mut decompressed = Vec::new(); + decoder.read_to_end(&mut decompressed)?; + + Ok(decompressed) +} + +pub fn extract_sddl_sec(file: &File, output_folder: &str) -> Result<(), Box> { + + let file_size = file.metadata()?.len(); + + let key = [ + 0x26, 0xE0, 0x96, 0xD3, 0xEF, 0x8A, 0x8F, 0xBB, + 0xAA, 0x5E, 0x51, 0x6F, 0x77, 0x26, 0xC2, 0x2C, + ]; + + let iv = [ + 0x3E, 0x4A, 0xE2, 0x3A, 0x69, 0xDB, 0x81, 0x54, + 0xCD, 0x88, 0x38, 0xC4, 0xB9, 0x0C, 0x76, 0x66, + ]; + + let mut offset = 32; + + while offset < file_size { + let header = common::read_file(&file, offset, 32)?; + let decrypted_header: Vec; + + match decrypt(&header, &key, &iv) { + Ok(v) => decrypted_header = v, + Err(_) => { + // SDDL files can have a footer(signature?) of 0x80 OR 0x100 lenght in later ones, and there is no good way to detect it before entering the while loop and the footer has no common header. + // so we can assume if a file fails to decode at negative offsets 0x80 or 0x100, that is the footer and it can be skipped. + if offset == file_size - 128 { + break + } else if offset == file_size - 256{ + break + } else { + println!("!!Decryption error!! This file is invalid or not compatible!"); + std::process::exit(0) + } + }, + } + + let decrypted_string = String::from_utf8_lossy(&decrypted_header); + + let filename = decrypted_string.split("\0").next().unwrap(); + let size_str = &decrypted_string[decrypted_string.len() - 12..]; + let size: u64 = size_str.parse().unwrap(); + + println!("\nFile: {}, Size: {}", filename, size); + + offset += 32; + + let data = common::read_file(&file, offset, size.try_into().unwrap())?; + let decrypted_data = decrypt(&data, &key, &iv)?; + + if decrypted_data.starts_with(&[0x11, 0x22, 0x33, 0x44]) && filename != "SDIT.FDI"{ // header of obfuscated file, SDIT.FDI also has this header but seems to work differently so its skipped + + println!("- Version: {}.{}{}{}", decrypted_data[24], decrypted_data[25], decrypted_data[26], decrypted_data[27]); + println!("- Deciphering..."); + let deciphered_data = decipher(&decrypted_data[48..], decrypted_data[48..].len()); + + let control_byte = decrypted_data[34]; + let out_data: Vec; + + if control_byte == 3 { + println!("-- Decompressing..."); + out_data = decompress_zlib(&deciphered_data)?; + } else { + println!("-- Uncompressed..."); + out_data = deciphered_data; + } + + let first_byte; + if out_data[1] & 0xF0 == 0xD0 { + first_byte = out_data[1] & 0x0F; + } else { + first_byte = out_data[1]; + } + + let dest_offset = u32::from_be_bytes([first_byte, out_data[2], out_data[3], out_data[4]]); + + let source_offset = u32::from_be_bytes([0x00, out_data[6], out_data[7], out_data[8]]); + + let path: PathBuf; + let msg: &str; + + if filename.starts_with("PEAKS.F") { + if source_offset == 270 { //unique for 2014-2018 files + let embedded_file_name_string = String::from_utf8_lossy(&out_data[14..270]); + let embedded_file_name = embedded_file_name_string.split("\0").next().unwrap(); + println!("--- Embedded file: {}", embedded_file_name); + + let folder_path = Path::new(&output_folder).join("PEAKS"); + fs::create_dir_all(&folder_path)?; + path = Path::new(&folder_path).join(embedded_file_name); + msg = "to PEAKS"; + } else { + path = Path::new(&output_folder).join("PEAKS.bin"); + msg = "to PEAKS.bin"; + } + } else { + path = Path::new(&output_folder).join(filename); + msg = "file"; + } + + fs::create_dir_all(&output_folder)?; + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(path)?; + + file.seek(SeekFrom::Start(dest_offset as u64))?; + file.write_all(&out_data[source_offset as usize..])?; + println!("--- Saved {}!", msg); + + } else { + let out_data = decrypted_data; + if filename.ends_with(".TXT") { + println!("{}", String::from_utf8_lossy(&out_data)); + } else { + let path = Path::new(&output_folder).join(filename); + + fs::create_dir_all(&output_folder)?; + let mut file = OpenOptions::new() + .write(true) + .create(true) + .open(path)?; + + file.write_all(&out_data)?; + println!("-- Saved file!"); + } + } + + offset += size; + } + + println!("\nExtraction finished!"); + + Ok(()) +} \ No newline at end of file diff --git a/src/formats/tpv_timg.rs b/src/formats/tpv_timg.rs index cc88f4f..812db83 100644 --- a/src/formats/tpv_timg.rs +++ b/src/formats/tpv_timg.rs @@ -9,9 +9,7 @@ use crate::common; pub fn is_tpv_timg_file(file: &File) -> bool { let header = common::read_file(&file, 0, 4).expect("Failed to read from file."); - let header_string = String::from_utf8_lossy(&header); - - if header_string == "TIMG"{ + if header == b"TIMG" { true } else { false @@ -27,8 +25,7 @@ fn decompress_gzip(compressed_data: &[u8]) -> Result, Box Result<(), Box> { - //TIMG header - let _timg = common::read_exact(&mut file, 288)?; + let _timg = common::read_exact(&mut file, 288)?; //TIMG magic + header loop { //PIMG @@ -39,48 +36,39 @@ pub fn extract_tpv_timg(mut file: &File, output_folder: &str) -> Result<(), Box< assert!(&pimg == b"PIMG", "Invalid PIMG section!"); } - //4 bytes 00 - let _ = common::read_exact(&mut file, 4)?; + let _ = common::read_exact(&mut file, 4)?; //4 bytes 00 - //4 bytes size let size_bytes = common::read_exact(&mut file, 4)?; let size = u32::from_le_bytes(size_bytes.try_into().unwrap()); - //4 bytes nothing - let _ = common::read_exact(&mut file, 4)?; + let _ = common::read_exact(&mut file, 4)?; //4 bytes 00 - //16 bytes checksum? or maybe signature - let _checksum = common::read_exact(&mut file, 16)?; + let _checksum = common::read_exact(&mut file, 16)?; //16 bytes checksum? or maybe signature - //16 bytes name let name_bytes = common::read_exact(&mut file, 16)?; let name = common::string_from_bytes(&name_bytes); - //64 bytes destination device let dev_bytes = common::read_exact(&mut file, 64)?; let dev = common::string_from_bytes(&dev_bytes); - //16 bytes compression type let comp_bytes = common::read_exact(&mut file, 16)?; let comp_type = common::string_from_bytes(&comp_bytes); + + let _ = common::read_exact(&mut file, 1032)?; //1032 bytes maybe comment? skip this - //1032 bytes maybe comment? skip this - let _ = common::read_exact(&mut file, 1032)?; - - //actual data let data = common::read_exact(&mut file, size as usize)?; - println!("- PIMG: Name: {} Size: {} Dest: {} Compression: {}", name, size, dev, comp_type); + println!("\nPIMG: Name: {}, Size: {}, Dest: {}, Compression: {}", name, size, dev, comp_type); let out_data; if comp_type == "gzip" { - println!("-- Decompressing gzip..."); + println!("- Decompressing gzip..."); out_data = decompress_gzip(&data)?; } else if comp_type == "none" { out_data = data; } else { - println!("-- Warning: unsupported compression type!"); + println!("- Warning: unsupported compression type!"); out_data = data; } @@ -97,8 +85,7 @@ pub fn extract_tpv_timg(mut file: &File, output_folder: &str) -> Result<(), Box< println!("-- Saved file!"); } - println!(); - println!("Extraction finished!"); + println!("\nExtraction finished!"); Ok(()) } diff --git a/src/main.rs b/src/main.rs index c015c8e..4a6e7ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ struct Args { } fn main() -> Result<(), Box> { - println!("unixtract Firmware extractor v0.0"); + println!("unixtract Firmware extractor"); let args = Args::parse(); let target_path = args.input_target; @@ -24,8 +24,7 @@ fn main() -> Result<(), Box> { let path = PathBuf::from(target_path); if path.is_dir() { if formats::samsung_old::is_samsung_old_dir(&path) { - println!("Samsung old firmware dir detected!"); - println!(); + println!("Samsung old firmware dir detected!\n"); formats::samsung_old::extract_samsung_old(&path, &output_path)? } else { println!("Input format not recognized!"); @@ -35,29 +34,29 @@ fn main() -> Result<(), Box> { println!(); - if formats::msd10::is_msd10_file(&file) { + if formats::sddl_sec::is_sddl_sec_file(&file) { + println!("SDDL.SEC file detected!"); + formats::sddl_sec::extract_sddl_sec(&file, &output_path)?; + } else if formats::msd10::is_msd10_file(&file) { println!("MSD10 file detected!"); - println!(); formats::msd10::extract_msd10(&file, &output_path)?; + } else if formats::msd11::is_msd11_file(&file) { + println!("MSD11 file detected!"); + formats::msd11::extract_msd11(&file, &output_path)?; } else if formats::tpv_timg::is_tpv_timg_file(&file) { println!("TPV TIMG file detected!"); - println!(); formats::tpv_timg::extract_tpv_timg(&file, &output_path)?; } else if formats::novatek::is_novatek_file(&file) { println!("Novatek file detected!"); - println!(); formats::novatek::extract_novatek(&file, &output_path)?; } else if formats::epk1::is_epk1_file(&file) { println!("EPK1 file detected!"); - println!(); formats::epk1::extract_epk1(&file, &output_path)?; } else if formats::pfl_upg::is_pfl_upg_file(&file) { println!("PFL UPG file detected!"); - println!(); formats::pfl_upg::extract_pfl_upg(&file, &output_path)?; } else if formats::mstar::is_mstar_file(&file) { println!("Mstar upgrade file detected!"); - println!(); formats::mstar::extract_mstar(&file, &output_path)?; } else { println!("Input format not recognized!");