diff --git a/src/formats/msd10.rs b/src/formats/msd10.rs index 6908915..3996ad3 100644 --- a/src/formats/msd10.rs +++ b/src/formats/msd10.rs @@ -6,6 +6,7 @@ use binrw::{BinRead, BinReaderExt}; use crate::utils::common; use crate::keys; use crate::formats::msd::{decrypt_aes_salted_old, decrypt_aes_salted_tizen, decrypt_aes_tizen}; +use crate::utils::msd_ouith_parser_old::{parse_ouith_blob}; #[derive(BinRead)] struct FileHeader { @@ -37,7 +38,6 @@ struct Section { index: u32, offset: u32, size: u32, - name: String, } #[derive(BinRead)] @@ -55,21 +55,6 @@ impl TizenTocEntry { } } -#[derive(BinRead)] -struct OldTocEntry { - #[br(count = 4)] _magic: Vec, - segment_length: u32, - segment_size: u32, - #[br(count = 26)] _unk: Vec, - name_lenght: u8, - #[br(count = name_lenght)] name_bytes: Vec, -} -impl OldTocEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} - pub fn is_msd10_file(file: &File) -> bool { let header = common::read_file(&file, 0, 6).expect("Failed to read from file."); if header == b"MSDU10" { @@ -84,20 +69,17 @@ pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box = Vec::new(); - for _i in 0..header.section_count { let section: SectionEntry = file.read_le()?; println!("Section {}: offset: {}, size: {}", section.index, section.offset, section.size); - sections.push(Section {index: section.index, offset: section.offset, size: section.size, name: "".to_owned()}); + sections.push(Section {index: section.index, offset: section.offset, size: section.size}); } - let _0 = common::read_exact(&mut file, 4)?; //0000 - let header_count_bytes = common::read_exact(&mut file, 4)?; - let header_count = u32::from_le_bytes(header_count_bytes.try_into().unwrap()); + let _zero_padding = common::read_exact(&mut file, 4)?; + let header_count: u32 = file.read_le()?; println!("\nNumber of headers: {}", header_count); let mut headers: Vec = Vec::new(); - for i in 0..header_count { let header: HeaderEntry = file.read_le()?; println!("Header {}: {}, offset: {}, size: {}", i + 1, header.name(), header.offset, header.size); @@ -168,33 +150,41 @@ pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box Result<(), Box = Vec::new(); - for _i in 0..header.section_count { let section: SectionEntry = file.read_le()?; println!("Section {}: offset: {}, size: {}", section.index, section.offset, section.size); sections.push(Section {index: section.index, offset: section.offset, size: section.size}); } - let header_count_bytes = common::read_exact(&mut file, 4)?; - let header_count = u32::from_le_bytes(header_count_bytes.try_into().unwrap()); + let header_count: u32 = file.read_le()?; println!("\nNumber of headers: {}", header_count); let mut headers: Vec = Vec::new(); - for i in 0..header_count { let header: HeaderEntry = file.read_le()?; println!("Header {}: {}, offset: {}, size: {}", i + 1, header.name(), header.offset, header.size); diff --git a/src/keys.rs b/src/keys.rs index ec0fdc3..84bf573 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -16,11 +16,12 @@ pub static SAMSUNG: &[(&str, &str)] = &[ //MSD10 keys //fw prefix, type, key pub static MSD10: &[(&str, &str, &str)] = &[ - ("T-ECP", "old", "3ef6067262cf0c678598bff22169d1f1ea57c284"), //for T-ECPDEUT - ("T-MST12", "old", "010287362008be691dc8c9f6d2c5ca5aa210ecb8"), //X12 - 2013 Mstar - ("T-MST14", "old", "d145f1d55aee3ef520d194e1275b429c580baacb"), //X14H - 2014 Mstar - ("T-MS14J", "old", "d145f1d55aee3ef520d194e1275b429c580baacb"), //X14J - 2014 Mstar (For 2015 models) - ("T-NT14M", "old", "95d01e0bae861a05695bc8a6edb2ea835a09accd"), //NT14M - 2014 Novatek (low-end) + ("T-ECP", "old", "3ef6067262cf0c678598bff22169d1f1ea57c284"), //EchoP - 2012 Samsung + ("T-MST12", "old", "010287362008be691dc8c9f6d2c5ca5aa210ecb8"), //X12 - 2013 Mstar + ("T-MST14", "old", "d145f1d55aee3ef520d194e1275b429c580baacb"), //X14 - 2014 Mstar + ("T-MS14J", "old", "d145f1d55aee3ef520d194e1275b429c580baacb"), //X14J - 2014 Mstar (For 2015 models) + ("T-M14HK", "old", "d145f1d55aee3ef520d194e1275b429c580baacb"), //X14HK - 2014 Mstar (For 2016 models) + ("T-NT14M", "old", "95d01e0bae861a05695bc8a6edb2ea835a09accd"), //NT14M - 2014 Novatek (low-end) ("T-N14MJ", "old", "95d01e0bae861a05695bc8a6edb2ea835a09accd"), //NT14M_J - 2014 Novatek (low-end) (for 2015 models) //github.com/bugficks/msddecrypt diff --git a/src/utils.rs b/src/utils.rs index 2b69df5..80fb92d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -8,4 +8,5 @@ pub mod compression; pub mod android_ota_update_metadata; pub mod mtk_crypto; pub mod lzhs; -pub mod huffman_tables; \ No newline at end of file +pub mod huffman_tables; +pub mod msd_ouith_parser_old; \ No newline at end of file diff --git a/src/utils/msd_ouith_parser_old.rs b/src/utils/msd_ouith_parser_old.rs new file mode 100644 index 0000000..d6aff53 --- /dev/null +++ b/src/utils/msd_ouith_parser_old.rs @@ -0,0 +1,211 @@ +//MAIN CODE: https://github.com/theubusu/msd_OUITH_parser + +use std::io::{Seek, SeekFrom, Cursor}; +use binrw::{BinRead, BinReaderExt}; + +use crate::utils::common; + +#[derive(BinRead)] +struct ChunkHeader { + size: u32, + value: u32, +} + +#[derive(BinRead)] +struct DescriptorHeader { + tag: u16, + size: u32, +} + +#[derive(BinRead)] +//OUSWFileVersionDesc, OUPartitionVersionDesc, OUCMACDataDesc +struct CommonDestinationInfo { + _name_len: u8, + #[br(count = _name_len)] name_bytes: Vec, + _version: u16, +} +impl CommonDestinationInfo { + fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} + +#[derive(BinRead)] +struct OUAESEncryptionDesc { + _mode: u8, + _key_size: u32, + _salt_size: u32, +} + +#[derive(BinRead)] +struct OURSAValidationDesc { + _mode: u8, + _unknown: u32, + _signature_size: u32, +} + +#[derive(BinRead)] +struct OUSecureHashValidationDesc { + _mode: u8, + _hash_size: u16, + #[br(count = _hash_size)] hash: Vec, +} + +#[derive(BinRead)] +struct OUGroupDesc { + _group_id: u32, + _field_2: u8, + _field_3: u8, +} + +#[derive(BinRead)] +pub struct OUSWImageVersionExDesc { + pub _name_len: u8, + #[br(count = _name_len)] pub name_bytes: Vec, + pub major_ver: u16, + pub minor_ver: u16, + pub date_year: u8, + pub date_month: u8, + pub date_day: u8, +} +impl OUSWImageVersionExDesc { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} + +//this is a struct that will communicate all values important when parsing the MSD file. +pub struct MSDItem { + pub item_id: u32, + pub item_type: u16, //File, Partition, CMACData + pub all_size: u32, //same size as in msd header + pub name: String, + + pub heading_size: u32, //has the signature and salt + pub data_size: u32, + + pub aes_encryption: bool, + //pub crc32_hash: Option, + //pub secure_hash: Option>, +} + +pub fn parse_ouith_blob(blob: &[u8]) -> Result<(Vec, Option), Box> { + let mut reader = Cursor::new(blob); + let mut items: Vec = Vec::new(); + let mut info: Option = None; + + let _signature = common::read_exact(&mut reader, 128)?; //signature included at the beginning of blob in MSD file + + let mut _chunk_n = 0; + while reader.stream_position()? < blob.len() as u64 { + _chunk_n += 1; + let chunk: ChunkHeader = reader.read_be()?; + let chunk_end = reader.stream_position()? + chunk.size as u64; + + //parse top level descriptor. it can only be ID 1(OUUpgradeItemDesc) or 2(OUGroupDesc) + let top_descriptor: DescriptorHeader = reader.read_be()?; + if top_descriptor.tag == 0x01 { + let item_id: u32 = reader.read_be()?; + + //REQUIRED are items in order: OUDestinationDesc(0x03), OUDataProcessingDesc(0x07), OUGroupInfoDesc(0x13). OPTIONAL items: OUDependenciesDesc(0x04), OUDataPostProcessingDesc(0x08) + //In MSD files, no others seem to be used than the required ones. We will ignore all data after required descriptors. + let destination_descriptor: DescriptorHeader = reader.read_be()?; + if destination_descriptor.tag != 0x03 {return Err(format!("Unexpected descriptor type in OUUpgradeItemDesc, Expected: 0x03, Got: 0x{:02x}!", destination_descriptor.tag).into())} + let _out_size: u32 = reader.read_be()?; + + //OUDestinationDesc needs one of OUSWFileVersionDesc(0x0B), OUPartitionVersionDesc(0x0A), OUCMACDataDesc(0x11). Their structure is the same. so we can store the type + let type_descriptor: DescriptorHeader = reader.read_be()?; + if ![0x0B, 0xA, 0x11].contains(&type_descriptor.tag) {return Err(format!("Unexpected descriptor type in OUDestinationDesc, Expected: one of 0x0B, 0x0A, 0x11, Got: 0x{:02x}!", type_descriptor.tag).into())} + let destination_info: CommonDestinationInfo = reader.read_be()?; + + //OUDataProcessingDesc can have OUXOREncryptionDesc(0x0D), OUAESEncryptionDesc(0x0E), OUCompressionDesc(0x0F), OUSecureHashValidationDesc(0x18), OURSAValidationDesc(0x10), OUDataCopyDesc(0x16), OUKeepCurrentDataDesc(0x1E), OUCRC32ValidationDesc(0x12) + let data_processing_descriptor: DescriptorHeader = reader.read_be()?; + if data_processing_descriptor.tag != 0x07 {return Err(format!("Unexpected descriptor type in OUUpgradeItemDesc, Expected: 0x07, Got: 0x{:02x}!", data_processing_descriptor.tag).into())} + let heading_size: u32 = reader.read_be()?; + let data_size: u32 = reader.read_be()?; + + let mut aes_encryption = false; + let mut _crc32_hash: Option = None; + let mut _secure_hash: Option> = None; + + let epos = reader.stream_position()? + (data_processing_descriptor.size - 8) as u64; + while reader.stream_position()? < epos { + let descriptor: DescriptorHeader = reader.read_be()?; + if ![0x0D, 0x0E, 0x0F, 0x18, 0x10, 0x16, 0x1E, 0x12].contains(&descriptor.tag) {return Err(format!("Unexpected descriptor type in OUDataProcessingDesc, Expected: one of 0x0D, 0x0E, 0x0F, 0x18, 0x10, 0x16, 0x1E, 0x12, Got: 0x{:02x}!", descriptor.tag).into())} + if descriptor.tag == 0x0E { + //OUAESEncryptionDesc + let _aes_encryption_desc: OUAESEncryptionDesc = reader.read_be()?; + aes_encryption = true; + } + else if descriptor.tag == 0x10 { + //OURSAValidationDesc + let _rsa_validation_desc: OURSAValidationDesc = reader.read_be()?; + } + else if descriptor.tag == 0x12 { + //OUCRC32ValidationDesc + let crc32: u32 = reader.read_be()?; + _crc32_hash = Some(crc32); + } + else if descriptor.tag == 0x18 { + //OUSecureHashValidationDesc + let secure_hash_validation_desc: OUSecureHashValidationDesc = reader.read_be()?; + _secure_hash = Some(secure_hash_validation_desc.hash); + } + else { + //type not implemented, ignore the data + let _descriptor_data = common::read_exact(&mut reader, descriptor.size as usize); + } + } + + //OUGroupInfoDesc + let group_info_descriptor: DescriptorHeader = reader.read_be()?; + if group_info_descriptor.tag != 0x13 {return Err(format!("Unexpected descriptor type in OUUpgradeItemDesc, Expected: 0x13, Got: 0x{:02x}!", group_info_descriptor.tag).into())} + let _group_id: u32 = reader.read_be()?; + + //create the msd item with all infos + let msd_item = MSDItem { + item_id: item_id, + item_type: type_descriptor.tag, + all_size: chunk.value, + name: destination_info.name(), + heading_size: heading_size, + data_size: data_size, + + aes_encryption: aes_encryption, + //crc32_hash: crc32_hash, + //secure_hash: secure_hash, + }; + items.push(msd_item); + + //go directly to the end of the chunk to skip optional descriptors just in case + reader.seek(SeekFrom::Start(chunk_end))?; + } + + else if top_descriptor.tag == 0x02 { + let _group_desc: OUGroupDesc = reader.read_be()?; + + //OUGroupDesc REQUIRES one of: OUSWImageVersionDesc(0x09), OUSWImageVersionExDesc(0x19), OUOptionalDataVersionDesc(0x14), OUFirmwareVersionDesc(0x15). OPTIONALLY it can also have OUDependenciesDesc + //MSD files seem to exclusively use OUSWImageVersionExDesc + let version_descriptor: DescriptorHeader = reader.read_be()?; + if ![0x09, 0x19, 0x14, 0x15].contains(&version_descriptor.tag) {return Err(format!("Unexpected descriptor type in OUGroupDesc, Expected: one of 0x09, 0x19, 0x14, 0x15, Got: 0x{:02x}!", version_descriptor.tag).into())} + if version_descriptor.tag == 0x19 { + let sw_image_version_ex_desc: OUSWImageVersionExDesc = reader.read_be()?; + + info = Some(sw_image_version_ex_desc); + } + else { + //type not implemented, ignore the data + let _descriptor_data = common::read_exact(&mut reader, version_descriptor.size as usize); + } + + //go directly to the end of the chunk to skip optional descriptors just in case + reader.seek(SeekFrom::Start(chunk_end))?; + } + + else { + return Err(format!("Unexpected top level descriptor type 0x{:02x}!", top_descriptor.tag).into()); + } + } + + Ok((items, info)) //finally, it will return a list of MSD items and info about image if it was collected. +} \ No newline at end of file