From 82f1b8f6320bdabce8de9266664bb688ef13f978 Mon Sep 17 00:00:00 2001 From: devttys0 Date: Tue, 29 Oct 2024 11:02:53 -0400 Subject: [PATCH 1/2] BTRFS identification working --- Cargo.lock | 10 ++++++ Cargo.toml | 1 + src/magic.rs | 11 +++++++ src/signatures.rs | 1 + src/signatures/btrfs.rs | 39 +++++++++++++++++++++++ src/structures.rs | 1 + src/structures/btrfs.rs | 68 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 131 insertions(+) create mode 100644 src/signatures/btrfs.rs create mode 100644 src/structures/btrfs.rs diff --git a/Cargo.lock b/Cargo.lock index 4e1b98eeb..5c2bbb455 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,6 +110,7 @@ dependencies = [ "clap", "colored", "crc32-v2", + "crc32c", "entropy", "env_logger", "flate2", @@ -325,6 +326,15 @@ version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f546fcecc3490696c3bea070d8949208279bbc220a5a7738573a10f584cda51" +[[package]] +name = "crc32c" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +dependencies = [ + "rustc_version", +] + [[package]] name = "crc32fast" version = "1.4.2" diff --git a/Cargo.toml b/Cargo.toml index 091cebf9d..4a7bf7576 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ entropy = "0.4.2" colored = "2.1.0" termsize = "0.1" crc32-v2 = "0.0.4" +crc32c = "0.6.8" plotters = "0.3.6" xz2 = "0.1.7" bzip2 = "0.4.4" diff --git a/src/magic.rs b/src/magic.rs index 7a6b529f8..aa0a7b783 100644 --- a/src/magic.rs +++ b/src/magic.rs @@ -921,6 +921,17 @@ pub fn patterns() -> Vec { description: signatures::apfs::DESCRIPTION.to_string(), extractor: Some(extractors::apfs::apfs_extractor()), }, + // BTRFS + signatures::common::Signature { + name: "btrfs".to_string(), + short: false, + magic_offset: 0, + always_display: false, + magic: signatures::btrfs::btrfs_magic(), + parser: signatures::btrfs::btrfs_parser, + description: signatures::btrfs::DESCRIPTION.to_string(), + extractor: None, + }, ]; binary_signatures diff --git a/src/signatures.rs b/src/signatures.rs index f6395cc34..6f71e2810 100644 --- a/src/signatures.rs +++ b/src/signatures.rs @@ -111,6 +111,7 @@ pub mod apfs; pub mod arcadyan; pub mod autel; pub mod binhdr; +pub mod btrfs; pub mod bzip2; pub mod cab; pub mod cfe; diff --git a/src/signatures/btrfs.rs b/src/signatures/btrfs.rs new file mode 100644 index 000000000..13e1d0e91 --- /dev/null +++ b/src/signatures/btrfs.rs @@ -0,0 +1,39 @@ +use crate::signatures::common::{SignatureError, SignatureResult, CONFIDENCE_MEDIUM}; +use crate::structures::btrfs::parse_btrfs_header; + +/// Human readable description +pub const DESCRIPTION: &str = "BTRFS file system"; + +/// BTRFS magic bytes +pub fn btrfs_magic() -> Vec> { + vec![b"_BHRfS_M".to_vec()] +} + +/// Validates the BTRFS header +pub fn btrfs_parser(file_data: &[u8], offset: usize) -> Result { + const MAGIC_OFFSET: usize = 0x10040; + + // Successful return value + let mut result = SignatureResult { + description: DESCRIPTION.to_string(), + confidence: CONFIDENCE_MEDIUM, + ..Default::default() + }; + + // Sanity check the reported offset + if offset >= MAGIC_OFFSET { + result.offset = offset - MAGIC_OFFSET; + + // Parse the superblock header; this also validates the superblock CRC + if let Ok(btrfs_header) = parse_btrfs_header(&file_data[result.offset..]) { + result.size = btrfs_header.total_size; + result.description = format!( + "{}, node size: {}, sector size: {}, leaf size: {}, stripe size: {}, bytes used: {}, total size: {} bytes", + result.description, btrfs_header.node_size, btrfs_header.sector_size, btrfs_header.leaf_size, btrfs_header.stripe_size, btrfs_header.bytes_used, result.size + ); + return Ok(result); + } + } + + Err(SignatureError) +} diff --git a/src/structures.rs b/src/structures.rs index f5e2ab764..fd11b48b9 100644 --- a/src/structures.rs +++ b/src/structures.rs @@ -100,6 +100,7 @@ pub mod androidsparse; pub mod apfs; pub mod autel; pub mod binhdr; +pub mod btrfs; pub mod cab; pub mod chk; pub mod common; diff --git a/src/structures/btrfs.rs b/src/structures/btrfs.rs new file mode 100644 index 000000000..fdfefc941 --- /dev/null +++ b/src/structures/btrfs.rs @@ -0,0 +1,68 @@ +use crate::structures::common::{self, StructureError}; +use crc32c::crc32c; + +/// Struct to store BTRFS header info +#[derive(Debug, Default, Clone)] +pub struct BTRFSHeader { + pub bytes_used: usize, + pub total_size: usize, + pub leaf_size: usize, + pub node_size: usize, + pub stripe_size: usize, + pub sector_size: usize, +} + +/// Parses a BTRFS header +pub fn parse_btrfs_header(btrfs_data: &[u8]) -> Result { + const SUPERBLOCK_OFFSET: usize = 0x10000; + const SUPERBLOCK_END: usize = SUPERBLOCK_OFFSET + 0x1000; + const CRC_START: usize = 0x20; + + // Partial BTRFS superblock structure for obtaining image size and CRC validation + // https://archive.kernel.org/oldwiki/btrfs.wiki.kernel.org/index.php/On-disk_Format.html#Superblock + let btrfs_structure = vec![ + ("header_checksum", "u32"), + ("unused1", "u32"), + ("unused2", "u64"), + ("unused3", "u64"), + ("unused4", "u64"), + ("uuid_p1", "u64"), + ("uuid_p2", "u64"), + ("block_phys_addr", "u64"), + ("flags", "u64"), + ("magic", "u64"), + ("generation", "u64"), + ("root_tree_address", "u64"), + ("chunk_tree_address", "u64"), + ("log_tree_address", "u64"), + ("log_root_transid", "u64"), + ("total_bytes", "u64"), + ("bytes_used", "u64"), + ("root_dir_objid", "u64"), + ("num_devices", "u64"), + ("sector_size", "u32"), + ("node_size", "u32"), + ("leaf_size", "u32"), + ("stripe_size", "u32"), + ]; + + // Parse the header + if let Some(btrfs_header_data) = btrfs_data.get(SUPERBLOCK_OFFSET..SUPERBLOCK_END) { + if let Ok(btrfs_header) = common::parse(btrfs_header_data, &btrfs_structure, "little") { + // Validate the superblock CRC + if btrfs_header["header_checksum"] == (crc32c(&btrfs_header_data[CRC_START..]) as usize) + { + return Ok(BTRFSHeader { + sector_size: btrfs_header["sector_size"], + node_size: btrfs_header["node_size"], + leaf_size: btrfs_header["leaf_size"], + stripe_size: btrfs_header["stripe_size"], + bytes_used: btrfs_header["bytes_used"], + total_size: btrfs_header["total_bytes"], + }); + } + } + } + + Err(StructureError) +} From 9391f8925d886144589c82314ef8d895a91e7689 Mon Sep 17 00:00:00 2001 From: devttys0 Date: Tue, 29 Oct 2024 13:14:48 -0400 Subject: [PATCH 2/2] BTRFS signature tested --- src/magic.rs | 2 +- src/signatures/btrfs.rs | 2 ++ src/structures/btrfs.rs | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/magic.rs b/src/magic.rs index aa0a7b783..38e18fe36 100644 --- a/src/magic.rs +++ b/src/magic.rs @@ -926,7 +926,7 @@ pub fn patterns() -> Vec { name: "btrfs".to_string(), short: false, magic_offset: 0, - always_display: false, + always_display: true, magic: signatures::btrfs::btrfs_magic(), parser: signatures::btrfs::btrfs_parser, description: signatures::btrfs::DESCRIPTION.to_string(), diff --git a/src/signatures/btrfs.rs b/src/signatures/btrfs.rs index 13e1d0e91..d96a3f9d8 100644 --- a/src/signatures/btrfs.rs +++ b/src/signatures/btrfs.rs @@ -11,6 +11,7 @@ pub fn btrfs_magic() -> Vec> { /// Validates the BTRFS header pub fn btrfs_parser(file_data: &[u8], offset: usize) -> Result { + // Offset of the superblock magic bytes in a BTRFS image const MAGIC_OFFSET: usize = 0x10040; // Successful return value @@ -22,6 +23,7 @@ pub fn btrfs_parser(file_data: &[u8], offset: usize) -> Result= MAGIC_OFFSET { + // Actual offset is the location of the magic bytes minus the magic byte offset result.offset = offset - MAGIC_OFFSET; // Parse the superblock header; this also validates the superblock CRC diff --git a/src/structures/btrfs.rs b/src/structures/btrfs.rs index fdfefc941..5eaf7da45 100644 --- a/src/structures/btrfs.rs +++ b/src/structures/btrfs.rs @@ -1,7 +1,7 @@ use crate::structures::common::{self, StructureError}; use crc32c::crc32c; -/// Struct to store BTRFS header info +/// Struct to store BTRFS super block info #[derive(Debug, Default, Clone)] pub struct BTRFSHeader { pub bytes_used: usize, @@ -12,7 +12,7 @@ pub struct BTRFSHeader { pub sector_size: usize, } -/// Parses a BTRFS header +/// Parse and validate a BTRFS super block pub fn parse_btrfs_header(btrfs_data: &[u8]) -> Result { const SUPERBLOCK_OFFSET: usize = 0x10000; const SUPERBLOCK_END: usize = SUPERBLOCK_OFFSET + 0x1000;