feat: exfat content extraction

This commit is contained in:
jujuforce
2025-07-16 15:12:32 +02:00
parent 252ea9c797
commit 53c389ba05
3 changed files with 293 additions and 3 deletions
Generated
+240 -1
View File
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "aes"
@@ -19,6 +19,21 @@ version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
dependencies = [
"bytemuck",
]
[[package]]
name = "block-padding"
version = "0.3.3"
@@ -28,6 +43,26 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bytemuck"
version = "1.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "cbc"
version = "0.1.2"
@@ -43,6 +78,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "checked_num"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deb5a345c406bf67f62bd14f284c9cb041ce5107725c22e665a163c871a4fb7b"
dependencies = [
"num-traits",
]
[[package]]
name = "cipher"
version = "0.4.4"
@@ -94,12 +138,130 @@ dependencies = [
"typenum",
]
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "derive_builder"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "derive_builder_macro"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
"syn",
]
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "endify"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "412c0c5048548a61a2ed94233a7305d2dfa4d1d3acc69039da77efa84e51df21"
dependencies = [
"endify-derive",
]
[[package]]
name = "endify-derive"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a204d4aa704d0a74ee3bebccc65f24bca0cdef2588f08eaafda467cd4d3c8a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "enumeric"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f2f9bd649bd8c884aa518a798861fd111699dfc7d7dcc193bc2b658c8b9188d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "exfat-fs"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cbdfa8ecf36ab2341032974d3644a56f1e40c9da3b643118492cf97f357f3c5"
dependencies = [
"bitflags",
"bytemuck",
"checked_num",
"derive_builder",
"endify",
"enumeric",
"thiserror",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fsdecrypt"
version = "0.1.1"
@@ -108,6 +270,7 @@ dependencies = [
"anyhow",
"cbc",
"crc32fast",
"exfat-fs",
"hex-literal",
"indicatif",
]
@@ -128,6 +291,12 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "indicatif"
version = "0.17.8"
@@ -172,6 +341,15 @@ version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "number_prefix"
version = "0.4.0"
@@ -184,12 +362,73 @@ version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-width"
version = "0.1.13"
+1
View File
@@ -10,3 +10,4 @@ cbc = "0.1.2"
crc32fast = "1.4.2"
hex-literal = "0.4.1"
indicatif = "0.17.8"
exfat-fs = "0.1.3"
+52 -2
View File
@@ -1,9 +1,11 @@
use std::{
fs::File,
fs::{File, create_dir_all},
io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write},
path::Path,
};
use exfat_fs::dir::{Root, entry::fs::FsElement};
use aes::{
cipher::{block_padding::NoPadding, BlockDecryptMut, InnerIvInit, KeyInit, KeyIvInit},
Aes128Dec,
@@ -21,6 +23,44 @@ mod crypto;
const PAGE_SIZE: u64 = 4096;
fn extract_exfat_contents(exfat_path: &Path) -> Result<()> {
println!("Extracting contents of {}", exfat_path.display());
let file = File::open(exfat_path)?;
let mut root = Root::open(file)?;
// Create output directory with same name as exfat file (without extension)
let output_dir = exfat_path.with_extension("");
create_dir_all(&output_dir)?;
extract_fs_elements(root.items(), &output_dir)?;
Ok(())
}
fn extract_fs_elements(elements: &mut [FsElement<File>], output_dir: &Path) -> Result<()> {
let item_count = elements.len();
for i in 0..item_count {
if let Some(FsElement::F(ref mut file_ref)) = elements.get_mut(i) {
// Extract file
let filename = file_ref.name().to_owned();
let file_path = output_dir.join(&filename);
let mut buffer = Vec::new();
file_ref.read_to_end(&mut buffer)?;
std::fs::write(file_path, buffer)?;
} else if let Some(FsElement::D(ref dir_ref)) = elements.get(i) {
// Handle directory
let dirname = dir_ref.name();
let dir_path = output_dir.join(dirname);
create_dir_all(&dir_path)?;
let mut children = dir_ref.open()?;
// Extract children recursively
extract_fs_elements(&mut children, &dir_path)?;
}
}
Ok(())
}
fn main() -> Result<()> {
let args = std::env::args().collect::<Vec<String>>();
@@ -160,7 +200,7 @@ fn main() -> Result<()> {
.template("{prefix} [{bar:20!.bright.yellow/dim.white}] {bytes:>8} [{elapsed}<{eta}, {bytes_per_sec}]")?
);
pb.set_prefix(output_filename);
pb.set_prefix(output_filename.clone());
reader.seek(SeekFrom::Start(data_offset))?;
for _ in 0..output_size / PAGE_SIZE {
@@ -184,6 +224,16 @@ fn main() -> Result<()> {
writer.flush()?;
pb.finish();
// Extract exfat contents if this is an exfat file
if bootid.container_type == ContainerType::OPTION && output_path.extension().unwrap_or_default() == "exfat" {
if let Err(e) = extract_exfat_contents(&output_path) {
println!("WARNING: Failed to extract exfat contents: {e}");
}
println!("Extracted exfat contents: {}", output_path.display());
println!("Deleting exfat file: {}", output_path.display());
std::fs::remove_file(output_path)?;
}
page.clear();
page_iv.fill(0);
bootid_bytes.fill(0);