From a3fbb9e76ff6805d7887cc334a69aec530929ed7 Mon Sep 17 00:00:00 2001 From: theubusu <80545678+theubusu@users.noreply.github.com> Date: Sun, 12 Apr 2026 16:00:59 +0200 Subject: [PATCH] improvements to roku and fix output folder issue in sony_bdp --- src/formats/roku/include.rs | 118 +++++++++++++++++++++++++----------- src/formats/roku/mod.rs | 24 +++++--- src/formats/sony_bdp/mod.rs | 11 ++-- 3 files changed, 104 insertions(+), 49 deletions(-) diff --git a/src/formats/roku/include.rs b/src/formats/roku/include.rs index b2d0bc2..5dd1326 100644 --- a/src/formats/roku/include.rs +++ b/src/formats/roku/include.rs @@ -7,43 +7,93 @@ pub static FILE_KEY: [u8; 16] = [ 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], +// u-boot-2011.06/include/aimage.h +// (C) Copyright 2004-2008 Andre McCurdy, NXP Semiconductors + +#[derive(Debug, BinRead)] +pub struct AImageHeader { + _vector: u32, // reserved for a branch instruction (if data_start_offset and data_entry_point_offset are 0) + _vector2: u32, // reserved for a branch instruction branch delay slot + _magic: u32, // 0x41676d69 + _magic2: u32, // 0x43634d52 + _release_id: u32, // toplevel build / release version + _platform_id: u32, // target platform information + pub image_type: u32, // image type + pub lenght: u32, // length of entire image (header + data + trailing padding) + _data_lenght: u32, // length of image data (ie from data_start_offset to start of trailing padding) + pub data_start_offset: u32, // 0 if header is part of data, sizeof(aimage_v1_header_t) if header is prepended to data + _data_link_address: u32, // for non-pic executable images, where should the image placed in memory in order to execute + _data_entry_point_offset: u32, // 0 if execution entry point is at the beginning of image data + pub flags: u32, // image flags + _build_time: u32, // if non-zero, gives time when image was created (seconds since 1970) + _build_host_offset: u32, // if non-zero, offset of a string in the image data giving build host information + _signature: [u8; 128], // 32 32bit words == 1024 bit RSA signature + _hash: [u8; 20], // raw sha1 hash (fallback if not checking signature) + _usd: u32, // "un-signed data" (treated as 0 during hash and signature verification, regardless of actual value) + _reserved: [u8; 28], // image type specific params etc (number tweaked to keep header size == 256 bytes) + /* + Note: The final 16 bytes of the header are used to form the + IV for aimage images which are encrypted with AES (ie if + IMG_FLAG_ENC RYPTED_V1 flag is set). + From a security standpoint, the IV does _not_ have to be + secret in any way, but it _should_ be unique for every + image which is encrypted with a given AES key. + */ + _iv: [u8; 16], // space reserved for random data or timestamp to ensure IV is unique for every image which is signed } -impl ImageHeader { - pub fn is_encrypted(&self) -> bool { - self.flags == 0x80 +impl AImageHeader { + //pub fn release_id_str(&self) -> String { + // let release_id_major = ((self.release_id) >> 21) & 0x3FF; + // let release_id_minor = ((self.release_id) >> 15) & 0x3F; + // let release_id_build = ((self.release_id) >> 1) & 0x3FFF; + // return format!("{}.{}-{}", release_id_major, release_id_minor, release_id_build) + //} + + pub fn image_type_str(&self) -> &str { + match self.image_type { + 0x00 => "invalid", + 0x01 => "debug_message", + 0x02 => "bloader", + 0x03 => "slaveloader", // [O] + 0x04 => "env", + 0x05 => "rbf", + 0x06 => "bootdata", // [O] ROKUBOOT_TYPE_PKG + 0x07 => "cexapp", + 0x08 => "slaveapp", + 0x09 => "zimage", + 0x0A => "initfs_cramfs", // [O] cramfs image + 0x0B => "initfs_zext2", + 0x0C => "appfs_ext2", + 0x0D => "appfs_cramfs", // [O] custom_pkg image + 0x0E => "firmware_blob", // [O] ROKUBOOT_TYPE_UBOOT //main u-boot + 0x0F => "bootcount", + 0x10 => "bootselection", + 0x11 => "gnfs_ext2", + 0x12 => "eth0mac", + 0x13 => "bloader_testmode", + 0x14 => "bloader_dfu", + 0x15 => "splashscreen", + 0x16 => "custom_pkg_token", // [O] + 0x17 => "initrd", + 0x18 => "uimage", // [O] ROKUBOOT_TYPE_KERNEL // linux + 0x19 => "uboot_mipsel", + 0x1A => "uboot_mipseb", + 0x1B => "initfs_squashfs", + 0x101 => "cramfs_auth", // [O] merkle tree for cramfs + _ => "UNKNOWN", + } } - 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" + + pub fn encmode_str(&self) -> &str { + let encmode_flags = self.flags & (7 << 6); + if (encmode_flags & (1 << 6)) != 0 { // image data is encrypted before signing: CBC mode, entire image in one pass + return "cbc_onepass" + } else if (encmode_flags & (2 << 6)) != 0 { // image data is encrypted before signing: CBC mode, 4k byte blocks, block group iv = (iv + (first block's block offset)) + return "cbc_4kblocks" + } else if (encmode_flags & (3 << 6)) != 0 { // image data is encrypted before signing: CTR mode, block iv = (iv + (block offset)) + return "ctr" } else { - return "unknown" + return "none/unknown" } } } \ No newline at end of file diff --git a/src/formats/roku/mod.rs b/src/formats/roku/mod.rs index 5459fc0..838e16c 100644 --- a/src/formats/roku/mod.rs +++ b/src/formats/roku/mod.rs @@ -4,7 +4,7 @@ use crate::AppContext; use std::fs::{self, OpenOptions}; use std::path::Path; -use std::io::{Write, Seek, Read, Cursor}; +use std::io::{Cursor, Read, Seek, SeekFrom, Write}; use tar::Archive; use binrw::BinReaderExt; @@ -38,6 +38,7 @@ pub fn extract_roku(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< for entry_result in tar_archive.entries_with_seek()? { let mut entry = entry_result?; + let path = entry.path()?.to_path_buf(); if path == std::path::Path::new("manifest") { @@ -58,26 +59,29 @@ pub fn extract_roku(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box< let mut i = 1; while image_reader.stream_position()? < size as u64 { - let image: ImageHeader = image_reader.read_le()?; - println!("\nImage {} - Type: {:x}({}), Size: {}, Flags: {:x}{}, Data offset: {}", - i ,image.image_type, image.type_string(), image.size1, image.flags, if image.is_encrypted(){"(Encrypted)"}else{" "}, image.data_start_offset); + let image: AImageHeader = image_reader.read_le()?; + println!(" #{} - Type: {}(0x{:x}), Lenght: {}, encmode: {}", + i, image.image_type_str(), image.image_type, image.lenght, image.encmode_str()); + //println!("{:?}", image); let data = - if image.data_start_offset == 0 { - common::read_exact(&mut image_reader, image.size1 as usize - 256)? + if image.data_start_offset == 0 { // "0 if header is part of data" + image_reader.seek(SeekFrom::Current(-256))?; //rewind header + common::read_exact(&mut image_reader, image.lenght as usize)? + } else { - let _extra_data = common::read_exact(&mut image_reader, image.data_start_offset as usize - 256)?; - common::read_exact(&mut image_reader, image.size1 as usize - image.data_start_offset as usize)? + let _skip_data = common::read_exact(&mut image_reader, image.data_start_offset as usize - 256)?; + common::read_exact(&mut image_reader, image.lenght as usize - image.data_start_offset as usize)? }; let folder_path = Path::new(&app_ctx.output_dir).join(&path); - let output_path = Path::new(&folder_path).join(format!("{}_{}.bin", i, image.type_string())); + let output_path = Path::new(&folder_path).join(format!("{}_{}.bin", i, image.image_type_str())); fs::create_dir_all(&folder_path)?; let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; out_file.write_all(&data)?; - println!("- Saved file!"); + println!(" - Saved file!\n"); i += 1; } diff --git a/src/formats/sony_bdp/mod.rs b/src/formats/sony_bdp/mod.rs index 646c206..51524cb 100644 --- a/src/formats/sony_bdp/mod.rs +++ b/src/formats/sony_bdp/mod.rs @@ -36,9 +36,10 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Box) -> Result<(), let mut last_file_path: Option = None; let mut first_entry_offset = 0; - let mut i = 1; + let mut i = 0; loop { - if (i != 1) && (hdr_reader.position() >= first_entry_offset) { + + if (i != 0) && (hdr_reader.position() >= first_entry_offset) { break } @@ -48,14 +49,14 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Box) -> Result<(), } println!("\n#{} - Offset: {}, Size: {}", i, entry.offset, entry.size); - if i == 1 { + if i == 0 { first_entry_offset = entry.offset as u64; } let obf_data = common::read_file(&file, entry.offset as u64, entry.size as usize)?; let data = hex_substitute(&obf_data); - let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", i)); + let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", i+1)); last_file_path = Some(output_path.clone()); fs::create_dir_all(&app_ctx.output_dir)?; @@ -71,7 +72,7 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Box) -> Result<(), println!("\nChecking if it's also MTK BDP..."); let last_file = File::open(last_file_path.unwrap())?; - let mtk_extraction_path = app_ctx.output_dir.join(format!("{}", i + 1)); + let mtk_extraction_path = app_ctx.output_dir.join(format!("{}", i)); //this is getting stupid... let ctx: AppContext = AppContext { input: InputTarget::File(last_file), output_dir: mtk_extraction_path, options: app_ctx.options.clone() };