diff --git a/src/formats.rs b/src/formats.rs index 47f8a89..5d88d44 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -42,36 +42,154 @@ pub mod mtk_pkg_old; pub mod mtk_pkg_new; pub mod mtk_bdp; +//define all formats here pub fn get_registry() -> Vec { return vec![ - crate::formats::mstar::format(), - crate::formats::samsung_old::format(), - crate::formats::nvt_timg::format(), - crate::formats::pfl_upg::format(), - crate::formats::sddl_sec::format(), - crate::formats::novatek::format(), - crate::formats::ruf::format(), - crate::formats::invincible_image::format(), - crate::formats::slp::format(), - crate::formats::roku::format(), - crate::formats::sony_bdp::format(), - crate::formats::rvp::format(), - crate::formats::funai_upg::format(), - crate::formats::pana_dvd::format(), - crate::formats::android_ota_payload::format(), - crate::formats::bdl::format(), - crate::formats::amlogic::format(), - crate::formats::pup::format(), - crate::formats::msd10::format(), - crate::formats::msd11::format(), - crate::formats::epk::format(), - crate::formats::epk1::format(), - crate::formats::epk2::format(), - crate::formats::epk2b::format(), - crate::formats::epk3::format(), - crate::formats::mtk_pkg::format(), - crate::formats::mtk_pkg_old::format(), - crate::formats::mtk_pkg_new::format(), - crate::formats::mtk_bdp::format(), + Format { + name: "mstar", + detector_func: crate::formats::mstar::is_mstar_file, + extractor_func: crate::formats::mstar::extract_mstar, + }, + Format { + name: "samsung_old", + detector_func: crate::formats::samsung_old::is_samsung_old_dir, + extractor_func: crate::formats::samsung_old::extract_samsung_old, + }, + Format { + name: "nvt_timg", + detector_func: crate::formats::nvt_timg::is_nvt_timg_file, + extractor_func: crate::formats::nvt_timg::extract_nvt_timg, + }, + Format { + name: "pfl_upg", + detector_func: crate::formats::pfl_upg::is_pfl_upg_file, + extractor_func: crate::formats::pfl_upg::extract_pfl_upg, + }, + Format { + name: "sddl_sec", + detector_func: crate::formats::sddl_sec::is_sddl_sec_file, + extractor_func: crate::formats::sddl_sec::extract_sddl_sec, + }, + Format { + name: "novatek", + detector_func: crate::formats::novatek::is_novatek_file, + extractor_func: crate::formats::novatek::extract_novatek, + }, + Format { + name: "ruf", + detector_func: crate::formats::ruf::is_ruf_file, + extractor_func: crate::formats::ruf::extract_ruf, + }, + Format { + name: "invincible_image", + detector_func: crate::formats::invincible_image::is_invincible_image_file, + extractor_func: crate::formats::invincible_image::extract_invincible_image, + }, + Format { + name: "slp", + detector_func: crate::formats::slp::is_slp_file, + extractor_func: crate::formats::slp::extract_slp, + }, + Format { + name: "roku", + detector_func: crate::formats::roku::is_roku_file, + extractor_func: crate::formats::roku::extract_roku, + }, + Format { + name: "sony_bdp", + detector_func: crate::formats::sony_bdp::is_sony_bdp_file, + extractor_func: crate::formats::sony_bdp::extract_sony_bdp, + }, + Format { + name: "rvp", + detector_func: crate::formats::rvp::is_rvp_file, + extractor_func: crate::formats::rvp::extract_rvp, + }, + Format { + name: "funai_upg", + detector_func: crate::formats::funai_upg::is_funai_upg_file, + extractor_func: crate::formats::funai_upg::extract_funai_upg, + }, + Format { + name: "pana_dvd", + detector_func: crate::formats::pana_dvd::is_pana_dvd_file, + extractor_func: crate::formats::pana_dvd::extract_pana_dvd, + }, + Format { + name: "android_ota_payload", + detector_func: crate::formats::android_ota_payload::is_android_ota_payload_file, + extractor_func: crate::formats::android_ota_payload::extract_android_ota_payload, + }, + Format { + name: "bdl", + detector_func: crate::formats::bdl::is_bdl_file, + extractor_func: crate::formats::bdl::extract_bdl, + }, + Format { + name: "amlogic", + detector_func: crate::formats::amlogic::is_amlogic_file, + extractor_func: crate::formats::amlogic::extract_amlogic, + }, + Format { + name: "pup", + detector_func: crate::formats::pup::is_pup_file, + extractor_func: crate::formats::pup::extract_pup, + }, + Format { + name: "msd10", + detector_func: crate::formats::msd10::is_msd10_file, + extractor_func: crate::formats::msd10::extract_msd10, + }, + Format { + name: "msd11", + detector_func: crate::formats::msd11::is_msd11_file, + extractor_func: crate::formats::msd11::extract_msd11, + }, + Format { + name: "epk", + detector_func: crate::formats::epk::is_epk_file, + extractor_func: crate::formats::epk::extract_epk, + }, + Format { + name: "epk1", + detector_func: crate::formats::epk1::is_epk1_file, + extractor_func: crate::formats::epk1::extract_epk1, + }, + Format { + name: "epk2", + detector_func: crate::formats::epk2::is_epk2_file, + extractor_func: crate::formats::epk2::extract_epk2, + }, + Format { + name: "epk2b", + detector_func: crate::formats::epk2b::is_epk2b_file, + extractor_func: crate::formats::epk2b::extract_epk2b, + }, + Format { + name: "epk3", + detector_func: crate::formats::epk3::is_epk3_file, + extractor_func: crate::formats::epk3::extract_epk3, + }, + Format { + name: "mtk_pkg", + detector_func: crate::formats::mtk_pkg::is_mtk_pkg_file, + extractor_func: crate::formats::mtk_pkg::extract_mtk_pkg, + }, + Format { + name: "mtk_pkg_old", + detector_func: crate::formats::mtk_pkg_old::is_mtk_pkg_old_file, + extractor_func: crate::formats::mtk_pkg_old::extract_mtk_pkg_old, + }, + Format { + name: "mtk_pkg_new", + detector_func: crate::formats::mtk_pkg_new::is_mtk_pkg_new_file, + extractor_func: crate::formats::mtk_pkg_new::extract_mtk_pkg_new, + }, + Format { + name: "mtk_bdp", + detector_func: crate::formats::mtk_bdp::is_mtk_bdp_file, + extractor_func: crate::formats::mtk_bdp::extract_mtk_bdp, + }, + ] } \ No newline at end of file diff --git a/src/formats/amlogic/include.rs b/src/formats/amlogic/include.rs new file mode 100644 index 0000000..428115e --- /dev/null +++ b/src/formats/amlogic/include.rs @@ -0,0 +1,39 @@ +use crate::utils::common; +use binrw::{BinRead}; + +#[derive(BinRead)] +pub struct ImageHeader { + _crc32: u32, + pub version: u32, + _magic_bytes: [u8; 4], //56 19 B5 27 + pub image_size: u64, + pub item_align_size: u32, + pub item_count: u32, + _reserved: [u8; 36], +} + +#[derive(BinRead)] +pub struct ItemEntry { + _item_id: u32, + pub file_type: u32, + _current_offset_in_item: u64, + pub offset_in_image: u64, + pub item_size: u64, + item_type_bytes: [u8; 256], + name_bytes: [u8; 256], + _verify: u32, + _is_backup_item: u16, + _backup_item_id: u16, + _reserved: [u8; 24], +} +impl ItemEntry { + pub fn item_type(&self) -> String { + common::string_from_bytes(&self.item_type_bytes) + } + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } + pub fn is_sparse(&self) -> bool { + self.file_type == 254 + } +} \ No newline at end of file diff --git a/src/formats/amlogic.rs b/src/formats/amlogic/mod.rs similarity index 69% rename from src/formats/amlogic.rs rename to src/formats/amlogic/mod.rs index 74e55f1..53b1706 100644 --- a/src/formats/amlogic.rs +++ b/src/formats/amlogic/mod.rs @@ -1,53 +1,15 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "amlogic", detector_func: is_amlogic_file, extractor_func: extract_amlogic } -} +use crate::AppContext; -use std::path::{Path}; +use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; -use crate::utils::sparse::{unsparse_to_file}; - -#[derive(BinRead)] -struct ImageHeader { - _crc32: u32, - version: u32, - #[br(count = 4)] _magic_bytes: Vec, //56 19 B5 27 - image_size: u64, - item_align_size: u32, - item_count: u32, - #[br(count = 36)] _reserved: Vec, -} - -#[derive(BinRead)] -struct ItemEntry { - _item_id: u32, - file_type: u32, - _current_offset_in_item: u64, - offset_in_image: u64, - item_size: u64, - #[br(count = 256)] item_type_bytes: Vec, - #[br(count = 256)] name_bytes: Vec, - _verify: u32, - _is_backup_item: u16, - _backup_item_id: u16, - #[br(count = 24)] _reserved: Vec, -} -impl ItemEntry { - fn item_type(&self) -> String { - common::string_from_bytes(&self.item_type_bytes) - } - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } - fn is_sparse(&self) -> bool { - self.file_type == 254 - } -} +use crate::utils::sparse::unsparse_to_file; +use include::*; pub fn is_amlogic_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/utils/android_ota_update_metadata.rs b/src/formats/android_ota_payload/android_ota_update_metadata.rs similarity index 100% rename from src/utils/android_ota_update_metadata.rs rename to src/formats/android_ota_payload/android_ota_update_metadata.rs diff --git a/src/formats/android_ota_payload/include.rs b/src/formats/android_ota_payload/include.rs new file mode 100644 index 0000000..e508bbd --- /dev/null +++ b/src/formats/android_ota_payload/include.rs @@ -0,0 +1,9 @@ +use binrw::{BinRead}; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 4], //CrAU + pub file_format_version: u64, + pub manifest_size: u64, + pub metadata_signature_size: u32, +} \ No newline at end of file diff --git a/src/formats/android_ota_payload.rs b/src/formats/android_ota_payload/mod.rs similarity index 86% rename from src/formats/android_ota_payload.rs rename to src/formats/android_ota_payload/mod.rs index 0fa13fd..adcc919 100644 --- a/src/formats/android_ota_payload.rs +++ b/src/formats/android_ota_payload/mod.rs @@ -1,26 +1,18 @@ +mod include; +mod android_ota_update_metadata; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "android_ota_payload", detector_func: is_android_ota_payload_file, extractor_func: extract_android_ota_payload } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; -use std::path::{Path}; -use std::io::{Write}; -use binrw::{BinRead, BinReaderExt}; +use std::path::Path; +use std::io::Write; +use binrw::BinReaderExt; use prost::Message; use crate::utils::common; -use crate::utils::android_ota_update_metadata::{DeltaArchiveManifest, install_operation}; +use android_ota_update_metadata::{DeltaArchiveManifest, install_operation}; use crate::utils::compression::{decompress_bzip, decompress_xz}; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] _magic_bytes: Vec, //CrAU - file_format_version: u64, - manifest_size: u64, - metadata_signature_size: u32, -} +use include::*; pub fn is_android_ota_payload_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/bdl/include.rs b/src/formats/bdl/include.rs new file mode 100644 index 0000000..3f94337 --- /dev/null +++ b/src/formats/bdl/include.rs @@ -0,0 +1,76 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct BdlHeader { + _magic_bytes: [u8; 4], //ibdl + _file_version: [u8; 8], + _unk1: u32, + pub pkg_count: u32, + _unk2: [u8; 12], + date_bytes: [u8; 256], + manufacturer_bytes: [u8; 256], + model_bytes: [u8; 256], + _unk3: [u8; 9], + version_bytes: [u8; 256], + info_bytes: [u8; 1280], +} +impl BdlHeader { + pub fn date(&self) -> String { + common::string_from_bytes(&self.date_bytes) + } + pub fn manufacturer(&self) -> String { + common::string_from_bytes(&self.manufacturer_bytes) + } + pub fn model(&self) -> String { + common::string_from_bytes(&self.model_bytes) + } + pub fn version(&self) -> String { + common::string_from_bytes(&self.version_bytes) + } + pub fn info(&self) -> String { + common::string_from_bytes(&self.info_bytes) + } +} + +#[derive(BinRead)] +pub struct PkgListEntry { + pub offset: u64, + pub size: u64, +} + +#[derive(BinRead)] +pub struct PkgHeader { + _magic_bytes: [u8; 4], //ipkg + _unk1: [u8; 12], + pub entry_count: u32, + _unk2: [u8; 12], + version_bytes: [u8; 256], + manufacturer_bytes: [u8; 256], + name_bytes: [u8; 256], + _unk3: [u8; 285], +} +impl PkgHeader { + pub fn version(&self) -> String { + common::string_from_bytes(&self.version_bytes) + } + pub fn manufacturer(&self) -> String { + common::string_from_bytes(&self.manufacturer_bytes) + } + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} + +#[derive(BinRead)] +pub struct PkgEntry { + name_bytes: [u8; 256], + pub offset: u64, + pub size: u64, + _crc32: u32, +} +impl PkgEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} \ No newline at end of file diff --git a/src/formats/bdl.rs b/src/formats/bdl/mod.rs similarity index 55% rename from src/formats/bdl.rs rename to src/formats/bdl/mod.rs index bd7952f..4445cae 100644 --- a/src/formats/bdl.rs +++ b/src/formats/bdl/mod.rs @@ -1,89 +1,14 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "bdl", detector_func: is_bdl_file, extractor_func: extract_bdl } -} +use crate::AppContext; -use std::path::{Path}; +use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; - -#[derive(BinRead)] -struct BdlHeader { - #[br(count = 4)] _magic_bytes: Vec, //ibdl - #[br(count = 8)] _file_version: Vec, - _unk1: u32, - pkg_count: u32, - #[br(count = 12)] _unk2: Vec, - #[br(count = 256)] date_bytes: Vec, - #[br(count = 256)] manufacturer_bytes: Vec, - #[br(count = 256)] model_bytes: Vec, - #[br(count = 9)] _unk3: Vec, - #[br(count = 256)] version_bytes: Vec, - #[br(count = 1280)] info_bytes: Vec, -} -impl BdlHeader { - fn date(&self) -> String { - common::string_from_bytes(&self.date_bytes) - } - fn manufacturer(&self) -> String { - common::string_from_bytes(&self.manufacturer_bytes) - } - fn model(&self) -> String { - common::string_from_bytes(&self.model_bytes) - } - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn info(&self) -> String { - common::string_from_bytes(&self.info_bytes) - } -} - -#[derive(BinRead)] -struct PkgListEntry { - offset: u64, - size: u64, -} - -#[derive(BinRead)] -struct PkgHeader { - #[br(count = 4)] _magic_bytes: Vec, //ipkg - #[br(count = 12)] _unk1: Vec, - entry_count: u32, - #[br(count = 12)] _unk2: Vec, - #[br(count = 256)] version_bytes: Vec, - #[br(count = 256)] manufacturer_bytes: Vec, - #[br(count = 256)] name_bytes: Vec, - #[br(count = 285)] _unk3: Vec, -} -impl PkgHeader { - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn manufacturer(&self) -> String { - common::string_from_bytes(&self.manufacturer_bytes) - } - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} - -#[derive(BinRead)] -struct PkgEntry { - #[br(count = 256)] name_bytes: Vec, - offset: u64, - size: u64, - _crc32: u32, -} -impl PkgEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} +use include::*; pub fn is_bdl_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/epk.rs b/src/formats/epk/mod.rs similarity index 96% rename from src/formats/epk.rs rename to src/formats/epk/mod.rs index 9307d0b..d23fbec 100644 --- a/src/formats/epk.rs +++ b/src/formats/epk/mod.rs @@ -1,8 +1,5 @@ use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "epk", detector_func: is_epk_file, extractor_func: extract_epk } -} +use crate::AppContext; use crate::utils::common; use crate::formats; diff --git a/src/formats/epk1/include.rs b/src/formats/epk1/include.rs new file mode 100644 index 0000000..43cad85 --- /dev/null +++ b/src/formats/epk1/include.rs @@ -0,0 +1,31 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct CommonHeader { + _magic_bytes: [u8; 4], + pub file_size: u32, + pub pak_count: u32, +} + +#[derive(BinRead)] +pub struct PakHeader { + pak_name_bytes: [u8; 4], + pub image_size: u32, + platform_id_bytes: [u8; 64], + _reserved: [u8; 56], +} +impl PakHeader { + pub fn pak_name(&self) -> String { + common::string_from_bytes(&self.pak_name_bytes) + } + pub fn platform_id(&self) -> String { + common::string_from_bytes(&self.platform_id_bytes) + } +} + +#[derive(BinRead)] +pub struct Pak { + pub offset : u32, + pub size : u32, +} \ No newline at end of file diff --git a/src/formats/epk1.rs b/src/formats/epk1/mod.rs similarity index 84% rename from src/formats/epk1.rs rename to src/formats/epk1/mod.rs index 12a177b..110671c 100644 --- a/src/formats/epk1.rs +++ b/src/formats/epk1/mod.rs @@ -1,44 +1,14 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "epk1", detector_func: is_epk1_file, extractor_func: extract_epk1 } -} +use crate::AppContext; -use std::path::{Path}; +use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; - -#[derive(BinRead)] -struct CommonHeader { - #[br(count = 4)] _magic_bytes: Vec, - file_size: u32, - pak_count: u32, -} - -#[derive(BinRead)] -struct PakHeader { - #[br(count = 4)] pak_name_bytes: Vec, - image_size: u32, - #[br(count = 64)] platform_id_bytes: Vec, - #[br(count = 56)] _reserved: Vec, -} -impl PakHeader { - fn pak_name(&self) -> String { - common::string_from_bytes(&self.pak_name_bytes) - } - fn platform_id(&self) -> String { - common::string_from_bytes(&self.platform_id_bytes) - } -} - -#[derive(BinRead)] -struct Pak { - offset : u32, - size : u32, -} +use include::*; pub fn is_epk1_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/epk2/include.rs b/src/formats/epk2/include.rs new file mode 100644 index 0000000..47a1bfb --- /dev/null +++ b/src/formats/epk2/include.rs @@ -0,0 +1,59 @@ +use crate::utils::common; +use binrw::BinRead; + +pub static SIGNATURE_SIZE: u32 = 128; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 4], //epak + pub file_size: u32, + pub pak_count: u32, + _epk2_magic: [u8; 4], //EPK2 + pub version: [u8; 4], + ota_id_bytes: [u8; 32], +} +impl Header { + pub fn ota_id(&self) -> String { + common::string_from_bytes(&self.ota_id_bytes) + } +} + +#[derive(BinRead)] +pub struct PakEntry { + pub offset: u32, + pub size: u32, + name_bytes: [u8; 4], + _version: [u8; 4], + pub segment_size: u32, +} +impl PakEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} + +#[derive(BinRead)] +pub struct PakHeader { + _pak_name_bytes: [u8; 4], + pub image_size: u32, + platform_id_bytes: [u8; 64], + _sw_version: u32, + _sw_date: u32, + _build_type: u32, + pub segment_count: u32, + pub segment_size: u32, + pub segment_index: u32, + _pak_magic_bytes: [u8; 4], //MPAK + _reserved: [u8; 24], + _segment_crc32: u32, +} +impl PakHeader { + pub fn platform_id(&self) -> String { + common::string_from_bytes(&self.platform_id_bytes) + } +} +pub struct Pak { + pub offset: u32, + pub _size: u32, + pub name: String, +} \ No newline at end of file diff --git a/src/formats/epk2.rs b/src/formats/epk2/mod.rs similarity index 79% rename from src/formats/epk2.rs rename to src/formats/epk2/mod.rs index 843ffc5..dd0e8d0 100644 --- a/src/formats/epk2.rs +++ b/src/formats/epk2/mod.rs @@ -1,75 +1,16 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "epk2", detector_func: is_epk2_file, extractor_func: extract_epk2 } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; -use std::path::{Path}; +use std::path::Path; use std::io::{Write, Seek, SeekFrom, Cursor}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::keys; use crate::formats::epk::{decrypt_aes_ecb_auto, find_key}; - -static SIGNATURE_SIZE: u32 = 128; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] _magic_bytes: Vec, //epak - file_size: u32, - pak_count: u32, - #[br(count = 4)] _epk2_magic: Vec, //EPK2 - #[br(count = 4)] version: Vec, - #[br(count = 32)] ota_id_bytes: Vec, -} -impl Header { - fn ota_id(&self) -> String { - common::string_from_bytes(&self.ota_id_bytes) - } -} - -#[derive(BinRead)] -struct PakEntry { - offset: u32, - size: u32, - #[br(count = 4)] name_bytes: Vec, - #[br(count = 4)] _version: Vec, - segment_size: u32, -} -impl PakEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} - -#[derive(BinRead)] -struct PakHeader { - #[br(count = 4)] _pak_name_bytes: Vec, - image_size: u32, - #[br(count = 64)] platform_id_bytes: Vec, - _sw_version: u32, - _sw_date: u32, - _build_type: u32, - segment_count: u32, - segment_size: u32, - segment_index: u32, - #[br(count = 4)] _pak_magic_bytes: Vec, //MPAK - #[br(count = 24)] _reserved: Vec, - _segment_crc32: u32, -} -impl PakHeader { - fn platform_id(&self) -> String { - common::string_from_bytes(&self.platform_id_bytes) - } -} - -struct Pak { - offset: u32, - _size: u32, - name: String, -} +use include::*; pub fn is_epk2_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -86,8 +27,6 @@ pub fn extract_epk2(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let mut file = app_ctx.file().ok_or("Extractor expected file")?; file.seek(SeekFrom::Start(0))?; - let _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?; - let _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?; let stored_header = common::read_exact(&mut file, 1584)?; //max header size let header; diff --git a/src/formats/epk2b/include.rs b/src/formats/epk2b/include.rs new file mode 100644 index 0000000..71954c0 --- /dev/null +++ b/src/formats/epk2b/include.rs @@ -0,0 +1,47 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct EpkHeader { + _epk_magic: [u8; 4], //epak + pub file_size: u32, + pub pak_count: u32, + _epk2_magic: [u8; 4], //EPK2 + pub version: [u8; 4], + ota_id_bytes: [u8; 32], +} +impl EpkHeader { + pub fn ota_id(&self) -> String { + common::string_from_bytes(&self.ota_id_bytes) + } +} + +#[derive(BinRead)] +pub struct PakHeader { + pak_name_bytes: [u8; 4], + pub image_size: u32, + platform_id_bytes: [u8; 64], + _sw_version: u32, + _sw_date: u32, + _build_type: u32, + pub segment_count: u32, + pub segment_size: u32, + pub segment_index: u32, + _pak_magic_bytes: [u8; 4], //MPAK + _reserved: [u8; 24], + _segment_crc32: u32, +} +impl PakHeader { + pub fn pak_name(&self) -> String { + common::string_from_bytes(&self.pak_name_bytes) + } + pub fn platform_id(&self) -> String { + common::string_from_bytes(&self.platform_id_bytes) + } +} + +#[derive(BinRead)] +pub struct Pak { + pub offset : u32, + pub size : u32, +} \ No newline at end of file diff --git a/src/formats/epk2b.rs b/src/formats/epk2b/mod.rs similarity index 71% rename from src/formats/epk2b.rs rename to src/formats/epk2b/mod.rs index ef39257..37ff92b 100644 --- a/src/formats/epk2b.rs +++ b/src/formats/epk2b/mod.rs @@ -1,60 +1,14 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "epk2b", detector_func: is_epk2b_file, extractor_func: extract_epk2b } -} +use crate::AppContext; use std::path::{Path}; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; - -#[derive(BinRead)] -struct EpkHeader { - #[br(count = 4)] _epk_magic: Vec, //epak - file_size: u32, - pak_count: u32, - #[br(count = 4)] _epk2_magic: Vec, //EPK2 - #[br(count = 4)] version: Vec, - #[br(count = 32)] ota_id_bytes: Vec, -} -impl EpkHeader { - fn ota_id(&self) -> String { - common::string_from_bytes(&self.ota_id_bytes) - } -} - -#[derive(BinRead)] -struct PakHeader { - #[br(count = 4)] pak_name_bytes: Vec, - image_size: u32, - #[br(count = 64)] platform_id_bytes: Vec, - _sw_version: u32, - _sw_date: u32, - _build_type: u32, - segment_count: u32, - segment_size: u32, - segment_index: u32, - #[br(count = 4)] _pak_magic_bytes: Vec, //MPAK - #[br(count = 24)] _reserved: Vec, - _segment_crc32: u32, -} -impl PakHeader { - fn pak_name(&self) -> String { - common::string_from_bytes(&self.pak_name_bytes) - } - fn platform_id(&self) -> String { - common::string_from_bytes(&self.platform_id_bytes) - } -} - -#[derive(BinRead)] -struct Pak { - offset : u32, - size : u32, -} +use include::*; pub fn is_epk2b_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/epk3/include.rs b/src/formats/epk3/include.rs new file mode 100644 index 0000000..c0972d0 --- /dev/null +++ b/src/formats/epk3/include.rs @@ -0,0 +1,64 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 4], //EPK3 + pub version: [u8; 4], + ota_id_bytes: [u8; 32], + pub package_info_size: u32, + _bchunked: u32, +} +impl Header { + pub fn ota_id(&self) -> String { + common::string_from_bytes(&self.ota_id_bytes) + } +} + +#[derive(BinRead)] +pub struct HeaderNewEx { + _pak_info_magic: [u8; 4], + encrypt_type_bytes: [u8; 6], + update_type_bytes: [u8; 6], + pub update_platform_version: f32, + pub compatible_minimum_version: f32, + pub need_to_check_compatible_version: i32, +} +impl HeaderNewEx { + pub fn encrypt_type(&self) -> String { + common::string_from_bytes(&self.encrypt_type_bytes) + } + pub fn update_type(&self) -> String { + common::string_from_bytes(&self.update_type_bytes) + } +} + +#[derive(BinRead)] +pub struct PkgInfoHeader { + pub package_info_list_size: u32, + pub package_info_count: u32, +} + +#[derive(BinRead)] +pub struct PkgInfoEntry { + _package_type: u32, + _package_info_size: u32, + package_name_bytes: [u8; 128], + _package_version_bytes: [u8; 96], + _package_architecture_bytes: [u8; 32], + _checksum: [u8; 32], + pub package_size: u32, + _dipk: u32, + //segment info + _is_segmented: u32, + pub segment_index: u32, + pub segment_count: u32, + pub segment_size: u32, + // + _unk: u32, +} +impl PkgInfoEntry { + pub fn package_name(&self) -> String { + common::string_from_bytes(&self.package_name_bytes) + } +} diff --git a/src/formats/epk3.rs b/src/formats/epk3/mod.rs similarity index 72% rename from src/formats/epk3.rs rename to src/formats/epk3/mod.rs index ed935fb..4858c7a 100644 --- a/src/formats/epk3.rs +++ b/src/formats/epk3/mod.rs @@ -1,80 +1,16 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "epk3", detector_func: is_epk3_file, extractor_func: extract_epk3 } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; -use std::path::{Path}; +use std::path::Path; use std::io::{Write, Seek, SeekFrom, Cursor}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::keys; use crate::formats::epk::{decrypt_aes_ecb_auto, find_key}; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] _magic_bytes: Vec, //EPK3 - #[br(count = 4)] version: Vec, - #[br(count = 32)] ota_id_bytes: Vec, - package_info_size: u32, - _bchunked: u32, -} -impl Header { - fn ota_id(&self) -> String { - common::string_from_bytes(&self.ota_id_bytes) - } -} - -#[derive(BinRead)] -struct HeaderNewEx { - #[br(count = 4)] _pak_info_magic: Vec, - #[br(count = 6)] encrypt_type_bytes: Vec, - #[br(count = 6)] update_type_bytes: Vec, - update_platform_version: f32, - compatible_minimum_version: f32, - need_to_check_compatible_version: i32, -} -impl HeaderNewEx { - fn encrypt_type(&self) -> String { - common::string_from_bytes(&self.encrypt_type_bytes) - } - fn update_type(&self) -> String { - common::string_from_bytes(&self.update_type_bytes) - } -} - - -#[derive(BinRead)] -struct PkgInfoHeader { - package_info_list_size: u32, - package_info_count: u32, -} - -#[derive(BinRead)] -struct PkgInfoEntry { - _package_type: u32, - _package_info_size: u32, - #[br(count = 128)] package_name_bytes: Vec, - #[br(count = 96)] _package_version_bytes: Vec, - #[br(count = 32)] _package_architecture_bytes: Vec, - #[br(count = 32)] _checksum: Vec, - package_size: u32, - _dipk: u32, - //segment info - _is_segmented: u32, - segment_index: u32, - segment_count: u32, - segment_size: u32, - // - _unk: u32, -} -impl PkgInfoEntry { - fn package_name(&self) -> String { - common::string_from_bytes(&self.package_name_bytes) - } -} +use include::*; pub fn is_epk3_file(_app_ctx: &AppContext) -> Result>, Box> { Ok(None) diff --git a/src/formats/funai_upg/include.rs b/src/formats/funai_upg/include.rs new file mode 100644 index 0000000..08331ae --- /dev/null +++ b/src/formats/funai_upg/include.rs @@ -0,0 +1,15 @@ +use binrw::BinRead; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 6], + pub entry_count: u16, + pub file_size: u32, +} + +#[derive(BinRead)] +pub struct Entry { + pub entry_type: u16, + pub entry_size: u32, + _unk: u16, +} \ No newline at end of file diff --git a/src/formats/funai_upg.rs b/src/formats/funai_upg/mod.rs similarity index 79% rename from src/formats/funai_upg.rs rename to src/formats/funai_upg/mod.rs index 31c86c3..b721f2a 100644 --- a/src/formats/funai_upg.rs +++ b/src/formats/funai_upg/mod.rs @@ -1,29 +1,14 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "funai_upg", detector_func: is_funai_upg_file, extractor_func: extract_funai_upg } -} +use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; -use std::io::{Write}; -use binrw::{BinRead, BinReaderExt}; +use std::io::Write; +use binrw::BinReaderExt; use crate::utils::common; - -#[derive(BinRead)] -struct Header { - #[br(count = 6)] _magic_bytes: Vec, - entry_count: u16, - file_size: u32, -} - -#[derive(BinRead)] -struct Entry { - entry_type: u16, - entry_size: u32, - _unk: u16, -} +use include::*; pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/invincible_image/include.rs b/src/formats/invincible_image/include.rs new file mode 100644 index 0000000..5faa69d --- /dev/null +++ b/src/formats/invincible_image/include.rs @@ -0,0 +1,56 @@ +use crate::utils::common; +use binrw::BinRead; + +//v3 key + iv +pub static V3_KEY: [u8; 16] = [0x32, 0xe5, 0x26, 0x1e, 0x22, 0x67, 0x5e, 0x93, 0x20, 0xcf, 0x35, 0x91, 0x7c, 0x63, 0x7a, 0x36]; +pub static V3_IV: [u8; 16] = [0xe3, 0x9f, 0x36, 0x39, 0x56, 0x9a, 0x6b, 0x8d, 0x3f, 0x2e, 0xc9, 0x44, 0xd9, 0xbc, 0xec, 0x43]; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 16], + pub file_version: [u8; 4], + _unk1: u32, + ver1_bytes: [u8; 16], + ver2_bytes: [u8; 16], + _unk2: u16, + _type: u8, + pub keep_size: u32, + _unk3: u8, + pub data_start_offset: u32, + pub data_size: u32, + _data_size_2: u32, + pub skip_size: u32, + _unk4: u16, + _encryption_method: u8, // 0x01 - AES128, 0x02 - AES256 + _hash_type: u8, // 0x01 - MD5, 0x02 - SHA1 + ver3_bytes: [u8; 16], + ver4_bytes: [u8; 16], + _unk6: [u8; 11], + pub payload_count: u8, +} +impl Header { + pub fn ver1(&self) -> String { + common::string_from_bytes(&self.ver1_bytes).replace('\n', "") + } + pub fn ver2(&self) -> String { + common::string_from_bytes(&self.ver2_bytes).replace('\n', "") + } + pub fn ver3(&self) -> String { + common::string_from_bytes(&self.ver3_bytes).replace('\n', "") + } + pub fn ver4(&self) -> String { + common::string_from_bytes(&self.ver4_bytes).replace('\n', "") + } +} + +#[derive(BinRead)] +pub struct Entry { + name_bytes: [u8; 16], + pub start_offset: u32, + pub size: u32, +} +impl Entry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} \ No newline at end of file diff --git a/src/formats/invincible_image.rs b/src/formats/invincible_image/mod.rs similarity index 60% rename from src/formats/invincible_image.rs rename to src/formats/invincible_image/mod.rs index 33a0247..fae3853 100644 --- a/src/formats/invincible_image.rs +++ b/src/formats/invincible_image/mod.rs @@ -1,66 +1,15 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "invincible_image", detector_func: is_invincible_image_file, extractor_func: extract_invincible_image } -} +use crate::AppContext; use std::path::{Path}; use std::fs::{self, OpenOptions}; use std::io::{Write, Read, Seek, SeekFrom, Cursor}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::utils::common; - -#[derive(BinRead)] -struct Header { - #[br(count = 16)] _magic_bytes: Vec, - #[br(count = 4)] file_version: Vec, - _unk1: u32, - #[br(count = 16)] ver1_bytes: Vec, - #[br(count = 16)] ver2_bytes: Vec, - _unk2: u16, - _type: u8, - keep_size: u32, - _unk3: u8, - data_start_offset: u32, - data_size: u32, - _data_size_2: u32, - skip_size: u32, - _unk4: u16, - _encryption_method: u8, // 0x01 - AES128, 0x02 - AES256 - _hash_type: u8, // 0x01 - MD5, 0x02 - SHA1 - #[br(count = 16)] ver3_bytes: Vec, - #[br(count = 16)] ver4_bytes: Vec, - #[br(count = 11)] _unk6: Vec, - payload_count: u8, -} -impl Header { - fn ver1(&self) -> String { - common::string_from_bytes(&self.ver1_bytes).replace('\n', "") - } - fn ver2(&self) -> String { - common::string_from_bytes(&self.ver2_bytes).replace('\n', "") - } - fn ver3(&self) -> String { - common::string_from_bytes(&self.ver3_bytes).replace('\n', "") - } - fn ver4(&self) -> String { - common::string_from_bytes(&self.ver4_bytes).replace('\n', "") - } -} - -#[derive(BinRead)] -struct Entry { - #[br(count = 16)] name_bytes: Vec, - start_offset: u32, - size: u32, -} -impl Entry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} +use include::*; pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -108,12 +57,8 @@ pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Box) -> Res file.seek(SeekFrom::Current(header.skip_size.into()))?; } - //v3 key + iv - let key: [u8; 16] = [0x32, 0xe5, 0x26, 0x1e, 0x22, 0x67, 0x5e, 0x93, 0x20, 0xcf, 0x35, 0x91, 0x7c, 0x63, 0x7a, 0x36]; - let iv: [u8; 16] = [0xe3, 0x9f, 0x36, 0x39, 0x56, 0x9a, 0x6b, 0x8d, 0x3f, 0x2e, 0xc9, 0x44, 0xd9, 0xbc, 0xec, 0x43]; - println!("\nDecrypting data..."); - let decrypted_data = decrypt_aes128_cbc_nopad(&encrypted_data, &key, &iv)?; + let decrypted_data = decrypt_aes128_cbc_nopad(&encrypted_data, &V3_KEY, &V3_IV)?; let mut data_reader = Cursor::new(decrypted_data); diff --git a/src/formats/msd.rs b/src/formats/msd/mod.rs similarity index 94% rename from src/formats/msd.rs rename to src/formats/msd/mod.rs index 54196db..230265b 100644 --- a/src/formats/msd.rs +++ b/src/formats/msd/mod.rs @@ -1,3 +1,8 @@ +//MSD OUITH parsers +pub mod msd_ouith_parser_old; +pub mod msd_ouith_parser_tizen_1_8; +pub mod msd_ouith_parser_tizen_1_9; + // COMMON MSD FUNCTIONS use aes::Aes128; use cbc::{Decryptor, cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit}}; diff --git a/src/utils/msd_ouith_parser_old.rs b/src/formats/msd/msd_ouith_parser_old.rs similarity index 100% rename from src/utils/msd_ouith_parser_old.rs rename to src/formats/msd/msd_ouith_parser_old.rs diff --git a/src/utils/msd_ouith_parser_tizen_1_8.rs b/src/formats/msd/msd_ouith_parser_tizen_1_8.rs similarity index 100% rename from src/utils/msd_ouith_parser_tizen_1_8.rs rename to src/formats/msd/msd_ouith_parser_tizen_1_8.rs diff --git a/src/utils/msd_ouith_parser_tizen_1_9.rs b/src/formats/msd/msd_ouith_parser_tizen_1_9.rs similarity index 100% rename from src/utils/msd_ouith_parser_tizen_1_9.rs rename to src/formats/msd/msd_ouith_parser_tizen_1_9.rs diff --git a/src/formats/msd10/include.rs b/src/formats/msd10/include.rs new file mode 100644 index 0000000..4642fea --- /dev/null +++ b/src/formats/msd10/include.rs @@ -0,0 +1,28 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct FileHeader { + _magic_bytes: [u8; 6], + pub section_count: u32 +} + +#[derive(BinRead)] +pub struct SectionEntry { + pub index: u32, + pub offset: u32, + pub size: u32, +} + +#[derive(BinRead)] +pub struct HeaderEntry { + pub offset: u32, + pub size: u32, + _name_length: u8, + #[br(count = _name_length)] name_bytes: Vec, +} +impl HeaderEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} \ No newline at end of file diff --git a/src/formats/msd10.rs b/src/formats/msd10/mod.rs similarity index 85% rename from src/formats/msd10.rs rename to src/formats/msd10/mod.rs index 49acb85..7b99408 100644 --- a/src/formats/msd10.rs +++ b/src/formats/msd10/mod.rs @@ -1,51 +1,18 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "msd10", detector_func: is_msd10_file, extractor_func: extract_msd10 } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; -use std::path::{Path}; +use std::path::Path; use std::io::{Write, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::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}; -use crate::utils::msd_ouith_parser_tizen_1_8::{parse_blob_1_8}; - -#[derive(BinRead)] -struct FileHeader { - #[br(count = 6)] _magic_bytes: Vec, - section_count: u32 -} - -#[derive(BinRead)] -struct SectionEntry { - index: u32, - offset: u32, - size: u32, -} - -#[derive(BinRead)] -struct HeaderEntry { - offset: u32, - size: u32, - _name_length: u8, - #[br(count = _name_length)] name_bytes: Vec, -} -impl HeaderEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} - -struct Section { - index: u32, - offset: u32, - size: u32, -} +use crate::formats::msd::msd_ouith_parser_old::{parse_ouith_blob}; +use crate::formats::msd::msd_ouith_parser_tizen_1_8::{parse_blob_1_8}; +use include::*; pub fn is_msd10_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -64,11 +31,11 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let header: FileHeader = file.read_le()?; println!("\nNumber of sections: {}", header.section_count); - let mut sections: Vec
= Vec::new(); + let mut sections: Vec = 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}); + sections.push(section); } let _zero_padding = common::read_exact(&mut file, 4)?; diff --git a/src/formats/msd11/include.rs b/src/formats/msd11/include.rs new file mode 100644 index 0000000..75dc4d8 --- /dev/null +++ b/src/formats/msd11/include.rs @@ -0,0 +1,30 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct FileHeader { + _magic_bytes: [u8; 6], + _header_checksum: u32, //CRC32 of data with the size of "_header_size" + _header_size: u64, + pub section_count: u32, +} + +#[derive(BinRead)] +pub struct SectionEntry { + pub index: u32, + pub offset: u64, + pub size: u64, +} + +#[derive(BinRead)] +pub struct HeaderEntry { + pub offset: u64, + pub size: u32, + _name_length: u8, + #[br(count = _name_length)] name_bytes: Vec, +} +impl HeaderEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} \ No newline at end of file diff --git a/src/formats/msd11.rs b/src/formats/msd11/mod.rs similarity index 76% rename from src/formats/msd11.rs rename to src/formats/msd11/mod.rs index 1c5bd1d..6b87cb3 100644 --- a/src/formats/msd11.rs +++ b/src/formats/msd11/mod.rs @@ -1,52 +1,17 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "msd11", detector_func: is_msd11_file, extractor_func: extract_msd11 } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; -use std::path::{Path}; -use std::io::{Write}; -use binrw::{BinRead, BinReaderExt}; +use std::path::Path; +use std::io::Write; +use binrw::BinReaderExt; use crate::utils::common; use crate::keys; use crate::formats::msd::{decrypt_aes_salted_tizen, decrypt_aes_tizen}; -use crate::utils::msd_ouith_parser_tizen_1_9::{parse_blob_1_9}; - -#[derive(BinRead)] -struct FileHeader { - #[br(count = 6)] _magic_bytes: Vec, - _header_checksum: u32, //CRC32 of data with the size of "_header_size" - _header_size: u64, - section_count: u32, -} - -#[derive(BinRead)] -struct SectionEntry { - index: u32, - offset: u64, - size: u64, -} - -#[derive(BinRead)] -struct HeaderEntry { - offset: u64, - size: u32, - _name_length: u8, - #[br(count = _name_length)] name_bytes: Vec, -} -impl HeaderEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} - -struct Section { - index: u32, - offset: u64, - size: u64, -} +use crate::formats::msd::msd_ouith_parser_tizen_1_9::{parse_blob_1_9}; +use include::*; pub fn is_msd11_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -65,11 +30,11 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box let header: FileHeader = file.read_le()?; println!("\nNumber of sections: {}", header.section_count); - let mut sections: Vec
= Vec::new(); + let mut sections: Vec = 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}); + sections.push(section); } let header_count: u32 = file.read_le()?; diff --git a/src/formats/mstar/include.rs b/src/formats/mstar/include.rs new file mode 100644 index 0000000..f51b1b4 --- /dev/null +++ b/src/formats/mstar/include.rs @@ -0,0 +1,7 @@ +pub fn parse_number(s: &str) -> Option { + if let Some(hex_str) = s.strip_prefix("0x") { + u64::from_str_radix(hex_str, 16).ok() + } else { + u64::from_str_radix(s, 16).ok() + } +} \ No newline at end of file diff --git a/src/formats/mstar.rs b/src/formats/mstar/mod.rs similarity index 94% rename from src/formats/mstar.rs rename to src/formats/mstar/mod.rs index 81b68cb..906dce0 100644 --- a/src/formats/mstar.rs +++ b/src/formats/mstar/mod.rs @@ -1,17 +1,16 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "mstar", detector_func: is_mstar_file, extractor_func: extract_mstar } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; -use std::path::{Path}; -use std::io::{Write}; +use std::path::Path; +use std::io::Write; use crate::utils::common; use crate::utils::compression::{decompress_lzma, decompress_lz4}; use crate::utils::lzop::{unlzop_to_file}; use crate::utils::sparse::{unsparse_to_file}; +use include::*; pub fn is_mstar_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -25,14 +24,6 @@ pub fn is_mstar_file(app_ctx: &AppContext) -> Result>, Box Option { - if let Some(hex_str) = s.strip_prefix("0x") { - u64::from_str_radix(hex_str, 16).ok() - } else { - u64::from_str_radix(s, 16).ok() - } -} - pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { let file = app_ctx.file().ok_or("Extractor expected file")?; diff --git a/src/formats/mtk_bdp/include.rs b/src/formats/mtk_bdp/include.rs new file mode 100644 index 0000000..6a2686c --- /dev/null +++ b/src/formats/mtk_bdp/include.rs @@ -0,0 +1,65 @@ +use crate::utils::common; +use binrw::BinRead; + +pub fn find_bytes(data: &[u8], pattern: &[u8]) -> Option { + data.windows(pattern.len()).position(|window| window == pattern) +} + +pub static PITIT_MAGIC: [u8; 8] = [0x69, 0x54, 0x49, 0x50, 0x69, 0x54, 0x49, 0x50]; +pub static PITIT_END_MARKER: u32 = 0x69_54_49_50; //PITi - end marker of PITIT + +#[derive(BinRead)] +pub struct PITITPITEntry { + pub nand_size: u32, + pub pit_offset: u32, + pub pit_size: u32, + _table_id: u32, +} + +#[derive(BinRead)] +pub struct PITITBITEntry { + pub bit_offset: u32, + pub bit_size: u32, + _private_data_1: u32, + _private_data_2: u32, +} + +#[derive(BinRead)] +pub struct PITHeader { + pub pit_magic: [u8; 8], + _version: u32, + pub first_entry_offset: u32, //"header len" + pub entry_size: u32, //"item lenght" + pub entry_count: u32, //"item num" +} + +pub static PIT_MAGIC: [u8; 8] = [0xDC, 0xEA, 0x30, 0x85, 0xDC, 0xEA, 0x30, 0x85]; + +#[derive(BinRead)] +pub struct PITEntry { + name_bytes: [u8; 16], + pub partition_id: u32, + _part_info: u32, + pub offset_on_nand: u32, + pub size_on_nand: u32, + _enc_size: u32, + _no_enc_size: u32, + _reserve: [u8; 24], +} +impl PITEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } +} + +#[derive(BinRead)] +pub struct BITEntry { + pub partition_id: u32, + pub offset: u32, + pub size: u32, + pub offset_in_target_part: u32, + _bin_info: u32, //"Bin info" +} + +pub static BIT_MAGIC: [u8; 20] = [0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85]; +pub static BIT_END_MARKER: u32 = 0x85_30_EF_EF; //EF EF 30 85 - end marker of BIT \ No newline at end of file diff --git a/src/formats/mtk_bdp.rs b/src/formats/mtk_bdp/mod.rs similarity index 75% rename from src/formats/mtk_bdp.rs rename to src/formats/mtk_bdp/mod.rs index ba01d1b..5431255 100644 --- a/src/formats/mtk_bdp.rs +++ b/src/formats/mtk_bdp/mod.rs @@ -1,82 +1,19 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "mtk_bdp", detector_func: is_mtk_bdp_file, extractor_func: extract_mtk_bdp } -} +use crate::AppContext; use std::path::{Path}; use std::fs::{self, OpenOptions}; use std::io::{Seek, SeekFrom, Read, Write}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; +use include::*; + pub struct MtkBdpContext { pitit_offset: u64, } -static PITIT_MAGIC: [u8; 8] = [0x69, 0x54, 0x49, 0x50, 0x69, 0x54, 0x49, 0x50]; -static PITIT_END_MARKER: u32 = 0x69_54_49_50; //PITi - end marker of PITIT - -#[derive(BinRead)] -struct PITITPITEntry { - nand_size: u32, - pit_offset: u32, - pit_size: u32, - _table_id: u32, -} - -#[derive(BinRead)] -struct PITITBITEntry { - bit_offset: u32, - bit_size: u32, - _private_data_1: u32, - _private_data_2: u32, -} - -#[derive(BinRead)] -struct PITHeader { - #[br(count = 8)] pit_magic: Vec, - _version: u32, - first_entry_offset: u32, //"header len" - entry_size: u32, //"item lenght" - entry_count: u32, //"item num" -} - -static PIT_MAGIC: [u8; 8] = [0xDC, 0xEA, 0x30, 0x85, 0xDC, 0xEA, 0x30, 0x85]; - -#[derive(BinRead)] -struct PITEntry { - #[br(count = 16)] name_bytes: Vec, - partition_id: u32, - _part_info: u32, - offset_on_nand: u32, - size_on_nand: u32, - _enc_size: u32, - _no_enc_size: u32, - #[br(count = 24)] _reserve: Vec, -} -impl PITEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } -} - -#[derive(BinRead)] -struct BITEntry { - partition_id: u32, - offset: u32, - size: u32, - offset_in_target_part: u32, - _bin_info: u32, //"Bin info" -} - -static BIT_MAGIC: [u8; 20] = [0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85, 0xCD, 0xAB, 0x30, 0x85]; -static BIT_END_MARKER: u32 = 0x85_30_EF_EF; //EF EF 30 85 - end marker of BIT - -fn find_bytes(data: &[u8], pattern: &[u8]) -> Option { - data.windows(pattern.len()).position(|window| window == pattern) -} - pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result>, Box> { let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file_size = file.metadata()?.len(); diff --git a/src/utils/huffman_tables.rs b/src/formats/mtk_pkg/huffman_tables.rs similarity index 100% rename from src/utils/huffman_tables.rs rename to src/formats/mtk_pkg/huffman_tables.rs diff --git a/src/formats/mtk_pkg/include.rs b/src/formats/mtk_pkg/include.rs new file mode 100644 index 0000000..7b01dfd --- /dev/null +++ b/src/formats/mtk_pkg/include.rs @@ -0,0 +1,63 @@ +use crate::utils::common; +use binrw::BinRead; + +pub static HEADER_KEY: [u8; 16] = [ + 0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94, + 0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94, +]; + +pub static HEADER_IV: [u8; 16] = [0x00; 16]; + +pub static MTK_HEADER_MAGIC: &[u8; 8] = b"#DH@FiRm"; +pub static MTK_RESERVED_MAGIC: &[u8; 16] = b"reserved mtk inc"; +pub static MTK_META_MAGIC: &[u8; 4] = b"iMtK"; +pub static MTK_META_PAD_MAGIC: &[u8; 4] = b"iPAd"; +pub static CRYPTED_HEADER_SIZE: usize = 0x30; + +pub static HEADER_SIZE: usize = 0x90; + +pub static PHILIPS_EXTRA_HEADER_SIZE: usize = 0x80; +pub static _PHILIPS_FOOTER_SIGNATURE_SIZE: usize = 0x100; + +#[derive(BinRead)] +pub struct Header { + pub vendor_magic_bytes: [u8; 4], + _mtk_magic: [u8; 8], //#DH@FiRm + version_bytes: [u8; 60], + pub file_size: u32, + _flags: u32, + product_name_bytes: [u8; 32], +} +impl Header { + pub fn vendor_magic(&self) -> String { + common::string_from_bytes(&self.vendor_magic_bytes) + } + pub fn version(&self) -> String { + common::string_from_bytes(&self.version_bytes) + } + pub fn product_name(&self) -> String { + common::string_from_bytes(&self.product_name_bytes) + } + +} + +#[derive(BinRead)] +pub struct PartEntry { + name_bytes: [u8; 4], + pub flags: u32, + pub size: u32, +} +impl PartEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } + pub fn is_valid(&self) -> bool { + self.name().is_ascii() + } + pub fn is_encrypted(&self) -> bool { + (self.flags & 1 << 0) != 0 + } + pub fn is_compressed(&self) -> bool { //lzhs fs + (self.flags & 1 << 8) != 0 + } +} \ No newline at end of file diff --git a/src/utils/lzhs.rs b/src/formats/mtk_pkg/lzhs.rs similarity index 98% rename from src/utils/lzhs.rs rename to src/formats/mtk_pkg/lzhs.rs index db82286..634218e 100644 --- a/src/utils/lzhs.rs +++ b/src/formats/mtk_pkg/lzhs.rs @@ -3,7 +3,7 @@ use std::io::{Write, Cursor, Seek, SeekFrom, Read}; use binrw::{BinRead, BinReaderExt}; use std::path::{PathBuf}; -use crate::utils::huffman_tables::{CHARLEN, POS}; +use super::huffman_tables::{CHARLEN, POS}; use crate::utils::compression::{decompress_lz4}; #[derive(BinRead)] @@ -11,7 +11,7 @@ struct LzhsHeader { uncompressed_size: u32, compressed_size: u32, checksum_or_seg_idx: u16, //as checksum in normal lzhs header, as index in lzhs_fs header - #[br(count = 6)] padding: Vec, + padding: [u8; 6], } #[derive(BinRead)] @@ -35,7 +35,7 @@ pub fn decompress_lzhs_fs_file2file(mut file: &File, output_file: PathBuf) -> Re //lz4 type uses a 4 byte checksum instead of 1(2) byte , so the padding is not 0 anymore //maybe this method will be changed - let is_lz4 = if lzhs_header.padding != b"\x00\x00\x00\x00\x00\x00" {true} else {false}; + let is_lz4 = if &lzhs_header.padding != b"\x00\x00\x00\x00\x00\x00" {true} else {false}; println!("[{}] Segment {} - Compressed size: {}, Decompressed size: {}, Expected Checksum: 0x{:02x?}", if is_lz4 {"LZ4"} else {"LZHS"}, segment_header.checksum_or_seg_idx, lzhs_header.compressed_size, lzhs_header.uncompressed_size, lzhs_header.checksum_or_seg_idx); diff --git a/src/formats/mtk_pkg.rs b/src/formats/mtk_pkg/mod.rs similarity index 78% rename from src/formats/mtk_pkg.rs rename to src/formats/mtk_pkg/mod.rs index 9b8bac5..aa1f523 100644 --- a/src/formats/mtk_pkg.rs +++ b/src/formats/mtk_pkg/mod.rs @@ -1,86 +1,25 @@ +pub mod include; +pub mod lzhs; +mod huffman_tables; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "mtk_pkg", detector_func: is_mtk_pkg_file, extractor_func: extract_mtk_pkg } -} +use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Cursor, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; -use crate::utils::lzhs::{decompress_lzhs_fs_file2file}; use crate::keys; +use lzhs::{decompress_lzhs_fs_file2file}; +use include::*; pub struct MtkPkgContext { is_philips_variant: bool, decrypted_header: Vec, } -#[derive(BinRead)] -struct Header { - #[br(count = 4)] vendor_magic_bytes: Vec, - #[br(count = 8)] _mtk_magic: Vec, //#DH@FiRm - #[br(count = 60)] version_bytes: Vec, - file_size: u32, - _flags: u32, - #[br(count = 32)] product_name_bytes: Vec, - #[br(count = 32)] _digest: Vec, -} -impl Header { - fn vendor_magic(&self) -> String { - common::string_from_bytes(&self.vendor_magic_bytes) - } - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn product_name(&self) -> String { - common::string_from_bytes(&self.product_name_bytes) - } - -} - -#[derive(BinRead)] -struct PartEntry { - #[br(count = 4)] name_bytes: Vec, - flags: u32, - size: u32, -} -impl PartEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } - fn is_valid(&self) -> bool { - self.name().is_ascii() - } - fn is_encrypted(&self) -> bool { - (self.flags & 1 << 0) != 0 - } - fn is_compressed(&self) -> bool { //lzhs fs - (self.flags & 1 << 8) != 0 - } -} - -pub static MTK_HEADER_MAGIC: &[u8; 8] = b"#DH@FiRm"; -pub static MTK_RESERVED_MAGIC: &[u8; 16] = b"reserved mtk inc"; -pub static MTK_META_MAGIC: &[u8; 4] = b"iMtK"; -pub static MTK_META_PAD_MAGIC: &[u8; 4] = b"iPAd"; -pub static CRYPTED_HEADER_SIZE: usize = 0x30; - -static HEADER_SIZE: usize = 0x90; - -static PHILIPS_EXTRA_HEADER_SIZE: usize = 0x80; -static _PHILIPS_FOOTER_SIGNATURE_SIZE: usize = 0x100; - -static HEADER_KEY: [u8; 16] = [ - 0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94, - 0x09, 0x29, 0x10, 0x94, 0x09, 0x29, 0x10, 0x94, -]; - -static HEADER_IV: [u8; 16] = [0x00; 16]; - pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/mtk_pkg_new/include.rs b/src/formats/mtk_pkg_new/include.rs new file mode 100644 index 0000000..7a53f4f --- /dev/null +++ b/src/formats/mtk_pkg_new/include.rs @@ -0,0 +1 @@ +pub static HEADER_SIZE: usize = 0x170; \ No newline at end of file diff --git a/src/formats/mtk_pkg_new.rs b/src/formats/mtk_pkg_new/mod.rs similarity index 77% rename from src/formats/mtk_pkg_new.rs rename to src/formats/mtk_pkg_new/mod.rs index fd0e664..dd0bfc1 100644 --- a/src/formats/mtk_pkg_new.rs +++ b/src/formats/mtk_pkg_new/mod.rs @@ -1,20 +1,18 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "mtk_pkg_new", detector_func: is_mtk_pkg_new_file, extractor_func: extract_mtk_pkg_new } -} +use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Cursor, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; -use crate::utils::lzhs::{decompress_lzhs_fs_file2file}; use crate::keys; - -use crate::formats::mtk_pkg::{MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC}; +use crate::formats::mtk_pkg::lzhs::{decompress_lzhs_fs_file2file}; +use crate::formats::mtk_pkg::include::{Header, PartEntry, MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC}; +use include::*; pub struct MtkPkgNewContext { matching_key_name: String, @@ -23,53 +21,6 @@ pub struct MtkPkgNewContext { decrypted_header: Vec, } -#[derive(BinRead)] -struct Header { - #[br(count = 4)] vendor_magic_bytes: Vec, - #[br(count = 8)] _mtk_magic: Vec, //#DH@FiRm - #[br(count = 56)] version_bytes: Vec, - _unk: u32, - file_size: u32, - _flags: u32, - #[br(count = 32)] product_name_bytes: Vec, - #[br(count = 256)] _digest: Vec, -} -impl Header { - fn vendor_magic(&self) -> String { - common::string_from_bytes(&self.vendor_magic_bytes) - } - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn product_name(&self) -> String { - common::string_from_bytes(&self.product_name_bytes) - } - -} - -#[derive(BinRead)] -struct PartEntry { - #[br(count = 4)] name_bytes: Vec, - flags: u32, - size: u32, -} -impl PartEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } - fn is_valid(&self) -> bool { - self.name().is_ascii() - } - fn is_encrypted(&self) -> bool { - (self.flags & 1 << 0) != 0 - } - fn is_compressed(&self) -> bool { //lzhs fs - (self.flags & 1 << 8) != 0 - } -} - -static HEADER_SIZE: usize = 0x170; - pub fn is_mtk_pkg_new_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/mtk_pkg_old/include.rs b/src/formats/mtk_pkg_old/include.rs new file mode 100644 index 0000000..11e36e8 --- /dev/null +++ b/src/formats/mtk_pkg_old/include.rs @@ -0,0 +1,33 @@ +use crate::utils::common; +use binrw::BinRead; + +pub static KEY: u32 = 0x94102909; //09 29 10 94 +// first 4 bytes of header and content are additionally XORed, they have different masks although only differ by half a byte +pub static HEADER_XOR_MASK: u32 = 0x04BE7C75; //75 7C BE 04 +pub static CONTENT_XOR_MASK: u32 = 0x04BE7C72; //72 7C BE 04 + +pub static HEADER_SIZE: usize = 0x98; + +#[derive(BinRead)] +pub struct Header { + pub vendor_magic_bytes: [u8; 4], + _mtk_magic: [u8; 8], + version_bytes: [u8; 68], + pub file_size: u32, + _flags: u32, + product_name_bytes: [u8; 32], + _digest: [u8; 32], +} +impl Header { + pub fn vendor_magic(&self) -> String { + common::string_from_bytes(&self.vendor_magic_bytes) + } + pub fn version(&self) -> String { + common::string_from_bytes(&self.version_bytes) + } + pub fn product_name(&self) -> String { + common::string_from_bytes(&self.product_name_bytes) + } + +} + diff --git a/src/formats/mtk_pkg_old.rs b/src/formats/mtk_pkg_old/mod.rs similarity index 71% rename from src/formats/mtk_pkg_old.rs rename to src/formats/mtk_pkg_old/mod.rs index 3ffe9e1..6719736 100644 --- a/src/formats/mtk_pkg_old.rs +++ b/src/formats/mtk_pkg_old/mod.rs @@ -1,67 +1,18 @@ +mod include; +mod mtk_crypto; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "mtk_pkg_old", detector_func: is_mtk_pkg_old_file, extractor_func: extract_mtk_pkg_old } -} +use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Cursor, Seek}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; -use crate::utils::mtk_crypto::{decrypt}; -use crate::utils::lzhs::{decompress_lzhs_fs_file2file_old}; - -static KEY: u32 = 0x94102909; //09 29 10 94 -// first 4 bytes of header and content are additionally XORed, they have different masks although only differ by half a byte -static HEADER_XOR_MASK: u32 = 0x04BE7C75; //75 7C BE 04 -static CONTENT_XOR_MASK: u32 = 0x04BE7C72; //72 7C BE 04 - -use crate::formats::mtk_pkg::{MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC}; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] vendor_magic_bytes: Vec, - #[br(count = 8)] _mtk_magic: Vec, //#DH@FiRm - #[br(count = 68)] version_bytes: Vec, - file_size: u32, - _flags: u32, - #[br(count = 32)] product_name_bytes: Vec, - #[br(count = 32)] _digest: Vec, -} -impl Header { - fn vendor_magic(&self) -> String { - common::string_from_bytes(&self.vendor_magic_bytes) - } - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn product_name(&self) -> String { - common::string_from_bytes(&self.product_name_bytes) - } - -} - -#[derive(BinRead)] -struct PartEntry { - #[br(count = 4)] name_bytes: Vec, - flags: u32, - size: u32, -} -impl PartEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } - fn is_encrypted(&self) -> bool { - (self.flags & 1 << 0) == 1 << 0 - } - fn is_compressed(&self) -> bool { //lzhs fs - (self.flags & 1 << 8) != 0 - } -} - -static HEADER_SIZE: usize = 0x98; +use crate::formats::mtk_pkg::lzhs::{decompress_lzhs_fs_file2file_old}; +use crate::formats::mtk_pkg::include::{PartEntry, MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC}; +use mtk_crypto::{decrypt}; +use include::*; pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result>, Box> { let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/utils/mtk_crypto.rs b/src/formats/mtk_pkg_old/mtk_crypto.rs similarity index 100% rename from src/utils/mtk_crypto.rs rename to src/formats/mtk_pkg_old/mtk_crypto.rs diff --git a/src/formats/novatek/include.rs b/src/formats/novatek/include.rs new file mode 100644 index 0000000..f4eda9a --- /dev/null +++ b/src/formats/novatek/include.rs @@ -0,0 +1,30 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 4], + pub version_major: u32, + pub version_minor: u32, + _unused: u32, + firmware_name_bytes: [u8; 16], + pub data_size: u32, + _md5_checksum: [u8; 16], //data checksum + pub part_count: u32, + _data_start_offset: u32, + _signature: [u8; 128], + _header_checksum: u32, //CRC32, calculated with the field set to 0 +} +impl Header { + pub fn firmware_name(&self) -> String { + common::string_from_bytes(&self.firmware_name_bytes) + } +} + +#[derive(BinRead)] +pub struct PartEntry { + pub id: u32, + pub size: u32, + pub offset: u32, + _md5_checksum: [u8; 16], +} \ No newline at end of file diff --git a/src/formats/novatek.rs b/src/formats/novatek/mod.rs similarity index 64% rename from src/formats/novatek.rs rename to src/formats/novatek/mod.rs index 45ead49..3ef7c6f 100644 --- a/src/formats/novatek.rs +++ b/src/formats/novatek/mod.rs @@ -1,44 +1,14 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "novatek", detector_func: is_novatek_file, extractor_func: extract_novatek } -} +use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; -use std::io::{Write}; - -use binrw::{BinRead, BinReaderExt}; +use std::io::Write; +use binrw::BinReaderExt; use crate::utils::common; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] _magic_bytes: Vec, - version_major: u32, - version_minor: u32, - _unused: u32, - #[br(count = 16)] firmware_name_bytes: Vec, - data_size: u32, - #[br(count = 16)] _md5_checksum: Vec, //data checksum - part_count: u32, - _data_start_offset: u32, - #[br(count = 128)] _signature: Vec, - _header_checksum: u32, //CRC32, calculated with the field set to 0 -} -impl Header { - fn firmware_name(&self) -> String { - common::string_from_bytes(&self.firmware_name_bytes) - } -} - -#[derive(BinRead)] -struct PartEntry { - id: u32, - size: u32, - offset: u32, - #[br(count = 16)] _md5_checksum: Vec, -} +use include::*; pub fn is_novatek_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/nvt_timg/include.rs b/src/formats/nvt_timg/include.rs new file mode 100644 index 0000000..1b91aa1 --- /dev/null +++ b/src/formats/nvt_timg/include.rs @@ -0,0 +1,38 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(Debug, BinRead)] +pub struct TIMG { + _magic_bytes: [u8; 4], //TIMG + _unused1: u32, + pub data_size: u32, + _unused2: u32, + _md5_checksum: [u8; 16], + _signature: [u8; 256], +} + +#[derive(Debug, BinRead)] +pub struct PIMG { + pub magic_bytes: [u8; 4], //PIMG + _unused1: u32, + pub size: u32, + _unused2: u32, + _md5_checksum: [u8; 16], + name_bytes: [u8; 16], + dest_dev_bytes: [u8; 64], + comp_type_bytes: [u8; 16], + _unknown1: u32, + _comment: [u8; 1024], + _unknown2: u32, +} +impl PIMG { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } + pub fn dest_dev(&self) -> String { + common::string_from_bytes(&self.dest_dev_bytes) + } + pub fn comp_type(&self) -> String { + common::string_from_bytes(&self.comp_type_bytes) + } +} \ No newline at end of file diff --git a/src/formats/nvt_timg.rs b/src/formats/nvt_timg/mod.rs similarity index 65% rename from src/formats/nvt_timg.rs rename to src/formats/nvt_timg/mod.rs index 040a89e..774486c 100644 --- a/src/formats/nvt_timg.rs +++ b/src/formats/nvt_timg/mod.rs @@ -1,53 +1,16 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "nvt_timg", detector_func: is_nvt_timg_file, extractor_func: extract_nvt_timg } -} +use crate::AppContext; -use std::path::{Path}; +use std::path::Path; use std::io::{Seek, Write}; use std::fs::{self, OpenOptions}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::utils::compression::{decompress_gzip}; use crate::utils::sparse::{unsparse_to_file}; - -#[derive(Debug, BinRead)] -struct TIMG { - #[br(count = 4)] _magic_bytes: Vec, //TIMG - _unused1: u32, - data_size: u32, - _unused2: u32, - #[br(count = 16)] _md5_checksum: Vec, - #[br(count = 256)] _signature: Vec, -} - -#[derive(Debug, BinRead)] -struct PIMG { - #[br(count = 4)] magic_bytes: Vec, //PIMG - _unused1: u32, - size: u32, - _unused2: u32, - #[br(count = 16)] _md5_checksum: Vec, - #[br(count = 16)] name_bytes: Vec, - #[br(count = 64)] dest_dev_bytes: Vec, - #[br(count = 16)] comp_type_bytes: Vec, - _unknown1: u32, - #[br(count = 1024)] _comment: Vec, - _unknown2: u32, -} -impl PIMG { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } - fn dest_dev(&self) -> String { - common::string_from_bytes(&self.dest_dev_bytes) - } - fn comp_type(&self) -> String { - common::string_from_bytes(&self.comp_type_bytes) - } -} +use include::*; pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -71,7 +34,7 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Box) -> Result<(), while file.stream_position()? < file_size as u64 { pimg_i += 1; let pimg: PIMG = file.read_le()?; - if pimg.magic_bytes != b"PIMG" { + if &pimg.magic_bytes != b"PIMG" { println!("Invalid PIMG magic!"); return Ok(()); } diff --git a/src/formats/pana_dvd/include.rs b/src/formats/pana_dvd/include.rs new file mode 100644 index 0000000..080ccb0 --- /dev/null +++ b/src/formats/pana_dvd/include.rs @@ -0,0 +1,132 @@ +use crate::utils::common; +use binrw::BinRead; +use super::pana_dvd_crypto::{decrypt_data}; +use crate::utils::aes::{decrypt_aes128_cbc_nopad}; + +pub fn find_key<'a>(key_array: &'a [&'a str], data: &[u8], expected_magic: &[u8], magic_offset: usize) -> Result, Box> { + for key_hex in key_array { + let key_bytes = hex::decode(key_hex)?; + let key_array: [u8; 8] = key_bytes.as_slice().try_into()?; + let decrypted = decrypt_data(data, &key_array); + + if decrypted[magic_offset..].starts_with(expected_magic) { + return Ok(Some(key_array)); + } + } + Ok(None) +} + +pub fn find_aes_key_pair<'a>(key_array: &'a [(&'a str, &'a str, &'a str)], data: &[u8], expected_magic: &[u8], magic_offset: usize) -> Result, Box> { + for (aes_key_hex, aes_iv_hex, cust_key_hex) in key_array { + let aes_key: [u8; 16] = hex::decode(aes_key_hex)?.as_slice().try_into()?; + let aes_iv: [u8; 16] = hex::decode(aes_iv_hex)?.as_slice().try_into()?; + let aes_decrypted = decrypt_aes128_cbc_nopad(data, &aes_key, &aes_iv)?; + + let key_bytes = hex::decode(cust_key_hex)?; + let key_array: [u8; 8] = key_bytes.as_slice().try_into()?; + let decrypted = decrypt_data(&aes_decrypted, &key_array); + + if decrypted[magic_offset..].starts_with(expected_magic) { + return Ok(Some((aes_key, aes_iv, key_array))); + } + } + Ok(None) +} + +pub static MAX_HEADER_SIZE: usize = 0x2000; + +#[derive(BinRead)] +pub struct AesHeaderFileEntry { + pub offset: u32, + pub size: u32, +} + +pub struct FileEntry { + pub offset: u32, + pub base_offset: u32, +} + +//checksums are mostly Adler32, but some very old files use Checksum32 instead. + +#[derive(BinRead)] +pub struct ModuleEntry { + #[br(count = 4)] pub name_bytes: Vec, + version_bytes: [u8; 4], + _unk: u32, + pub offset: u32, + platform_bytes: [u8; 8], + _unk1: u16, + id_bytes: [u8; 6], + pub size: u32, + pub data_checksum: u32, //checksum of the entrys' DATA + _unk2: u32, + _entry_checksum: u32, //checksum of THIS header entry (all previous 44 bytes) +} +impl ModuleEntry { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_bytes) + } + pub fn version(&self) -> String { + common::string_from_bytes(&self.version_bytes) + } + pub fn platform(&self) -> String { + common::string_from_bytes(&self.platform_bytes) + } + pub fn id(&self) -> String { + common::string_from_bytes(&self.id_bytes) + } + pub fn is_valid(&self) -> bool { + self.name().is_ascii() && self.platform().is_ascii() + } +} + +#[derive(BinRead)] +pub struct MainListHeader { + _checksum: u32, //checksum of the MAIN LIST + _unk: u32, //seems to be always 1? + pub list_size: u32, + pub decompressed_part_size: u32, + _unk2: u32, +} +impl MainListHeader { + pub fn entry_count(&self) -> u32 { + (&self.list_size - 20) / 8 + } +} + +#[derive(BinRead)] +pub struct MainListEntry { + pub size: u32, + pub checksum: u32, //checksum of this MAIN entrys' data +} + +pub const COMPRESSED_FILE_MAGIC: &[u8; 8] = b"EXTRHEAD"; + +#[derive(BinRead)] +pub struct CompressedFileHeader { + _header_string: [u8; 14], //EXTRHEADDRV \x01\x00 + pub compression_type_byte: u16, + pub decompressed_size: u32, + _destination_address: u32, + pub compressed_size: u32, + _unk: u32, + _footer_offset: u32, + _base_address: u32, + _checksum: u32, //unknown type of checksum + _checksum_flag: u8, + _unused: [u8; 19], +} +impl CompressedFileHeader { + pub fn compression_type(&self) -> &str { + if self.compression_type_byte == 0 { + return "Uncompressed" + } else if self.compression_type_byte == 1 { + return "GZIP" + } else if self.compression_type_byte == 2 { + return "LZSS" + } else { + return "Unknown" + } + } +} + diff --git a/src/utils/lzss.rs b/src/formats/pana_dvd/lzss.rs similarity index 100% rename from src/utils/lzss.rs rename to src/formats/pana_dvd/lzss.rs diff --git a/src/formats/pana_dvd.rs b/src/formats/pana_dvd/mod.rs similarity index 73% rename from src/formats/pana_dvd.rs rename to src/formats/pana_dvd/mod.rs index 41919f6..ae907f6 100644 --- a/src/formats/pana_dvd.rs +++ b/src/formats/pana_dvd/mod.rs @@ -1,20 +1,21 @@ +mod include; +mod pana_dvd_crypto; +mod lzss; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "pana_dvd", detector_func: is_pana_dvd_file, extractor_func: extract_pana_dvd } -} +use crate::AppContext; use std::path::{Path, PathBuf}; use std::fs::{self, OpenOptions}; use std::io::{Write, Read, Cursor, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::keys; use crate::utils::common; -use crate::utils::pana_dvd_crypto::{decrypt_data}; use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::utils::compression::{decompress_gzip}; -use crate::utils::lzss::{decompress_lzss}; +use pana_dvd_crypto::{decrypt_data}; +use lzss::{decompress_lzss}; +use include::*; pub struct PanaDvdContext { matching_key: [u8; 8], @@ -24,133 +25,6 @@ pub struct PanaDvdContext { aes_iv: Option<[u8; 16]> } -#[derive(BinRead)] -struct AesHeaderFileEntry { - offset: u32, - size: u32, -} - -struct FileEntry { - offset: u32, - base_offset: u32, -} - -//checksums are mostly Adler32, but some very old files use Checksum32 instead. - -#[derive(BinRead)] -struct ModuleEntry { - #[br(count = 4)] name_bytes: Vec, - #[br(count = 4)] version_bytes: Vec, - _unk: u32, - offset: u32, - #[br(count = 8)] platform_bytes: Vec, - _unk1: u16, - #[br(count = 6)] id_bytes: Vec, - size: u32, - data_checksum: u32, //checksum of the entrys' DATA - _unk2: u32, - _entry_checksum: u32, //checksum of THIS header entry (all previous 44 bytes) -} -impl ModuleEntry { - fn name(&self) -> String { - common::string_from_bytes(&self.name_bytes) - } - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn platform(&self) -> String { - common::string_from_bytes(&self.platform_bytes) - } - fn id(&self) -> String { - common::string_from_bytes(&self.id_bytes) - } - fn is_valid(&self) -> bool { - self.name().is_ascii() && self.platform().is_ascii() - } -} - -#[derive(BinRead)] -struct MainListHeader { - _checksum: u32, //checksum of the MAIN LIST - _unk: u32, //seems to be always 1? - list_size: u32, - decompressed_part_size: u32, - _unk2: u32, -} -impl MainListHeader { - fn entry_count(&self) -> u32 { - (&self.list_size - 20) / 8 - } -} - -#[derive(BinRead)] -struct MainListEntry { - size: u32, - checksum: u32, //checksum of this MAIN entrys' data -} - -const COMPRESSED_FILE_MAGIC: &[u8; 8] = b"EXTRHEAD"; - -#[derive(BinRead)] -struct CompressedFileHeader { - #[br(count = 14)] _header_string: Vec, //EXTRHEADDRV \x01\x00 - compression_type_byte: u16, - decompressed_size: u32, - _destination_address: u32, - compressed_size: u32, - _unk: u32, - _footer_offset: u32, - _base_address: u32, - _checksum: u32, //unknown type of checksum - _checksum_flag: u8, - #[br(count = 19)] _unused: Vec, -} -impl CompressedFileHeader { - fn compression_type(&self) -> &str { - if self.compression_type_byte == 0 { - return "Uncompressed" - } else if self.compression_type_byte == 1 { - return "GZIP" - } else if self.compression_type_byte == 2 { - return "LZSS" - } else { - return "Unknown" - } - } -} - -static MAX_HEADER_SIZE: usize = 0x2000; - -pub fn find_key<'a>(key_array: &'a [&'a str], data: &[u8], expected_magic: &[u8], magic_offset: usize) -> Result, Box> { - for key_hex in key_array { - let key_bytes = hex::decode(key_hex)?; - let key_array: [u8; 8] = key_bytes.as_slice().try_into()?; - let decrypted = decrypt_data(data, &key_array); - - if decrypted[magic_offset..].starts_with(expected_magic) { - return Ok(Some(key_array)); - } - } - Ok(None) -} - -pub fn find_aes_key_pair<'a>(key_array: &'a [(&'a str, &'a str, &'a str)], data: &[u8], expected_magic: &[u8], magic_offset: usize) -> Result, Box> { - for (aes_key_hex, aes_iv_hex, cust_key_hex) in key_array { - let aes_key: [u8; 16] = hex::decode(aes_key_hex)?.as_slice().try_into()?; - let aes_iv: [u8; 16] = hex::decode(aes_iv_hex)?.as_slice().try_into()?; - let aes_decrypted = decrypt_aes128_cbc_nopad(data, &aes_key, &aes_iv)?; - - let key_bytes = hex::decode(cust_key_hex)?; - let key_array: [u8; 8] = key_bytes.as_slice().try_into()?; - let decrypted = decrypt_data(&aes_decrypted, &key_array); - - if decrypted[magic_offset..].starts_with(expected_magic) { - return Ok(Some((aes_key, aes_iv, key_array))); - } - } - Ok(None) -} - pub fn is_pana_dvd_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let header = common::read_file(&file, 0, 64)?; diff --git a/src/utils/pana_dvd_crypto.rs b/src/formats/pana_dvd/pana_dvd_crypto.rs similarity index 100% rename from src/utils/pana_dvd_crypto.rs rename to src/formats/pana_dvd/pana_dvd_crypto.rs diff --git a/src/formats/pfl_upg/include.rs b/src/formats/pfl_upg/include.rs new file mode 100644 index 0000000..233e540 --- /dev/null +++ b/src/formats/pfl_upg/include.rs @@ -0,0 +1,67 @@ +use crate::utils::common; +use binrw::BinRead; +use aes::Aes256; +use ecb::{Decryptor, cipher::{BlockDecryptMut, KeyInit, generic_array::GenericArray}}; + +pub static AUTO_FWS: &[(&str, &str)] = &[ + ("Q5551", "q5551"), + ("Q5553", "q5551"), + ("Q554E", "q5551"), + ("Q554M", "q5551"), + ("QF1EU", "qf1eu"), + ("QF2EU", "qf1eu"), + ("Q591E", "q591e"), + ("Q522E", "q522e"), + ("Q582E", "q522e"), + ("Q5481", "q5481"), + ("Q5431", "q5431"), + ("Q5492", "q5492"), + ("S5551", "q5551"), //Sharp +]; + +type Aes256EcbDec = Decryptor; + +pub fn decrypt_aes256_ecb(key: &[u8], ciphertext: &[u8]) -> Result, Box> { + let key_array: [u8; 32] = key.try_into()?; + + let mut decryptor = Aes256EcbDec::new(&key_array.into()); + let mut buffer = ciphertext.to_vec(); + + for chunk in buffer.chunks_exact_mut(16) { + let block: &mut [u8; 16] = chunk.try_into()?; + decryptor.decrypt_block_mut(GenericArray::from_mut_slice(block)); + } + + Ok(buffer) +} + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 8], + pub header_size: u32, + pub data_size: u32, + _crc32: u32, + pub mask: u32, + _data_size_decompressed: u32, + _padding2: u32, + description_bytes: [u8; 512], +} +impl Header { + pub fn description(&self) -> String { + common::string_from_bytes(&self.description_bytes) + } +} + +#[derive(BinRead)] +pub struct FileHeader { + file_name_bytes: [u8; 60], + pub real_size: u32, + pub stored_size: u32, + pub header_size: u32, + pub attributes: [u8; 4], +} +impl FileHeader { + pub fn file_name(&self) -> String { + common::string_from_bytes(&self.file_name_bytes) + } +} \ No newline at end of file diff --git a/src/formats/pfl_upg.rs b/src/formats/pfl_upg/mod.rs similarity index 73% rename from src/formats/pfl_upg.rs rename to src/formats/pfl_upg/mod.rs index 886566f..2e1b118 100644 --- a/src/formats/pfl_upg.rs +++ b/src/formats/pfl_upg/mod.rs @@ -1,52 +1,17 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "pfl_upg", detector_func: is_pfl_upg_file, extractor_func: extract_pfl_upg } -} +use crate::AppContext; use rsa::{RsaPublicKey, BigUint}; use hex::decode; use std::path::Path; use std::io::{Read, Cursor, Write}; use std::fs::{self, OpenOptions}; - -use aes::Aes256; -use ecb::{Decryptor, cipher::{BlockDecryptMut, KeyInit, generic_array::GenericArray}}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::keys; - -#[derive(BinRead)] -struct Header { - #[br(count = 8)] _magic_bytes: Vec, - header_size: u32, - data_size: u32, - #[br(count = 4)] _crc32: Vec, - mask: u32, - _data_size_decompressed: u32, - _padding2: u32, - #[br(count = 512)] description_bytes: Vec, -} -impl Header { - fn description(&self) -> String { - common::string_from_bytes(&self.description_bytes) - } -} - -#[derive(BinRead)] -struct FileHeader { - #[br(count = 60)] file_name_bytes: Vec, - real_size: u32, - stored_size: u32, - header_size: u32, - #[br(count = 4)] attributes: Vec, -} -impl FileHeader { - fn file_name(&self) -> String { - common::string_from_bytes(&self.file_name_bytes) - } -} +use include::*; pub fn is_pfl_upg_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -59,38 +24,6 @@ pub fn is_pfl_upg_file(app_ctx: &AppContext) -> Result>, Box } } -static AUTO_FWS: &[(&str, &str)] = &[ - ("Q5551", "q5551"), - ("Q5553", "q5551"), - ("Q554E", "q5551"), - ("Q554M", "q5551"), - ("QF1EU", "qf1eu"), - ("QF2EU", "qf1eu"), - ("Q591E", "q591e"), - ("Q522E", "q522e"), - ("Q582E", "q522e"), - ("Q5481", "q5481"), - ("Q5431", "q5431"), - ("Q5492", "q5492"), - ("S5551", "q5551"), //Sharp -]; - -type Aes256EcbDec = Decryptor; - -fn decrypt_aes256_ecb(key: &[u8], ciphertext: &[u8]) -> Result, Box> { - let key_array: [u8; 32] = key.try_into()?; - - let mut decryptor = Aes256EcbDec::new(&key_array.into()); - let mut buffer = ciphertext.to_vec(); - - for chunk in buffer.chunks_exact_mut(16) { - let block: &mut [u8; 16] = chunk.try_into()?; - decryptor.decrypt_block_mut(GenericArray::from_mut_slice(block)); - } - - Ok(buffer) -} - pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; diff --git a/src/formats/pup/include.rs b/src/formats/pup/include.rs new file mode 100644 index 0000000..b479d1c --- /dev/null +++ b/src/formats/pup/include.rs @@ -0,0 +1,45 @@ +use binrw::BinRead; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 4], + _unk1: u32, + _unk2: u16, + _flags: u8, + _unk3: u8, + _header_size: u16, + _hash_size: u16, + pub file_size: u64, + pub entry_count: u16, + _hash_count: u16, + _unk4: u32, +} + +#[derive(BinRead, Clone)] +pub struct Entry { + pub flags: u32, + _unk1: u32, + pub offset: u64, + pub compressed_size: u64, + pub uncompressed_size: u64, +} +impl Entry { + pub fn id(&self) -> u32 { + self.flags >> 20 + } + pub fn is_compressed(&self) -> bool { + (self.flags & 8) != 0 + } + pub fn is_blocked(&self) -> bool { + (self.flags & 0x800) != 0 + } + pub fn is_block_table(&self) -> bool { + (self.flags & 1) != 0 + } +} + +#[derive(BinRead, Clone)] +pub struct BlockEntry { + pub offset: u32, + pub size: u32, +} \ No newline at end of file diff --git a/src/formats/pup.rs b/src/formats/pup/mod.rs similarity index 84% rename from src/formats/pup.rs rename to src/formats/pup/mod.rs index 2ad20a4..f8eb905 100644 --- a/src/formats/pup.rs +++ b/src/formats/pup/mod.rs @@ -1,60 +1,15 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "pup", detector_func: is_pup_file, extractor_func: extract_pup } -} +use crate::AppContext; use std::path::{Path}; use std::fs::{self, OpenOptions}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use std::io::{Write, Seek, SeekFrom}; use crate::utils::common; use crate::utils::compression::{decompress_zlib}; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] _magic_bytes: Vec, - _unk1: u32, - _unk2: u16, - _flags: u8, - _unk3: u8, - _header_size: u16, - _hash_size: u16, - file_size: u64, - entry_count: u16, - _hash_count: u16, - _unk4: u32, -} - -#[derive(BinRead, Clone)] -struct Entry { - flags: u32, - _unk1: u32, - offset: u64, - compressed_size: u64, - uncompressed_size: u64, -} -impl Entry { - fn id(&self) -> u32 { - self.flags >> 20 - } - fn is_compressed(&self) -> bool { - (self.flags & 8) != 0 - } - fn is_blocked(&self) -> bool { - (self.flags & 0x800) != 0 - } - fn is_block_table(&self) -> bool { - (self.flags & 1) != 0 - } -} - -#[derive(BinRead, Clone)] -struct BlockEntry { - offset: u32, - size: u32, -} +use include::*; pub fn is_pup_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/roku/include.rs b/src/formats/roku/include.rs new file mode 100644 index 0000000..b2d0bc2 --- /dev/null +++ b/src/formats/roku/include.rs @@ -0,0 +1,49 @@ +use binrw::BinRead; + +pub static FILE_KEY: [u8; 16] = [ + 0x2A, 0x54, 0xA5, 0x30, 0xE0, 0x09, 0xA3, 0xDC, + 0x03, 0xFB, 0xC3, 0x5E, 0x23, 0xA2, 0xC1, 0x0D, +]; + +pub static FILE_IV: [u8; 16] = [0x00; 16]; + +#[derive(BinRead)] +pub struct ImageHeader { + _empty: [u8; 8], + _magic_bytes: [u8; 8], //imgARMcC + _target_bytes: [u8; 4], + _platform_id: [u8; 4], + pub image_type: u32, + pub size1: u32, + _size2: u32, + pub data_start_offset: u32, + _data_link_address: u32, + _data_entry_point_offset: u32, + pub flags: u32, + _timestamp: u32, + _build_host: [u8; 4], + _unk: [u8; 4], + _rest_of_header: [u8; 192], +} +impl ImageHeader { + pub fn is_encrypted(&self) -> bool { + self.flags == 0x80 + } + pub fn type_string(&self) -> &str { + if self.image_type == 0xa { + return "initfs" + } else if self.image_type == 0x18 { + return "uImage" + } else if self.image_type == 0x3 { + return "loader" + } else if self.image_type == 0xd { + return "app_cramfs" + } else if self.image_type == 0x6 { + return "Customization Package" + } else if self.image_type == 0xe { + return "firmware_blob" + } else { + return "unknown" + } + } +} \ No newline at end of file diff --git a/src/formats/roku.rs b/src/formats/roku/mod.rs similarity index 72% rename from src/formats/roku.rs rename to src/formats/roku/mod.rs index 6d0b78f..1b11099 100644 --- a/src/formats/roku.rs +++ b/src/formats/roku/mod.rs @@ -1,65 +1,16 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "roku", detector_func: is_roku_file, extractor_func: extract_roku } -} +use crate::AppContext; use std::fs::{self, OpenOptions}; use std::path::Path; use std::io::{Write, Seek, Read, Cursor}; use tar::Archive; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::utils::aes::{decrypt_aes128_cbc_nopad, decrypt_aes128_cbc_pcks7}; - -static FILE_KEY: [u8; 16] = [ - 0x2A, 0x54, 0xA5, 0x30, 0xE0, 0x09, 0xA3, 0xDC, - 0x03, 0xFB, 0xC3, 0x5E, 0x23, 0xA2, 0xC1, 0x0D, -]; - -static FILE_IV: [u8; 16] = [0x00; 16]; - -#[derive(BinRead)] -struct ImageHeader { - #[br(count = 8)] _empty: Vec, - #[br(count = 8)] _magic_bytes: Vec, //imgARMcC - #[br(count = 4)] _target_bytes: Vec, - #[br(count = 4)] _platform_id: Vec, - image_type: u32, - size1: u32, - _size2: u32, - data_start_offset: u32, - _data_link_address: u32, - _data_entry_point_offset: u32, - flags: u32, - _timestamp: u32, - #[br(count = 4)] _build_host: Vec, - #[br(count = 4)] _unk: Vec, - #[br(count = 192)] _rest_of_header: Vec, -} -impl ImageHeader { - fn is_encrypted(&self) -> bool { - self.flags == 0x80 - } - fn type_string(&self) -> &str { - if self.image_type == 0xa { - return "initfs" - } else if self.image_type == 0x18 { - return "uImage" - } else if self.image_type == 0x3 { - return "loader" - } else if self.image_type == 0xd { - return "app_cramfs" - } else if self.image_type == 0x6 { - return "Customization Package" - } else if self.image_type == 0xe { - return "firmware_blob" - } else { - return "unknown" - } - } -} +use include::*; pub fn is_roku_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/ruf/include.rs b/src/formats/ruf/include.rs new file mode 100644 index 0000000..5b755c7 --- /dev/null +++ b/src/formats/ruf/include.rs @@ -0,0 +1,64 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct RufHeader { + _magic_bytes: [u8; 6], + _upgrade_type_bytes: [u8; 2], + _unk1: u32, + date_time_bytes: [u8; 32], + buyer_bytes: [u8; 8], + model_bytes: [u8; 32], + region_info_bytes: [u8; 32], + pub version_bytes: [u8; 4], + pub data_size: u32, + _unk2: [u8; 20], + pub dual_ruf_flag: u32, + _unk3: [u8; 44], + pub payload_count: u16, + _payload_entry_size: u16, + pub payloads_start_offset: u32, +} +impl RufHeader { + pub fn date_time(&self) -> String { + common::string_from_bytes(&self.date_time_bytes) + } + pub fn buyer(&self) -> String { + common::string_from_bytes(&self.buyer_bytes) + } + pub fn model(&self) -> String { + common::string_from_bytes(&self.model_bytes) + } + pub fn region_info(&self) -> String { + common::string_from_bytes(&self.region_info_bytes) + } + pub fn is_dual_ruf(&self) -> bool { + if self.dual_ruf_flag == 0x44 {true} else {false} + } +} + +#[derive(BinRead)] +pub struct RufEntry { + _metadata: [u8; 32], + pub payload_type_bytes: u32, + pub size: u32, + _unk1: u32, + _unk2: [u8; 20], +} +impl RufEntry { + pub fn payload_type(&self) -> &str { + if self.payload_type_bytes == 1 { + return "squashfs" + } else if self.payload_type_bytes == 2 { + return "cfe" + } else if self.payload_type_bytes == 3 { + return "vmlinuz" + } else if self.payload_type_bytes == 4 { + return "loader" + } else if self.payload_type_bytes == 5 { + return "splash" + } else { + return "unknown" + } + } +} \ No newline at end of file diff --git a/src/formats/ruf.rs b/src/formats/ruf/mod.rs similarity index 66% rename from src/formats/ruf.rs rename to src/formats/ruf/mod.rs index ea3ab7a..a8b39f8 100644 --- a/src/formats/ruf.rs +++ b/src/formats/ruf/mod.rs @@ -1,79 +1,16 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "ruf", detector_func: is_ruf_file, extractor_func: extract_ruf } -} +use crate::AppContext; use std::path::{Path, PathBuf}; use std::fs::{self, File, OpenOptions}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use std::io::{Write, Seek, SeekFrom, Cursor}; use crate::utils::common; use crate::keys; use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; - -#[derive(BinRead)] -struct RufHeader { - #[br(count = 6)] _magic_bytes: Vec, - #[br(count = 2)] _upgrade_type_bytes: Vec, - _unk1: u32, - #[br(count = 32)] date_time_bytes: Vec, - #[br(count = 8)] buyer_bytes: Vec, - #[br(count = 32)] model_bytes: Vec, - #[br(count = 32)] region_info_bytes: Vec, - #[br(count = 4)] version_bytes: Vec, - data_size: u32, - #[br(count = 20)] _unk2: Vec, - dual_ruf_flag: u32, - #[br(count = 44)] _unk3: Vec, - payload_count: u16, - _payload_entry_size: u16, - payloads_start_offset: u32, -} -impl RufHeader { - fn date_time(&self) -> String { - common::string_from_bytes(&self.date_time_bytes) - } - fn buyer(&self) -> String { - common::string_from_bytes(&self.buyer_bytes) - } - fn model(&self) -> String { - common::string_from_bytes(&self.model_bytes) - } - fn region_info(&self) -> String { - common::string_from_bytes(&self.region_info_bytes) - } - fn is_dual_ruf(&self) -> bool { - if self.dual_ruf_flag == 0x44 {true} else {false} - } -} - -#[derive(BinRead)] -struct RufEntry { - #[br(count = 32)] _metadata: Vec, - payload_type_bytes: u32, - size: u32, - _unk1: u32, - #[br(count = 20)] _unk2: Vec, -} -impl RufEntry { - fn payload_type(&self) -> &str { - if self.payload_type_bytes == 1 { - return "squashfs" - } else if self.payload_type_bytes == 2 { - return "cfe" - } else if self.payload_type_bytes == 3 { - return "vmlinuz" - } else if self.payload_type_bytes == 4 { - return "loader" - } else if self.payload_type_bytes == 5 { - return "splash" - } else { - return "unknown" - } - } -} +use include::*; pub fn is_ruf_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/rvp/include.rs b/src/formats/rvp/include.rs new file mode 100644 index 0000000..0429955 --- /dev/null +++ b/src/formats/rvp/include.rs @@ -0,0 +1,7 @@ +pub fn decrypt_xor(data: &[u8]) -> Vec { + let key_bytes = b"\xCC\xF0\xC8\xC4\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA"; + data.iter() + .enumerate() + .map(|(i, &byte)| byte ^ key_bytes[i % key_bytes.len()]) + .collect() +} \ No newline at end of file diff --git a/src/formats/rvp.rs b/src/formats/rvp/mod.rs similarity index 91% rename from src/formats/rvp.rs rename to src/formats/rvp/mod.rs index 9c2dfdc..1779ba1 100644 --- a/src/formats/rvp.rs +++ b/src/formats/rvp/mod.rs @@ -1,22 +1,13 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "rvp", detector_func: is_rvp_file, extractor_func: extract_rvp } -} +use crate::AppContext; use std::path::Path; use std::fs::{self, OpenOptions}; use std::io::{Write, Read, Cursor, Seek}; use crate::utils::common; - -fn decrypt_xor(data: &[u8]) -> Vec { - let key_bytes = b"\xCC\xF0\xC8\xC4\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA\xC6\xCA\xCC\xDA"; - data.iter() - .enumerate() - .map(|(i, &byte)| byte ^ key_bytes[i % key_bytes.len()]) - .collect() -} +use include::*; pub fn is_rvp_file(app_ctx: &AppContext) -> Result>, Box> { let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; @@ -118,7 +109,6 @@ pub fn extract_rvp(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box Vec { + let key_bytes = key.as_bytes(); + data.iter() + .enumerate() + .map(|(i, &byte)| byte ^ key_bytes[i % key_bytes.len()]) + .collect() +} \ No newline at end of file diff --git a/src/formats/samsung_old.rs b/src/formats/samsung_old/mod.rs similarity index 92% rename from src/formats/samsung_old.rs rename to src/formats/samsung_old/mod.rs index 2680c83..b147b31 100644 --- a/src/formats/samsung_old.rs +++ b/src/formats/samsung_old/mod.rs @@ -1,8 +1,6 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "samsung_old", detector_func: is_samsung_old_dir, extractor_func: extract_samsung_old } -} +use crate::AppContext; use std::fs; use std::path::{Path}; @@ -10,12 +8,12 @@ use std::fs::{File, OpenOptions}; use std::io::{Write}; use hex::decode; use sha1::{Digest, Sha1}; +use md5; use crate::utils::common; use crate::keys; use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; - -use md5; +use include::decrypt_xor; pub fn is_samsung_old_dir(app_ctx: &AppContext) -> Result>, Box> { let dir = match app_ctx.dir() {Some(d) => d, None => return Ok(None)}; @@ -27,14 +25,6 @@ pub fn is_samsung_old_dir(app_ctx: &AppContext) -> Result>, } } -fn decrypt_xor(data: &[u8], key: &str) -> Vec { - let key_bytes = key.as_bytes(); - data.iter() - .enumerate() - .map(|(i, &byte)| byte ^ key_bytes[i % key_bytes.len()]) - .collect() -} - pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { let path = app_ctx.dir().ok_or("Extractor expected directory")?; diff --git a/src/formats/sddl_sec/include.rs b/src/formats/sddl_sec/include.rs new file mode 100644 index 0000000..6c29ade --- /dev/null +++ b/src/formats/sddl_sec/include.rs @@ -0,0 +1,212 @@ +//sddl_dec 6.0 https://github.com/theubusu/sddl_dec +use crate::utils::common; +use binrw::BinRead; + +pub fn decipher(s: &[u8]) -> Vec { + let len_ = s.len(); + 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 +} + +// -- STRUCTURES -- +// -- SECFILE -- + +pub static DOWNLOAD_ID: [u8; 4] = [0x11, 0x22, 0x33, 0x44]; + +#[derive(Debug, BinRead)] +pub struct SecHeader { + pub _download_id: [u8; 4], //always 0x11, 0x22, 0x33, 0x44 - magic? + key_id_str_bytes: [u8; 4], //"key_id", purpose unknown + grp_num_str_bytes: [u8; 4], //"grp_num", the count of groups, also represents the count of info files because each group has a respective info file + prg_num_str_bytes: [u8; 4], //"prg_num", the count of module (.FXX) files + _unused_or_reserved: [u8; 16], //not used, is zeros +} +impl SecHeader { + pub fn key_id(&self) -> u32 { + let string = common::string_from_bytes(&self.key_id_str_bytes); + string.parse().unwrap() + } + pub fn grp_num(&self) -> u32 { + let string = common::string_from_bytes(&self.grp_num_str_bytes); + string.parse().unwrap() + } + pub fn prg_num(&self) -> u32 { + let string = common::string_from_bytes(&self.prg_num_str_bytes); + string.parse().unwrap() + } +} + +pub static INFO_FILE_EXTENSION: &str = ".TXT"; + +#[derive(Debug, BinRead)] +pub struct FileHeader { + name_str_bytes: [u8; 12], + size_str_bytes: [u8; 12], +} +impl FileHeader { + pub fn name(&self) -> String { + common::string_from_bytes(&self.name_str_bytes) + } + pub fn size(&self) -> u64 { + let string = common::string_from_bytes(&self.size_str_bytes); + string.parse().unwrap() + } +} + +// -- MODULE -- +#[derive(Debug, BinRead)] +pub struct ModuleComHeader { //"com_header" + pub download_id: [u8; 4], //always 0x11, 0x22, 0x33, 0x44 - magic? + _outer_maker_id: u8, + _outer_model_id: u8, + _inner_maker_id: u8, + _reserve1: u8, + _reserve2: u32, + _reserve3: u32, + _start_version: [u8; 4], //the first version that can upgrade to the new version + _end_version: [u8; 4], //the last version that can upgrade to the new version + _new_version: [u8; 4], //the new version, as in the version of the data in this module + _reserve4: u16, + _module_num: u16, //the logic seems to indicate that there can be multiple entries in one module, but i have never seen this go above 1. +} + +#[derive(Debug, BinRead)] +pub struct ModuleHeader { //"header", appears after com_header + _module_id: u16, + module_atr: u8, + _target_id: u8, + pub cmp_size: u32, + _org_size: u32, + _crc_value: u32, +} +impl ModuleHeader { + pub fn is_ciphered(&self) -> bool { + (self.module_atr & 0x02) != 0 + } + pub fn is_compressed(&self) -> bool { + (self.module_atr & 0x01) != 0 + } +} + +#[derive(Debug, BinRead)] +pub struct ContentHeader { + _magic1: u8, //always 0x01? + _dest_offset: u32, + _source_offset: u32, + pub size: u32, + _magic2: u8, //always 0x21? +} +impl ContentHeader { + //these hacks are needed because for some reason older files have the first nibble of the offset set to D/C + //no idea why, but masking them off makes it works properly + pub fn dest_offset(&self) -> u32 { + if ((self._dest_offset >> 28) & 0xF) == 0xD { + self._dest_offset & 0x0FFFFFFF + } else { + self._dest_offset + } + } + pub fn source_offset(&self) -> u32 { + if ((self._source_offset >> 28) & 0xF) == 0xC { + self._source_offset & 0x0FFFFFFF + } else { + self._source_offset + } + } + pub fn has_subfile(&self) -> bool { + self.source_offset() == 0x10E + } +} + +// -- TDI -- +// Called SDIT.FDI in the secfile + +pub static TDI_FILENAME: &str = "SDIT.FDI"; +pub static SUPPORTED_TDI_VERSION: u16 = 2; + +#[derive(Debug, BinRead)] +pub struct TdiHead { + pub download_id: [u8; 4], //always 0x11, 0x22, 0x33, 0x44 - magic? + pub num_of_group: u8, + _reserve1: u8, + pub format_version: u16, //checks for "2" here +} + +#[derive(Debug, BinRead)] +pub struct TdiGroupHead { + pub group_id: u8, + pub num_of_target: u8, //logic checks that this is not more than 5 + _reserved: u16, +} + +#[derive(Debug, BinRead)] +pub struct TdiTgtInf { + _outer_maker_id: u8, + _outer_model_id: u8, + _inner_maker_id: u8, + _reserve3: u8, + _inner_model_id: [u8; 4], + _ext_model_id: [u8; 4], + pub _start_version: [u8; 4], //the first version that can upgrade to the new version + pub _end_version: [u8; 4], //the last version that can upgrade to the new version + pub new_version: [u8; 4], //the new version, as in the version of the data in this module + pub target_id: u8, + _num_of_compatible_target: u8, + pub num_of_txx: u16, //"TXX" refers to the ".FXX" segment files of each module. I assume F is an encrypted version of T, the same happens with SDIT; "TDI" -> "FDI" + _unknown: [u8; 8], + module_name_bytes: [u8; 8], +} +impl TdiTgtInf { + pub fn module_name(&self) -> String { + common::string_from_bytes(&self.module_name_bytes) + } + pub fn version_string(&self) -> String { + format!("{}.{}{}{}", self.new_version[0], self.new_version[1], self.new_version[2], self.new_version[3]) + } +} + +// -- dec key -- +pub static DEC_KEY: [u8; 16] = [ + 0x26, 0xE0, 0x96, 0xD3, 0xEF, 0x8A, 0x8F, 0xBB, + 0xAA, 0x5E, 0x51, 0x6F, 0x77, 0x26, 0xC2, 0x2C, +]; + +pub static DEC_IV: [u8; 16] = [ + 0x3E, 0x4A, 0xE2, 0x3A, 0x69, 0xDB, 0x81, 0x54, + 0xCD, 0x88, 0x38, 0xC4, 0xB9, 0x0C, 0x76, 0x66, +]; \ No newline at end of file diff --git a/src/formats/sddl_sec.rs b/src/formats/sddl_sec/mod.rs similarity index 52% rename from src/formats/sddl_sec.rs rename to src/formats/sddl_sec/mod.rs index 0820193..823ba48 100644 --- a/src/formats/sddl_sec.rs +++ b/src/formats/sddl_sec/mod.rs @@ -1,227 +1,17 @@ -//sddl_dec 6.0 +//sddl_dec 6.0 https://github.com/theubusu/sddl_dec +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "sddl_sec", detector_func: is_sddl_sec_file, extractor_func: extract_sddl_sec } -} +use crate::AppContext; use std::path::{Path, PathBuf}; use std::fs::{self, File, OpenOptions}; use std::io::{Cursor, Seek, SeekFrom, Write}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; use crate::utils::compression::{decompress_zlib}; - -pub fn decipher(s: &[u8]) -> Vec { - let len_ = s.len(); - 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 -} - -// -- STRUCTURES -- -// -- SECFILE -- - -pub static DOWNLOAD_ID: [u8; 4] = [0x11, 0x22, 0x33, 0x44]; - -#[derive(Debug, BinRead)] -pub struct SecHeader { - pub _download_id: [u8; 4], //always 0x11, 0x22, 0x33, 0x44 - magic? - key_id_str_bytes: [u8; 4], //"key_id", purpose unknown - grp_num_str_bytes: [u8; 4], //"grp_num", the count of groups, also represents the count of info files because each group has a respective info file - prg_num_str_bytes: [u8; 4], //"prg_num", the count of module (.FXX) files - _unused_or_reserved: [u8; 16], //not used, is zeros -} -impl SecHeader { - pub fn key_id(&self) -> u32 { - let string = common::string_from_bytes(&self.key_id_str_bytes); - string.parse().unwrap() - } - pub fn grp_num(&self) -> u32 { - let string = common::string_from_bytes(&self.grp_num_str_bytes); - string.parse().unwrap() - } - pub fn prg_num(&self) -> u32 { - let string = common::string_from_bytes(&self.prg_num_str_bytes); - string.parse().unwrap() - } -} - -pub static INFO_FILE_EXTENSION: &str = ".TXT"; - -#[derive(Debug, BinRead)] -pub struct FileHeader { - name_str_bytes: [u8; 12], - size_str_bytes: [u8; 12], -} -impl FileHeader { - pub fn name(&self) -> String { - common::string_from_bytes(&self.name_str_bytes) - } - pub fn size(&self) -> u64 { - let string = common::string_from_bytes(&self.size_str_bytes); - string.parse().unwrap() - } -} - -// -- MODULE -- -#[derive(Debug, BinRead)] -pub struct ModuleComHeader { //"com_header" - pub download_id: [u8; 4], //always 0x11, 0x22, 0x33, 0x44 - magic? - _outer_maker_id: u8, - _outer_model_id: u8, - _inner_maker_id: u8, - _reserve1: u8, - _reserve2: u32, - _reserve3: u32, - _start_version: [u8; 4], //the first version that can upgrade to the new version - _end_version: [u8; 4], //the last version that can upgrade to the new version - _new_version: [u8; 4], //the new version, as in the version of the data in this module - _reserve4: u16, - _module_num: u16, //the logic seems to indicate that there can be multiple entries in one module, but i have never seen this go above 1. -} - -#[derive(Debug, BinRead)] -pub struct ModuleHeader { //"header", appears after com_header - _module_id: u16, - module_atr: u8, - _target_id: u8, - pub cmp_size: u32, - _org_size: u32, - _crc_value: u32, -} -impl ModuleHeader { - pub fn is_ciphered(&self) -> bool { - (self.module_atr & 0x02) != 0 - } - pub fn is_compressed(&self) -> bool { - (self.module_atr & 0x01) != 0 - } -} - -#[derive(Debug, BinRead)] -pub struct ContentHeader { - _magic1: u8, //always 0x01? - _dest_offset: u32, - _source_offset: u32, - pub size: u32, - _magic2: u8, //always 0x21? -} -impl ContentHeader { - //these hacks are needed because for some reason older files have the first nibble of the offset set to D/C - //no idea why, but masking them off makes it works properly - pub fn dest_offset(&self) -> u32 { - if ((self._dest_offset >> 28) & 0xF) == 0xD { - self._dest_offset & 0x0FFFFFFF - } else { - self._dest_offset - } - } - pub fn source_offset(&self) -> u32 { - if ((self._source_offset >> 28) & 0xF) == 0xC { - self._source_offset & 0x0FFFFFFF - } else { - self._source_offset - } - } - pub fn has_subfile(&self) -> bool { - self.source_offset() == 0x10E - } -} - -// -- TDI -- -// Called SDIT.FDI in the secfile - -pub static TDI_FILENAME: &str = "SDIT.FDI"; -pub static SUPPORTED_TDI_VERSION: u16 = 2; - -#[derive(Debug, BinRead)] -pub struct TdiHead { - pub download_id: [u8; 4], //always 0x11, 0x22, 0x33, 0x44 - magic? - pub num_of_group: u8, - _reserve1: u8, - pub format_version: u16, //checks for "2" here -} - -#[derive(Debug, BinRead)] -pub struct TdiGroupHead { - pub group_id: u8, - pub num_of_target: u8, //logic checks that this is not more than 5 - _reserved: u16, -} - -#[derive(Debug, BinRead)] -pub struct TdiTgtInf { - _outer_maker_id: u8, - _outer_model_id: u8, - _inner_maker_id: u8, - _reserve3: u8, - _inner_model_id: [u8; 4], - _ext_model_id: [u8; 4], - pub _start_version: [u8; 4], //the first version that can upgrade to the new version - pub _end_version: [u8; 4], //the last version that can upgrade to the new version - pub new_version: [u8; 4], //the new version, as in the version of the data in this module - pub target_id: u8, - _num_of_compatible_target: u8, - pub num_of_txx: u16, //"TXX" refers to the ".FXX" segment files of each module. I assume F is an encrypted version of T, the same happens with SDIT; "TDI" -> "FDI" - _unknown: [u8; 8], - module_name_bytes: [u8; 8], -} -impl TdiTgtInf { - pub fn module_name(&self) -> String { - common::string_from_bytes(&self.module_name_bytes) - } - pub fn version_string(&self) -> String { - format!("{}.{}{}{}", self.new_version[0], self.new_version[1], self.new_version[2], self.new_version[3]) - } -} - -// -- dec key -- -static DEC_KEY: [u8; 16] = [ - 0x26, 0xE0, 0x96, 0xD3, 0xEF, 0x8A, 0x8F, 0xBB, - 0xAA, 0x5E, 0x51, 0x6F, 0x77, 0x26, 0xC2, 0x2C, -]; - -static DEC_IV: [u8; 16] = [ - 0x3E, 0x4A, 0xE2, 0x3A, 0x69, 0xDB, 0x81, 0x54, - 0xCD, 0x88, 0x38, 0xC4, 0xB9, 0x0C, 0x76, 0x66, -]; +use include::*; pub fn is_sddl_sec_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/slp/include.rs b/src/formats/slp/include.rs new file mode 100644 index 0000000..cc31b6d --- /dev/null +++ b/src/formats/slp/include.rs @@ -0,0 +1,43 @@ +use crate::utils::common; +use binrw::BinRead; + +#[derive(BinRead)] +pub struct Header { + _magic_bytes: [u8; 4], + version_bytes: [u8; 8], + model_bytes: [u8; 16], + firmware_bytes: [u8; 16], + _unk: u32, + check: [u8; 8], + _unk2: [u8; 8], +} +impl Header { + pub fn version(&self) -> String { + common::string_from_bytes(&self.version_bytes) + } + pub fn model(&self) -> String { + common::string_from_bytes(&self.model_bytes) + } + pub fn firmware(&self) -> String { + common::string_from_bytes(&self.firmware_bytes) + } + pub fn is_new_type(&self) -> bool { + &self.check == b"\x01VER_PR1" + } +} + +#[derive(BinRead)] +pub struct EntryOld { + pub size: u32, + pub _unk: u32, + pub offset: u32, + pub _unk2: u32, +} + +#[derive(BinRead)] +pub struct EntryNew { + pub size: u32, + _unk: u32, + pub offset: u32, + _unk2: [u8; 12], +} \ No newline at end of file diff --git a/src/formats/slp.rs b/src/formats/slp/mod.rs similarity index 67% rename from src/formats/slp.rs rename to src/formats/slp/mod.rs index 0ec6a54..20833b8 100644 --- a/src/formats/slp.rs +++ b/src/formats/slp/mod.rs @@ -1,56 +1,14 @@ +mod include; use std::any::Any; -use crate::{AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "slp", detector_func: is_slp_file, extractor_func: extract_slp } -} +use crate::AppContext; use std::path::{Path}; use std::fs::{self, OpenOptions}; use std::io::{Write, Seek, SeekFrom}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; - -#[derive(BinRead)] -struct Header { - #[br(count = 4)] _magic_bytes: Vec, - #[br(count = 8)] version_bytes: Vec, - #[br(count = 16)] model_bytes: Vec, - #[br(count = 16)] firmware_bytes: Vec, - _unk: u32, - #[br(count = 8)] check: Vec, - #[br(count = 8)] _unk2: Vec, -} -impl Header { - fn version(&self) -> String { - common::string_from_bytes(&self.version_bytes) - } - fn model(&self) -> String { - common::string_from_bytes(&self.model_bytes) - } - fn firmware(&self) -> String { - common::string_from_bytes(&self.firmware_bytes) - } - fn is_new_type(&self) -> bool { - self.check == b"\x01VER_PR1" - } -} - -#[derive(BinRead)] -struct EntryOld { - size: u32, - _unk: u32, - offset: u32, - _unk2: u32, -} - -#[derive(BinRead)] -struct EntryNew { - size: u32, - _unk: u32, - offset: u32, - #[br(count = 12)] _unk2: Vec, -} +use include::*; pub fn is_slp_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/formats/sony_bdp/include.rs b/src/formats/sony_bdp/include.rs new file mode 100644 index 0000000..1859f1d --- /dev/null +++ b/src/formats/sony_bdp/include.rs @@ -0,0 +1,52 @@ +use crate::utils::common; +use binrw::BinRead; + +//thx sony +static HEX_SUBSTITUTION: [u8; 256] = [ + 0xE8, 0x4D, 0x63, 0xF4, 0xF8, 0xA9, 0x21, 0x9C, 0xC7, 0x82, 0xCD, 0xE3, 0xC1, 0xCE, 0xC0, 0xFA, 0xE7, 0xD6, 0x96, 0x46, 0x12, 0x03, 0x14, 0x33, 0xED, 0x10, 0xEC, 0x69, 0x16, 0xE0, 0x28, 0x30, + 0x77, 0x0E, 0x3D, 0xEF, 0x36, 0x4C, 0x18, 0xEB, 0x41, 0x89, 0x64, 0x8A, 0x70, 0x0C, 0x23, 0xA3, 0x79, 0x6D, 0x75, 0x7E, 0x1A, 0x2D, 0x01, 0x91, 0x88, 0xCB, 0xFC, 0x8B, 0xFD, 0x94, 0x0A, 0x39, + 0xBC, 0x98, 0x87, 0xBB, 0xC2, 0x9B, 0x81, 0x1C, 0x4B, 0xA6, 0x58, 0x59, 0x45, 0x57, 0x8C, 0x7B, 0x5A, 0x56, 0x08, 0x73, 0x65, 0xEE, 0x2A, 0x25, 0xB0, 0x5B, 0x55, 0xB2, 0xB8, 0x1E, 0xEA, 0xC5, + 0x6A, 0x40, 0x86, 0x5C, 0x3C, 0x54, 0xBF, 0xF6, 0xA8, 0xF2, 0x06, 0x4A, 0xFE, 0xC4, 0x32, 0x8D, 0xF0, 0x5D, 0x35, 0x53, 0xD7, 0xBD, 0xBA, 0x20, 0x2F, 0xCA, 0xE1, 0xCC, 0xA4, 0x44, 0x85, 0xDE, + 0xDB, 0x5E, 0x27, 0x52, 0x6E, 0x38, 0x04, 0x66, 0xD0, 0x92, 0xD3, 0xC6, 0x7D, 0x71, 0xDA, 0x2C, 0x9A, 0x49, 0x8E, 0x80, 0x13, 0x5F, 0x11, 0x51, 0x15, 0x22, 0xCF, 0xAC, 0x0F, 0xD5, 0xFF, 0x3F, + 0x17, 0xAD, 0xD8, 0xD9, 0xAB, 0x02, 0x6B, 0x0D, 0xDF, 0xF1, 0x84, 0x3B, 0x78, 0x19, 0x76, 0x60, 0x50, 0xDC, 0xC3, 0xB5, 0x43, 0x0B, 0x95, 0x97, 0x67, 0xD4, 0x29, 0xF7, 0xE4, 0x1B, 0xAE, 0x48, + 0xB1, 0x8F, 0x24, 0x7A, 0x74, 0xFB, 0x34, 0x09, 0x00, 0x31, 0x9F, 0x61, 0x4F, 0xB6, 0xA0, 0xA7, 0xB4, 0x9E, 0x1D, 0xAA, 0xF9, 0xBE, 0x37, 0x2E, 0xB9, 0x6F, 0xA5, 0x83, 0xA1, 0x93, 0x07, 0xE2, + 0x7F, 0x3E, 0xF3, 0x99, 0x62, 0x4E, 0xE9, 0xC8, 0x6C, 0x68, 0x1F, 0x47, 0x42, 0x26, 0x9D, 0xE5, 0x7C, 0x72, 0x3A, 0x2B, 0xE6, 0xF5, 0xD2, 0x90, 0x05, 0xD1, 0xDD, 0xC9, 0xAF, 0xB7, 0xA2, 0xB3, +]; + +pub fn hex_substitute(data: &[u8]) -> Vec { + data.iter().map(|&b| HEX_SUBSTITUTION[b as usize]).collect() +} + +#[derive(BinRead)] +pub struct Header { + firmware_name_bytes: [u8; 8], + _unk_version_bytes: [u8; 8], + firmware_version_bytes: [u8; 16], + date_bytes: [u8; 16], + _unk2_version_bytes: [u8; 8], + _unk3_version_bytes: [u8; 8], + _unk4_version_bytes: [u8; 16], + _unk: u32, + _checksum: u64, + pub file_size: u32, + _unk5_version_bytes: [u8; 16], + _unk6_version_bytes: [u8; 16], + _unk2: [u8; 32], +} +impl Header { + pub fn firmware_name(&self) -> String { + common::string_from_bytes(&self.firmware_name_bytes) + } + pub fn firmware_version(&self) -> String { + common::string_from_bytes(&self.firmware_version_bytes) + } + pub fn date(&self) -> String { + common::string_from_bytes(&self.date_bytes) + } +} + +#[derive(BinRead)] +pub struct Entry { + pub offset: u32, + pub size: u32, +} \ No newline at end of file diff --git a/src/formats/sony_bdp.rs b/src/formats/sony_bdp/mod.rs similarity index 50% rename from src/formats/sony_bdp.rs rename to src/formats/sony_bdp/mod.rs index 9543374..1ee0a9d 100644 --- a/src/formats/sony_bdp.rs +++ b/src/formats/sony_bdp/mod.rs @@ -1,67 +1,16 @@ +mod include; use std::any::Any; -use crate::{InputTarget, AppContext, formats::Format}; -pub fn format() -> Format { - Format { name: "sony_bdp", detector_func: is_sony_bdp_file, extractor_func: extract_sony_bdp } -} +use crate::{InputTarget, AppContext}; use std::fs::File; use std::path::{Path, PathBuf}; use std::fs::{self, OpenOptions}; use std::io::{Cursor, Write}; -use binrw::{BinRead, BinReaderExt}; +use binrw::BinReaderExt; use crate::utils::common; use crate::formats; - -//thx sony -static HEX_SUBSTITUTION: [u8; 256] = [ - 0xE8, 0x4D, 0x63, 0xF4, 0xF8, 0xA9, 0x21, 0x9C, 0xC7, 0x82, 0xCD, 0xE3, 0xC1, 0xCE, 0xC0, 0xFA, 0xE7, 0xD6, 0x96, 0x46, 0x12, 0x03, 0x14, 0x33, 0xED, 0x10, 0xEC, 0x69, 0x16, 0xE0, 0x28, 0x30, - 0x77, 0x0E, 0x3D, 0xEF, 0x36, 0x4C, 0x18, 0xEB, 0x41, 0x89, 0x64, 0x8A, 0x70, 0x0C, 0x23, 0xA3, 0x79, 0x6D, 0x75, 0x7E, 0x1A, 0x2D, 0x01, 0x91, 0x88, 0xCB, 0xFC, 0x8B, 0xFD, 0x94, 0x0A, 0x39, - 0xBC, 0x98, 0x87, 0xBB, 0xC2, 0x9B, 0x81, 0x1C, 0x4B, 0xA6, 0x58, 0x59, 0x45, 0x57, 0x8C, 0x7B, 0x5A, 0x56, 0x08, 0x73, 0x65, 0xEE, 0x2A, 0x25, 0xB0, 0x5B, 0x55, 0xB2, 0xB8, 0x1E, 0xEA, 0xC5, - 0x6A, 0x40, 0x86, 0x5C, 0x3C, 0x54, 0xBF, 0xF6, 0xA8, 0xF2, 0x06, 0x4A, 0xFE, 0xC4, 0x32, 0x8D, 0xF0, 0x5D, 0x35, 0x53, 0xD7, 0xBD, 0xBA, 0x20, 0x2F, 0xCA, 0xE1, 0xCC, 0xA4, 0x44, 0x85, 0xDE, - 0xDB, 0x5E, 0x27, 0x52, 0x6E, 0x38, 0x04, 0x66, 0xD0, 0x92, 0xD3, 0xC6, 0x7D, 0x71, 0xDA, 0x2C, 0x9A, 0x49, 0x8E, 0x80, 0x13, 0x5F, 0x11, 0x51, 0x15, 0x22, 0xCF, 0xAC, 0x0F, 0xD5, 0xFF, 0x3F, - 0x17, 0xAD, 0xD8, 0xD9, 0xAB, 0x02, 0x6B, 0x0D, 0xDF, 0xF1, 0x84, 0x3B, 0x78, 0x19, 0x76, 0x60, 0x50, 0xDC, 0xC3, 0xB5, 0x43, 0x0B, 0x95, 0x97, 0x67, 0xD4, 0x29, 0xF7, 0xE4, 0x1B, 0xAE, 0x48, - 0xB1, 0x8F, 0x24, 0x7A, 0x74, 0xFB, 0x34, 0x09, 0x00, 0x31, 0x9F, 0x61, 0x4F, 0xB6, 0xA0, 0xA7, 0xB4, 0x9E, 0x1D, 0xAA, 0xF9, 0xBE, 0x37, 0x2E, 0xB9, 0x6F, 0xA5, 0x83, 0xA1, 0x93, 0x07, 0xE2, - 0x7F, 0x3E, 0xF3, 0x99, 0x62, 0x4E, 0xE9, 0xC8, 0x6C, 0x68, 0x1F, 0x47, 0x42, 0x26, 0x9D, 0xE5, 0x7C, 0x72, 0x3A, 0x2B, 0xE6, 0xF5, 0xD2, 0x90, 0x05, 0xD1, 0xDD, 0xC9, 0xAF, 0xB7, 0xA2, 0xB3, -]; - -fn hex_substitute(data: &[u8]) -> Vec { - data.iter().map(|&b| HEX_SUBSTITUTION[b as usize]).collect() -} - -#[derive(BinRead)] -struct Header { - #[br(count = 8)] firmware_name_bytes: Vec, - #[br(count = 8)] _unk_version_bytes: Vec, - #[br(count = 16)] firmware_version_bytes: Vec, - #[br(count = 16)] date_bytes: Vec, - #[br(count = 8)] _unk2_version_bytes: Vec, - #[br(count = 8)] _unk3_version_bytes: Vec, - #[br(count = 16)] _unk4_version_bytes: Vec, - _unk: u32, - _checksum: u64, - file_size: u32, - #[br(count = 16)] _unk5_version_bytes: Vec, - #[br(count = 16)] _unk6_version_bytes: Vec, - #[br(count = 32)] _unk2: Vec, -} -impl Header { - fn firmware_name(&self) -> String { - common::string_from_bytes(&self.firmware_name_bytes) - } - fn firmware_version(&self) -> String { - common::string_from_bytes(&self.firmware_version_bytes) - } - fn date(&self) -> String { - common::string_from_bytes(&self.date_bytes) - } -} - -#[derive(BinRead)] -struct Entry { - offset: u32, - size: u32, -} +use include::*; pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; diff --git a/src/utils.rs b/src/utils.rs index b155bb2..5eec0cd 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,14 +1,5 @@ pub mod common; pub mod aes; -pub mod pana_dvd_crypto; -pub mod lzss; pub mod lzop; pub mod sparse; -pub mod compression; -pub mod android_ota_update_metadata; -pub mod mtk_crypto; -pub mod lzhs; -pub mod huffman_tables; -pub mod msd_ouith_parser_old; -pub mod msd_ouith_parser_tizen_1_8; -pub mod msd_ouith_parser_tizen_1_9; \ No newline at end of file +pub mod compression; \ No newline at end of file