RESTRUCTURING 1

- change formats to have their own folders with mod + include files + anything they want
- change format list to be defined in formats.rs instead of in each format
- move utils to their respective formats
- some minor code changes in some formats
This commit is contained in:
theubusu
2026-02-17 17:28:59 +01:00
parent 9fe39280cd
commit aa2249f024
69 changed files with 1678 additions and 1629 deletions
+147 -29
View File
@@ -42,36 +42,154 @@ pub mod mtk_pkg_old;
pub mod mtk_pkg_new; pub mod mtk_pkg_new;
pub mod mtk_bdp; pub mod mtk_bdp;
//define all formats here
pub fn get_registry() -> Vec<Format> { pub fn get_registry() -> Vec<Format> {
return vec![ return vec![
crate::formats::mstar::format(), Format {
crate::formats::samsung_old::format(), name: "mstar",
crate::formats::nvt_timg::format(), detector_func: crate::formats::mstar::is_mstar_file,
crate::formats::pfl_upg::format(), extractor_func: crate::formats::mstar::extract_mstar,
crate::formats::sddl_sec::format(), },
crate::formats::novatek::format(), Format {
crate::formats::ruf::format(), name: "samsung_old",
crate::formats::invincible_image::format(), detector_func: crate::formats::samsung_old::is_samsung_old_dir,
crate::formats::slp::format(), extractor_func: crate::formats::samsung_old::extract_samsung_old,
crate::formats::roku::format(), },
crate::formats::sony_bdp::format(), Format {
crate::formats::rvp::format(), name: "nvt_timg",
crate::formats::funai_upg::format(), detector_func: crate::formats::nvt_timg::is_nvt_timg_file,
crate::formats::pana_dvd::format(), extractor_func: crate::formats::nvt_timg::extract_nvt_timg,
crate::formats::android_ota_payload::format(), },
crate::formats::bdl::format(), Format {
crate::formats::amlogic::format(), name: "pfl_upg",
crate::formats::pup::format(), detector_func: crate::formats::pfl_upg::is_pfl_upg_file,
crate::formats::msd10::format(), extractor_func: crate::formats::pfl_upg::extract_pfl_upg,
crate::formats::msd11::format(), },
crate::formats::epk::format(), Format {
crate::formats::epk1::format(), name: "sddl_sec",
crate::formats::epk2::format(), detector_func: crate::formats::sddl_sec::is_sddl_sec_file,
crate::formats::epk2b::format(), extractor_func: crate::formats::sddl_sec::extract_sddl_sec,
crate::formats::epk3::format(), },
crate::formats::mtk_pkg::format(), Format {
crate::formats::mtk_pkg_old::format(), name: "novatek",
crate::formats::mtk_pkg_new::format(), detector_func: crate::formats::novatek::is_novatek_file,
crate::formats::mtk_bdp::format(), 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,
},
] ]
} }
+39
View File
@@ -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
}
}
@@ -1,53 +1,15 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "amlogic", detector_func: is_amlogic_file, extractor_func: extract_amlogic }
}
use std::path::{Path}; use std::path::Path;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Write, Seek, SeekFrom}; use std::io::{Write, Seek, SeekFrom};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use crate::utils::sparse::{unsparse_to_file}; use crate::utils::sparse::unsparse_to_file;
use include::*;
#[derive(BinRead)]
struct ImageHeader {
_crc32: u32,
version: u32,
#[br(count = 4)] _magic_bytes: Vec<u8>, //56 19 B5 27
image_size: u64,
item_align_size: u32,
item_count: u32,
#[br(count = 36)] _reserved: Vec<u8>,
}
#[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<u8>,
#[br(count = 256)] name_bytes: Vec<u8>,
_verify: u32,
_is_backup_item: u16,
_backup_item_id: u16,
#[br(count = 24)] _reserved: Vec<u8>,
}
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
}
}
pub fn is_amlogic_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_amlogic_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -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,
}
@@ -1,26 +1,18 @@
mod include;
mod android_ota_update_metadata;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "android_ota_payload", detector_func: is_android_ota_payload_file, extractor_func: extract_android_ota_payload }
}
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::path::{Path}; use std::path::Path;
use std::io::{Write}; use std::io::Write;
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use prost::Message; use prost::Message;
use crate::utils::common; 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}; use crate::utils::compression::{decompress_bzip, decompress_xz};
use include::*;
#[derive(BinRead)]
struct Header {
#[br(count = 4)] _magic_bytes: Vec<u8>, //CrAU
file_format_version: u64,
manifest_size: u64,
metadata_signature_size: u32,
}
pub fn is_android_ota_payload_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_android_ota_payload_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+76
View File
@@ -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)
}
}
+5 -80
View File
@@ -1,89 +1,14 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "bdl", detector_func: is_bdl_file, extractor_func: extract_bdl }
}
use std::path::{Path}; use std::path::Path;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Write, Seek, SeekFrom}; use std::io::{Write, Seek, SeekFrom};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use include::*;
#[derive(BinRead)]
struct BdlHeader {
#[br(count = 4)] _magic_bytes: Vec<u8>, //ibdl
#[br(count = 8)] _file_version: Vec<u8>,
_unk1: u32,
pkg_count: u32,
#[br(count = 12)] _unk2: Vec<u8>,
#[br(count = 256)] date_bytes: Vec<u8>,
#[br(count = 256)] manufacturer_bytes: Vec<u8>,
#[br(count = 256)] model_bytes: Vec<u8>,
#[br(count = 9)] _unk3: Vec<u8>,
#[br(count = 256)] version_bytes: Vec<u8>,
#[br(count = 1280)] info_bytes: Vec<u8>,
}
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<u8>, //ipkg
#[br(count = 12)] _unk1: Vec<u8>,
entry_count: u32,
#[br(count = 12)] _unk2: Vec<u8>,
#[br(count = 256)] version_bytes: Vec<u8>,
#[br(count = 256)] manufacturer_bytes: Vec<u8>,
#[br(count = 256)] name_bytes: Vec<u8>,
#[br(count = 285)] _unk3: Vec<u8>,
}
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<u8>,
offset: u64,
size: u64,
_crc32: u32,
}
impl PkgEntry {
fn name(&self) -> String {
common::string_from_bytes(&self.name_bytes)
}
}
pub fn is_bdl_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_bdl_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
@@ -1,8 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "epk", detector_func: is_epk_file, extractor_func: extract_epk }
}
use crate::utils::common; use crate::utils::common;
use crate::formats; use crate::formats;
+31
View File
@@ -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,
}
@@ -1,44 +1,14 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "epk1", detector_func: is_epk1_file, extractor_func: extract_epk1 }
}
use std::path::{Path}; use std::path::Path;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Write, Seek, SeekFrom}; use std::io::{Write, Seek, SeekFrom};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use include::*;
#[derive(BinRead)]
struct CommonHeader {
#[br(count = 4)] _magic_bytes: Vec<u8>,
file_size: u32,
pak_count: u32,
}
#[derive(BinRead)]
struct PakHeader {
#[br(count = 4)] pak_name_bytes: Vec<u8>,
image_size: u32,
#[br(count = 64)] platform_id_bytes: Vec<u8>,
#[br(count = 56)] _reserved: Vec<u8>,
}
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,
}
pub fn is_epk1_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_epk1_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+59
View File
@@ -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,
}
@@ -1,75 +1,16 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "epk2", detector_func: is_epk2_file, extractor_func: extract_epk2 }
}
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::path::{Path}; use std::path::Path;
use std::io::{Write, Seek, SeekFrom, Cursor}; use std::io::{Write, Seek, SeekFrom, Cursor};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use crate::keys; use crate::keys;
use crate::formats::epk::{decrypt_aes_ecb_auto, find_key}; use crate::formats::epk::{decrypt_aes_ecb_auto, find_key};
use include::*;
static SIGNATURE_SIZE: u32 = 128;
#[derive(BinRead)]
struct Header {
#[br(count = 4)] _magic_bytes: Vec<u8>, //epak
file_size: u32,
pak_count: u32,
#[br(count = 4)] _epk2_magic: Vec<u8>, //EPK2
#[br(count = 4)] version: Vec<u8>,
#[br(count = 32)] ota_id_bytes: Vec<u8>,
}
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<u8>,
#[br(count = 4)] _version: Vec<u8>,
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<u8>,
image_size: u32,
#[br(count = 64)] platform_id_bytes: Vec<u8>,
_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<u8>, //MPAK
#[br(count = 24)] _reserved: Vec<u8>,
_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,
}
pub fn is_epk2_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_epk2_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; 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<dyn Any>) -> Result<(), Box<
let mut file = app_ctx.file().ok_or("Extractor expected file")?; let mut file = app_ctx.file().ok_or("Extractor expected file")?;
file.seek(SeekFrom::Start(0))?; 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 _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?;
let stored_header = common::read_exact(&mut file, 1584)?; //max header size let stored_header = common::read_exact(&mut file, 1584)?; //max header size
let header; let header;
+47
View File
@@ -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,
}
@@ -1,60 +1,14 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "epk2b", detector_func: is_epk2b_file, extractor_func: extract_epk2b }
}
use std::path::{Path}; use std::path::{Path};
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Write, Seek, SeekFrom}; use std::io::{Write, Seek, SeekFrom};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use include::*;
#[derive(BinRead)]
struct EpkHeader {
#[br(count = 4)] _epk_magic: Vec<u8>, //epak
file_size: u32,
pak_count: u32,
#[br(count = 4)] _epk2_magic: Vec<u8>, //EPK2
#[br(count = 4)] version: Vec<u8>,
#[br(count = 32)] ota_id_bytes: Vec<u8>,
}
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<u8>,
image_size: u32,
#[br(count = 64)] platform_id_bytes: Vec<u8>,
_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<u8>, //MPAK
#[br(count = 24)] _reserved: Vec<u8>,
_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,
}
pub fn is_epk2b_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_epk2b_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+64
View File
@@ -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)
}
}
@@ -1,80 +1,16 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "epk3", detector_func: is_epk3_file, extractor_func: extract_epk3 }
}
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::path::{Path}; use std::path::Path;
use std::io::{Write, Seek, SeekFrom, Cursor}; use std::io::{Write, Seek, SeekFrom, Cursor};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use crate::keys; use crate::keys;
use crate::formats::epk::{decrypt_aes_ecb_auto, find_key}; use crate::formats::epk::{decrypt_aes_ecb_auto, find_key};
use include::*;
#[derive(BinRead)]
struct Header {
#[br(count = 4)] _magic_bytes: Vec<u8>, //EPK3
#[br(count = 4)] version: Vec<u8>,
#[br(count = 32)] ota_id_bytes: Vec<u8>,
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<u8>,
#[br(count = 6)] encrypt_type_bytes: Vec<u8>,
#[br(count = 6)] update_type_bytes: Vec<u8>,
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<u8>,
#[br(count = 96)] _package_version_bytes: Vec<u8>,
#[br(count = 32)] _package_architecture_bytes: Vec<u8>,
#[br(count = 32)] _checksum: Vec<u8>,
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)
}
}
pub fn is_epk3_file(_app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_epk3_file(_app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
Ok(None) Ok(None)
+15
View File
@@ -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,
}
@@ -1,29 +1,14 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "funai_upg", detector_func: is_funai_upg_file, extractor_func: extract_funai_upg }
}
use std::path::Path; use std::path::Path;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Write}; use std::io::Write;
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use include::*;
#[derive(BinRead)]
struct Header {
#[br(count = 6)] _magic_bytes: Vec<u8>,
entry_count: u16,
file_size: u32,
}
#[derive(BinRead)]
struct Entry {
entry_type: u16,
entry_size: u32,
_unk: u16,
}
pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+56
View File
@@ -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)
}
}
@@ -1,66 +1,15 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "invincible_image", detector_func: is_invincible_image_file, extractor_func: extract_invincible_image }
}
use std::path::{Path}; use std::path::{Path};
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Write, Read, Seek, SeekFrom, Cursor}; 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::aes::{decrypt_aes128_cbc_nopad};
use crate::utils::common; use crate::utils::common;
use include::*;
#[derive(BinRead)]
struct Header {
#[br(count = 16)] _magic_bytes: Vec<u8>,
#[br(count = 4)] file_version: Vec<u8>,
_unk1: u32,
#[br(count = 16)] ver1_bytes: Vec<u8>,
#[br(count = 16)] ver2_bytes: Vec<u8>,
_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<u8>,
#[br(count = 16)] ver4_bytes: Vec<u8>,
#[br(count = 11)] _unk6: Vec<u8>,
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<u8>,
start_offset: u32,
size: u32,
}
impl Entry {
fn name(&self) -> String {
common::string_from_bytes(&self.name_bytes)
}
}
pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; 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<dyn Any>) -> Res
file.seek(SeekFrom::Current(header.skip_size.into()))?; 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..."); 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); let mut data_reader = Cursor::new(decrypted_data);
@@ -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 // COMMON MSD FUNCTIONS
use aes::Aes128; use aes::Aes128;
use cbc::{Decryptor, cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit}}; use cbc::{Decryptor, cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit}};
+28
View File
@@ -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<u8>,
}
impl HeaderEntry {
pub fn name(&self) -> String {
common::string_from_bytes(&self.name_bytes)
}
}
@@ -1,51 +1,18 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "msd10", detector_func: is_msd10_file, extractor_func: extract_msd10 }
}
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::path::{Path}; use std::path::Path;
use std::io::{Write, Seek, SeekFrom}; use std::io::{Write, Seek, SeekFrom};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use crate::keys; use crate::keys;
use crate::formats::msd::{decrypt_aes_salted_old, decrypt_aes_salted_tizen, decrypt_aes_tizen}; 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::formats::msd::msd_ouith_parser_old::{parse_ouith_blob};
use crate::utils::msd_ouith_parser_tizen_1_8::{parse_blob_1_8}; use crate::formats::msd::msd_ouith_parser_tizen_1_8::{parse_blob_1_8};
use include::*;
#[derive(BinRead)]
struct FileHeader {
#[br(count = 6)] _magic_bytes: Vec<u8>,
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<u8>,
}
impl HeaderEntry {
fn name(&self) -> String {
common::string_from_bytes(&self.name_bytes)
}
}
struct Section {
index: u32,
offset: u32,
size: u32,
}
pub fn is_msd10_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_msd10_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; 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<dyn Any>) -> Result<(), Box
let header: FileHeader = file.read_le()?; let header: FileHeader = file.read_le()?;
println!("\nNumber of sections: {}", header.section_count); println!("\nNumber of sections: {}", header.section_count);
let mut sections: Vec<Section> = Vec::new(); let mut sections: Vec<SectionEntry> = Vec::new();
for _i in 0..header.section_count { for _i in 0..header.section_count {
let section: SectionEntry = file.read_le()?; let section: SectionEntry = file.read_le()?;
println!("Section {}: offset: {}, size: {}", section.index, section.offset, section.size); 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)?; let _zero_padding = common::read_exact(&mut file, 4)?;
+30
View File
@@ -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<u8>,
}
impl HeaderEntry {
pub fn name(&self) -> String {
common::string_from_bytes(&self.name_bytes)
}
}
@@ -1,52 +1,17 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "msd11", detector_func: is_msd11_file, extractor_func: extract_msd11 }
}
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::path::{Path}; use std::path::Path;
use std::io::{Write}; use std::io::Write;
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use crate::keys; use crate::keys;
use crate::formats::msd::{decrypt_aes_salted_tizen, decrypt_aes_tizen}; use crate::formats::msd::{decrypt_aes_salted_tizen, decrypt_aes_tizen};
use crate::utils::msd_ouith_parser_tizen_1_9::{parse_blob_1_9}; use crate::formats::msd::msd_ouith_parser_tizen_1_9::{parse_blob_1_9};
use include::*;
#[derive(BinRead)]
struct FileHeader {
#[br(count = 6)] _magic_bytes: Vec<u8>,
_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<u8>,
}
impl HeaderEntry {
fn name(&self) -> String {
common::string_from_bytes(&self.name_bytes)
}
}
struct Section {
index: u32,
offset: u64,
size: u64,
}
pub fn is_msd11_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_msd11_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; 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<dyn Any>) -> Result<(), Box
let header: FileHeader = file.read_le()?; let header: FileHeader = file.read_le()?;
println!("\nNumber of sections: {}", header.section_count); println!("\nNumber of sections: {}", header.section_count);
let mut sections: Vec<Section> = Vec::new(); let mut sections: Vec<SectionEntry> = Vec::new();
for _i in 0..header.section_count { for _i in 0..header.section_count {
let section: SectionEntry = file.read_le()?; let section: SectionEntry = file.read_le()?;
println!("Section {}: offset: {}, size: {}", section.index, section.offset, section.size); 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()?; let header_count: u32 = file.read_le()?;
+7
View File
@@ -0,0 +1,7 @@
pub fn parse_number(s: &str) -> Option<u64> {
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()
}
}
@@ -1,17 +1,16 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "mstar", detector_func: is_mstar_file, extractor_func: extract_mstar }
}
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::path::{Path}; use std::path::Path;
use std::io::{Write}; use std::io::Write;
use crate::utils::common; use crate::utils::common;
use crate::utils::compression::{decompress_lzma, decompress_lz4}; use crate::utils::compression::{decompress_lzma, decompress_lz4};
use crate::utils::lzop::{unlzop_to_file}; use crate::utils::lzop::{unlzop_to_file};
use crate::utils::sparse::{unsparse_to_file}; use crate::utils::sparse::{unsparse_to_file};
use include::*;
pub fn is_mstar_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_mstar_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; 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<Option<Box<dyn Any>>, Box<d
} }
} }
fn parse_number(s: &str) -> Option<u64> {
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<dyn Any>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_mstar(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let file = app_ctx.file().ok_or("Extractor expected file")?; let file = app_ctx.file().ok_or("Extractor expected file")?;
+65
View File
@@ -0,0 +1,65 @@
use crate::utils::common;
use binrw::BinRead;
pub fn find_bytes(data: &[u8], pattern: &[u8]) -> Option<usize> {
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
@@ -1,82 +1,19 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "mtk_bdp", detector_func: is_mtk_bdp_file, extractor_func: extract_mtk_bdp }
}
use std::path::{Path}; use std::path::{Path};
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Seek, SeekFrom, Read, Write}; use std::io::{Seek, SeekFrom, Read, Write};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use include::*;
pub struct MtkBdpContext { pub struct MtkBdpContext {
pitit_offset: u64, 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<u8>,
_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<u8>,
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<u8>,
}
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<usize> {
data.windows(pattern.len()).position(|window| window == pattern)
}
pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
let file_size = file.metadata()?.len(); let file_size = file.metadata()?.len();
+63
View File
@@ -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
}
}
@@ -3,7 +3,7 @@ use std::io::{Write, Cursor, Seek, SeekFrom, Read};
use binrw::{BinRead, BinReaderExt}; use binrw::{BinRead, BinReaderExt};
use std::path::{PathBuf}; use std::path::{PathBuf};
use crate::utils::huffman_tables::{CHARLEN, POS}; use super::huffman_tables::{CHARLEN, POS};
use crate::utils::compression::{decompress_lz4}; use crate::utils::compression::{decompress_lz4};
#[derive(BinRead)] #[derive(BinRead)]
@@ -11,7 +11,7 @@ struct LzhsHeader {
uncompressed_size: u32, uncompressed_size: u32,
compressed_size: u32, compressed_size: u32,
checksum_or_seg_idx: u16, //as checksum in normal lzhs header, as index in lzhs_fs header checksum_or_seg_idx: u16, //as checksum in normal lzhs header, as index in lzhs_fs header
#[br(count = 6)] padding: Vec<u8>, padding: [u8; 6],
} }
#[derive(BinRead)] #[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 //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 //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?}", 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); 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);
@@ -1,86 +1,25 @@
pub mod include;
pub mod lzhs;
mod huffman_tables;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "mtk_pkg", detector_func: is_mtk_pkg_file, extractor_func: extract_mtk_pkg }
}
use std::path::Path; use std::path::Path;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Write, Cursor, Seek, SeekFrom}; use std::io::{Write, Cursor, Seek, SeekFrom};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::utils::aes::{decrypt_aes128_cbc_nopad};
use crate::utils::lzhs::{decompress_lzhs_fs_file2file};
use crate::keys; use crate::keys;
use lzhs::{decompress_lzhs_fs_file2file};
use include::*;
pub struct MtkPkgContext { pub struct MtkPkgContext {
is_philips_variant: bool, is_philips_variant: bool,
decrypted_header: Vec<u8>, decrypted_header: Vec<u8>,
} }
#[derive(BinRead)]
struct Header {
#[br(count = 4)] vendor_magic_bytes: Vec<u8>,
#[br(count = 8)] _mtk_magic: Vec<u8>, //#DH@FiRm
#[br(count = 60)] version_bytes: Vec<u8>,
file_size: u32,
_flags: u32,
#[br(count = 32)] product_name_bytes: Vec<u8>,
#[br(count = 32)] _digest: Vec<u8>,
}
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<u8>,
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<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+1
View File
@@ -0,0 +1 @@
pub static HEADER_SIZE: usize = 0x170;
@@ -1,20 +1,18 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "mtk_pkg_new", detector_func: is_mtk_pkg_new_file, extractor_func: extract_mtk_pkg_new }
}
use std::path::Path; use std::path::Path;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Write, Cursor, Seek, SeekFrom}; use std::io::{Write, Cursor, Seek, SeekFrom};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::utils::aes::{decrypt_aes128_cbc_nopad};
use crate::utils::lzhs::{decompress_lzhs_fs_file2file};
use crate::keys; use crate::keys;
use crate::formats::mtk_pkg::lzhs::{decompress_lzhs_fs_file2file};
use crate::formats::mtk_pkg::{MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC}; use crate::formats::mtk_pkg::include::{Header, PartEntry, MTK_HEADER_MAGIC, MTK_META_MAGIC, MTK_META_PAD_MAGIC};
use include::*;
pub struct MtkPkgNewContext { pub struct MtkPkgNewContext {
matching_key_name: String, matching_key_name: String,
@@ -23,53 +21,6 @@ pub struct MtkPkgNewContext {
decrypted_header: Vec<u8>, decrypted_header: Vec<u8>,
} }
#[derive(BinRead)]
struct Header {
#[br(count = 4)] vendor_magic_bytes: Vec<u8>,
#[br(count = 8)] _mtk_magic: Vec<u8>, //#DH@FiRm
#[br(count = 56)] version_bytes: Vec<u8>,
_unk: u32,
file_size: u32,
_flags: u32,
#[br(count = 32)] product_name_bytes: Vec<u8>,
#[br(count = 256)] _digest: Vec<u8>,
}
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<u8>,
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<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_mtk_pkg_new_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+33
View File
@@ -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)
}
}
@@ -1,67 +1,18 @@
mod include;
mod mtk_crypto;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "mtk_pkg_old", detector_func: is_mtk_pkg_old_file, extractor_func: extract_mtk_pkg_old }
}
use std::path::Path; use std::path::Path;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Write, Cursor, Seek}; use std::io::{Write, Cursor, Seek};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use crate::utils::mtk_crypto::{decrypt}; use crate::formats::mtk_pkg::lzhs::{decompress_lzhs_fs_file2file_old};
use crate::utils::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};
static KEY: u32 = 0x94102909; //09 29 10 94 use include::*;
// 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<u8>,
#[br(count = 8)] _mtk_magic: Vec<u8>, //#DH@FiRm
#[br(count = 68)] version_bytes: Vec<u8>,
file_size: u32,
_flags: u32,
#[br(count = 32)] product_name_bytes: Vec<u8>,
#[br(count = 32)] _digest: Vec<u8>,
}
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<u8>,
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;
pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+30
View File
@@ -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],
}
@@ -1,44 +1,14 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "novatek", detector_func: is_novatek_file, extractor_func: extract_novatek }
}
use std::path::Path; use std::path::Path;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Write}; use std::io::Write;
use binrw::BinReaderExt;
use binrw::{BinRead, BinReaderExt};
use crate::utils::common; use crate::utils::common;
use include::*;
#[derive(BinRead)]
struct Header {
#[br(count = 4)] _magic_bytes: Vec<u8>,
version_major: u32,
version_minor: u32,
_unused: u32,
#[br(count = 16)] firmware_name_bytes: Vec<u8>,
data_size: u32,
#[br(count = 16)] _md5_checksum: Vec<u8>, //data checksum
part_count: u32,
_data_start_offset: u32,
#[br(count = 128)] _signature: Vec<u8>,
_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<u8>,
}
pub fn is_novatek_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_novatek_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+38
View File
@@ -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)
}
}
@@ -1,53 +1,16 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "nvt_timg", detector_func: is_nvt_timg_file, extractor_func: extract_nvt_timg }
}
use std::path::{Path}; use std::path::Path;
use std::io::{Seek, Write}; use std::io::{Seek, Write};
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use crate::utils::compression::{decompress_gzip}; use crate::utils::compression::{decompress_gzip};
use crate::utils::sparse::{unsparse_to_file}; use crate::utils::sparse::{unsparse_to_file};
use include::*;
#[derive(Debug, BinRead)]
struct TIMG {
#[br(count = 4)] _magic_bytes: Vec<u8>, //TIMG
_unused1: u32,
data_size: u32,
_unused2: u32,
#[br(count = 16)] _md5_checksum: Vec<u8>,
#[br(count = 256)] _signature: Vec<u8>,
}
#[derive(Debug, BinRead)]
struct PIMG {
#[br(count = 4)] magic_bytes: Vec<u8>, //PIMG
_unused1: u32,
size: u32,
_unused2: u32,
#[br(count = 16)] _md5_checksum: Vec<u8>,
#[br(count = 16)] name_bytes: Vec<u8>,
#[br(count = 64)] dest_dev_bytes: Vec<u8>,
#[br(count = 16)] comp_type_bytes: Vec<u8>,
_unknown1: u32,
#[br(count = 1024)] _comment: Vec<u8>,
_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)
}
}
pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; 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<dyn Any>) -> Result<(),
while file.stream_position()? < file_size as u64 { while file.stream_position()? < file_size as u64 {
pimg_i += 1; pimg_i += 1;
let pimg: PIMG = file.read_le()?; let pimg: PIMG = file.read_le()?;
if pimg.magic_bytes != b"PIMG" { if &pimg.magic_bytes != b"PIMG" {
println!("Invalid PIMG magic!"); println!("Invalid PIMG magic!");
return Ok(()); return Ok(());
} }
+132
View File
@@ -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<Option<[u8; 8]>, Box<dyn std::error::Error>> {
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<Option<([u8; 16], [u8; 16], [u8; 8])>, Box<dyn std::error::Error>> {
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<u8>,
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"
}
}
}
@@ -1,20 +1,21 @@
mod include;
mod pana_dvd_crypto;
mod lzss;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "pana_dvd", detector_func: is_pana_dvd_file, extractor_func: extract_pana_dvd }
}
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Write, Read, Cursor, Seek, SeekFrom}; use std::io::{Write, Read, Cursor, Seek, SeekFrom};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::keys; use crate::keys;
use crate::utils::common; use crate::utils::common;
use crate::utils::pana_dvd_crypto::{decrypt_data};
use crate::utils::aes::{decrypt_aes128_cbc_nopad}; use crate::utils::aes::{decrypt_aes128_cbc_nopad};
use crate::utils::compression::{decompress_gzip}; 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 { pub struct PanaDvdContext {
matching_key: [u8; 8], matching_key: [u8; 8],
@@ -24,133 +25,6 @@ pub struct PanaDvdContext {
aes_iv: Option<[u8; 16]> 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<u8>,
#[br(count = 4)] version_bytes: Vec<u8>,
_unk: u32,
offset: u32,
#[br(count = 8)] platform_bytes: Vec<u8>,
_unk1: u16,
#[br(count = 6)] id_bytes: Vec<u8>,
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<u8>, //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<u8>,
}
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<Option<[u8; 8]>, Box<dyn std::error::Error>> {
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<Option<([u8; 16], [u8; 16], [u8; 8])>, Box<dyn std::error::Error>> {
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<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_pana_dvd_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
let header = common::read_file(&file, 0, 64)?; let header = common::read_file(&file, 0, 64)?;
+67
View File
@@ -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<Aes256>;
pub fn decrypt_aes256_ecb(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
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)
}
}
@@ -1,52 +1,17 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "pfl_upg", detector_func: is_pfl_upg_file, extractor_func: extract_pfl_upg }
}
use rsa::{RsaPublicKey, BigUint}; use rsa::{RsaPublicKey, BigUint};
use hex::decode; use hex::decode;
use std::path::Path; use std::path::Path;
use std::io::{Read, Cursor, Write}; use std::io::{Read, Cursor, Write};
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use binrw::BinReaderExt;
use aes::Aes256;
use ecb::{Decryptor, cipher::{BlockDecryptMut, KeyInit, generic_array::GenericArray}};
use binrw::{BinRead, BinReaderExt};
use crate::utils::common; use crate::utils::common;
use crate::keys; use crate::keys;
use include::*;
#[derive(BinRead)]
struct Header {
#[br(count = 8)] _magic_bytes: Vec<u8>,
header_size: u32,
data_size: u32,
#[br(count = 4)] _crc32: Vec<u8>,
mask: u32,
_data_size_decompressed: u32,
_padding2: u32,
#[br(count = 512)] description_bytes: Vec<u8>,
}
impl Header {
fn description(&self) -> String {
common::string_from_bytes(&self.description_bytes)
}
}
#[derive(BinRead)]
struct FileHeader {
#[br(count = 60)] file_name_bytes: Vec<u8>,
real_size: u32,
stored_size: u32,
header_size: u32,
#[br(count = 4)] attributes: Vec<u8>,
}
impl FileHeader {
fn file_name(&self) -> String {
common::string_from_bytes(&self.file_name_bytes)
}
}
pub fn is_pfl_upg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_pfl_upg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; 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<Option<Box<dyn Any>>, 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<Aes256>;
fn decrypt_aes256_ecb(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
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<dyn Any>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file().ok_or("Extractor expected file")?; let mut file = app_ctx.file().ok_or("Extractor expected file")?;
+45
View File
@@ -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,
}
+4 -49
View File
@@ -1,60 +1,15 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "pup", detector_func: is_pup_file, extractor_func: extract_pup }
}
use std::path::{Path}; use std::path::{Path};
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use std::io::{Write, Seek, SeekFrom}; use std::io::{Write, Seek, SeekFrom};
use crate::utils::common; use crate::utils::common;
use crate::utils::compression::{decompress_zlib}; use crate::utils::compression::{decompress_zlib};
use include::*;
#[derive(BinRead)]
struct Header {
#[br(count = 4)] _magic_bytes: Vec<u8>,
_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,
}
pub fn is_pup_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_pup_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+49
View File
@@ -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"
}
}
}
@@ -1,65 +1,16 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "roku", detector_func: is_roku_file, extractor_func: extract_roku }
}
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::path::Path; use std::path::Path;
use std::io::{Write, Seek, Read, Cursor}; use std::io::{Write, Seek, Read, Cursor};
use tar::Archive; use tar::Archive;
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use crate::utils::aes::{decrypt_aes128_cbc_nopad, decrypt_aes128_cbc_pcks7}; use crate::utils::aes::{decrypt_aes128_cbc_nopad, decrypt_aes128_cbc_pcks7};
use include::*;
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<u8>,
#[br(count = 8)] _magic_bytes: Vec<u8>, //imgARMcC
#[br(count = 4)] _target_bytes: Vec<u8>,
#[br(count = 4)] _platform_id: Vec<u8>,
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<u8>,
#[br(count = 4)] _unk: Vec<u8>,
#[br(count = 192)] _rest_of_header: Vec<u8>,
}
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"
}
}
}
pub fn is_roku_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_roku_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+64
View File
@@ -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"
}
}
}
+4 -67
View File
@@ -1,79 +1,16 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "ruf", detector_func: is_ruf_file, extractor_func: extract_ruf }
}
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::fs::{self, File, OpenOptions}; use std::fs::{self, File, OpenOptions};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use std::io::{Write, Seek, SeekFrom, Cursor}; use std::io::{Write, Seek, SeekFrom, Cursor};
use crate::utils::common; use crate::utils::common;
use crate::keys; use crate::keys;
use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; use crate::utils::aes::{decrypt_aes128_cbc_pcks7};
use include::*;
#[derive(BinRead)]
struct RufHeader {
#[br(count = 6)] _magic_bytes: Vec<u8>,
#[br(count = 2)] _upgrade_type_bytes: Vec<u8>,
_unk1: u32,
#[br(count = 32)] date_time_bytes: Vec<u8>,
#[br(count = 8)] buyer_bytes: Vec<u8>,
#[br(count = 32)] model_bytes: Vec<u8>,
#[br(count = 32)] region_info_bytes: Vec<u8>,
#[br(count = 4)] version_bytes: Vec<u8>,
data_size: u32,
#[br(count = 20)] _unk2: Vec<u8>,
dual_ruf_flag: u32,
#[br(count = 44)] _unk3: Vec<u8>,
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<u8>,
payload_type_bytes: u32,
size: u32,
_unk1: u32,
#[br(count = 20)] _unk2: Vec<u8>,
}
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"
}
}
}
pub fn is_ruf_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_ruf_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+7
View File
@@ -0,0 +1,7 @@
pub fn decrypt_xor(data: &[u8]) -> Vec<u8> {
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()
}
+3 -13
View File
@@ -1,22 +1,13 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "rvp", detector_func: is_rvp_file, extractor_func: extract_rvp }
}
use std::path::Path; use std::path::Path;
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Write, Read, Cursor, Seek}; use std::io::{Write, Read, Cursor, Seek};
use crate::utils::common; use crate::utils::common;
use include::*;
fn decrypt_xor(data: &[u8]) -> Vec<u8> {
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()
}
pub fn is_rvp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_rvp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let mut file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let 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<dyn Any>) -> Result<(), Box<d
} else { } else {
println!("Unsupported header size!"); println!("Unsupported header size!");
break break
} }
println!("Size: {}", size); println!("Size: {}", size);
+7
View File
@@ -0,0 +1,7 @@
pub fn decrypt_xor(data: &[u8], key: &str) -> Vec<u8> {
let key_bytes = key.as_bytes();
data.iter()
.enumerate()
.map(|(i, &byte)| byte ^ key_bytes[i % key_bytes.len()])
.collect()
}
@@ -1,8 +1,6 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "samsung_old", detector_func: is_samsung_old_dir, extractor_func: extract_samsung_old }
}
use std::fs; use std::fs;
use std::path::{Path}; use std::path::{Path};
@@ -10,12 +8,12 @@ use std::fs::{File, OpenOptions};
use std::io::{Write}; use std::io::{Write};
use hex::decode; use hex::decode;
use sha1::{Digest, Sha1}; use sha1::{Digest, Sha1};
use md5;
use crate::utils::common; use crate::utils::common;
use crate::keys; use crate::keys;
use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; use crate::utils::aes::{decrypt_aes128_cbc_pcks7};
use include::decrypt_xor;
use md5;
pub fn is_samsung_old_dir(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_samsung_old_dir(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let dir = match app_ctx.dir() {Some(d) => d, None => return Ok(None)}; 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<Option<Box<dyn Any>>,
} }
} }
fn decrypt_xor(data: &[u8], key: &str) -> Vec<u8> {
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<dyn Any>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
let path = app_ctx.dir().ok_or("Extractor expected directory")?; let path = app_ctx.dir().ok_or("Extractor expected directory")?;
+212
View File
@@ -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<u8> {
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,
];
@@ -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 std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "sddl_sec", detector_func: is_sddl_sec_file, extractor_func: extract_sddl_sec }
}
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::fs::{self, File, OpenOptions}; use std::fs::{self, File, OpenOptions};
use std::io::{Cursor, Seek, SeekFrom, Write}; use std::io::{Cursor, Seek, SeekFrom, Write};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use crate::utils::aes::{decrypt_aes128_cbc_pcks7}; use crate::utils::aes::{decrypt_aes128_cbc_pcks7};
use crate::utils::compression::{decompress_zlib}; use crate::utils::compression::{decompress_zlib};
use include::*;
pub fn decipher(s: &[u8]) -> Vec<u8> {
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,
];
pub fn is_sddl_sec_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_sddl_sec_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+43
View File
@@ -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],
}
+4 -46
View File
@@ -1,56 +1,14 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::AppContext;
pub fn format() -> Format {
Format { name: "slp", detector_func: is_slp_file, extractor_func: extract_slp }
}
use std::path::{Path}; use std::path::{Path};
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Write, Seek, SeekFrom}; use std::io::{Write, Seek, SeekFrom};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use include::*;
#[derive(BinRead)]
struct Header {
#[br(count = 4)] _magic_bytes: Vec<u8>,
#[br(count = 8)] version_bytes: Vec<u8>,
#[br(count = 16)] model_bytes: Vec<u8>,
#[br(count = 16)] firmware_bytes: Vec<u8>,
_unk: u32,
#[br(count = 8)] check: Vec<u8>,
#[br(count = 8)] _unk2: Vec<u8>,
}
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<u8>,
}
pub fn is_slp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_slp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+52
View File
@@ -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<u8> {
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,
}
@@ -1,67 +1,16 @@
mod include;
use std::any::Any; use std::any::Any;
use crate::{InputTarget, AppContext, formats::Format}; use crate::{InputTarget, AppContext};
pub fn format() -> Format {
Format { name: "sony_bdp", detector_func: is_sony_bdp_file, extractor_func: extract_sony_bdp }
}
use std::fs::File; use std::fs::File;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::fs::{self, OpenOptions}; use std::fs::{self, OpenOptions};
use std::io::{Cursor, Write}; use std::io::{Cursor, Write};
use binrw::{BinRead, BinReaderExt}; use binrw::BinReaderExt;
use crate::utils::common; use crate::utils::common;
use crate::formats; use crate::formats;
use include::*;
//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<u8> {
data.iter().map(|&b| HEX_SUBSTITUTION[b as usize]).collect()
}
#[derive(BinRead)]
struct Header {
#[br(count = 8)] firmware_name_bytes: Vec<u8>,
#[br(count = 8)] _unk_version_bytes: Vec<u8>,
#[br(count = 16)] firmware_version_bytes: Vec<u8>,
#[br(count = 16)] date_bytes: Vec<u8>,
#[br(count = 8)] _unk2_version_bytes: Vec<u8>,
#[br(count = 8)] _unk3_version_bytes: Vec<u8>,
#[br(count = 16)] _unk4_version_bytes: Vec<u8>,
_unk: u32,
_checksum: u64,
file_size: u32,
#[br(count = 16)] _unk5_version_bytes: Vec<u8>,
#[br(count = 16)] _unk6_version_bytes: Vec<u8>,
#[br(count = 32)] _unk2: Vec<u8>,
}
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,
}
pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
+1 -10
View File
@@ -1,14 +1,5 @@
pub mod common; pub mod common;
pub mod aes; pub mod aes;
pub mod pana_dvd_crypto;
pub mod lzss;
pub mod lzop; pub mod lzop;
pub mod sparse; pub mod sparse;
pub mod compression; 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;