From 5781db7815adcd31075226e8a0cb3b71e400bebe Mon Sep 17 00:00:00 2001 From: cohaereo Date: Wed, 20 Nov 2024 11:25:10 +0100 Subject: [PATCH] Added support for DirectX shader bytecode files --- src/extractors.rs | 1 + src/extractors/dxbc.rs | 59 ++++++++++++++++++++++++++++++++++++++++++ src/magic.rs | 11 ++++++++ src/signatures.rs | 1 + src/signatures/dxbc.rs | 45 ++++++++++++++++++++++++++++++++ src/structures.rs | 1 + src/structures/dxbc.rs | 56 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 174 insertions(+) create mode 100644 src/extractors/dxbc.rs create mode 100644 src/signatures/dxbc.rs create mode 100644 src/structures/dxbc.rs diff --git a/src/extractors.rs b/src/extractors.rs index 467bf4f51..d86d04ae0 100644 --- a/src/extractors.rs +++ b/src/extractors.rs @@ -151,6 +151,7 @@ pub mod dahua_zip; pub mod dmg; pub mod dtb; pub mod dumpifs; +pub mod dxbc; pub mod gif; pub mod gzip; pub mod inflate; diff --git a/src/extractors/dxbc.rs b/src/extractors/dxbc.rs new file mode 100644 index 000000000..5bcf82e43 --- /dev/null +++ b/src/extractors/dxbc.rs @@ -0,0 +1,59 @@ +use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType}; +use crate::structures::dxbc::parse_dxbc_header; + +/// Defines the internal extractor function for carving out DXBC images +/// +/// ``` +/// use std::io::ErrorKind; +/// use std::process::Command; +/// use binwalk::extractors::common::ExtractorType; +/// use binwalk::extractors::dxbc::dxbc_extractor; +/// +/// match dxbc_extractor().utility { +/// ExtractorType::None => panic!("Invalid extractor type of None"), +/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func), +/// ExtractorType::External(cmd) => { +/// if let Err(e) = Command::new(&cmd).output() { +/// if e.kind() == ErrorKind::NotFound { +/// panic!("External extractor '{}' not found", cmd); +/// } else { +/// panic!("Failed to execute external extractor '{}': {}", cmd, e); +/// } +/// } +/// } +/// } +/// ``` +pub fn dxbc_extractor() -> Extractor { + Extractor { + do_not_recurse: true, + utility: ExtractorType::Internal(extract_dxbc_file), + ..Default::default() + } +} + +pub fn extract_dxbc_file( + file_data: &[u8], + offset: usize, + output_directory: Option<&String>, +) -> ExtractionResult { + const OUTFILE_NAME: &str = "shader.dxbc"; + + let mut result = ExtractionResult { + ..Default::default() + }; + + if let Ok(header) = parse_dxbc_header(&file_data[offset..]) { + // Report success + result.size = Some(header.size); + result.success = true; + + // Do extraction, if requested + if output_directory.is_some() { + let chroot = Chroot::new(output_directory); + result.success = + chroot.carve_file(OUTFILE_NAME, file_data, offset, result.size.unwrap()); + } + } + + result +} diff --git a/src/magic.rs b/src/magic.rs index c1f5bda8d..9c2716d77 100644 --- a/src/magic.rs +++ b/src/magic.rs @@ -976,6 +976,17 @@ pub fn patterns() -> Vec { description: signatures::csman::DESCRIPTION.to_string(), extractor: Some(extractors::csman::csman_extractor()), }, + // DirectX ByteCode + signatures::common::Signature { + name: "dxbc".to_string(), + short: false, + magic_offset: 0, + always_display: false, + magic: signatures::dxbc::dxbc_magic(), + parser: signatures::dxbc::dxbc_parser, + description: signatures::dxbc::DESCRIPTION.to_string(), + extractor: Some(extractors::dxbc::dxbc_extractor()), + }, ]; binary_signatures diff --git a/src/signatures.rs b/src/signatures.rs index 79955ad10..a07cdcc4a 100644 --- a/src/signatures.rs +++ b/src/signatures.rs @@ -127,6 +127,7 @@ pub mod deb; pub mod dlob; pub mod dmg; pub mod dtb; +pub mod dxbc; pub mod ecos; pub mod efigpt; pub mod elf; diff --git a/src/signatures/dxbc.rs b/src/signatures/dxbc.rs new file mode 100644 index 000000000..dc3769bb7 --- /dev/null +++ b/src/signatures/dxbc.rs @@ -0,0 +1,45 @@ +use crate::signatures::common::{ + SignatureError, SignatureResult, CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, +}; +use crate::structures::dxbc::parse_dxbc_header; + +/// Human readable description +pub const DESCRIPTION: &str = "DirectX shader bytecode"; + +/// DXBC file magic bytes +pub fn dxbc_magic() -> Vec> { + vec![b"DXBC".to_vec()] +} + +/// Validates the DXBC header +pub fn dxbc_parser(file_data: &[u8], offset: usize) -> Result { + const CHUNK_SM4: [u8; 4] = *b"SHDR"; + const CHUNK_SM5: [u8; 4] = *b"SHEX"; + + // Successful return value + let mut result = SignatureResult { + offset, + description: DESCRIPTION.to_string(), + confidence: CONFIDENCE_MEDIUM, + ..Default::default() + }; + + if let Ok(header) = parse_dxbc_header(&file_data[offset..]) { + result.confidence = CONFIDENCE_HIGH; + result.size = header.size; + + let shader_model = if header.chunk_ids.contains(&CHUNK_SM4) { + "Shader Model 4" + } else if header.chunk_ids.contains(&CHUNK_SM5) { + "Shader Model 5" + } else { + "Unknown Shader Model" + }; + + result.description = format!("{}, {}", result.description, shader_model); + + return Ok(result); + } + + Err(SignatureError) +} diff --git a/src/structures.rs b/src/structures.rs index fbf494184..820615382 100644 --- a/src/structures.rs +++ b/src/structures.rs @@ -111,6 +111,7 @@ pub mod deb; pub mod dlob; pub mod dmg; pub mod dtb; +pub mod dxbc; pub mod efigpt; pub mod elf; pub mod ext; diff --git a/src/structures/dxbc.rs b/src/structures/dxbc.rs new file mode 100644 index 000000000..fa7886b25 --- /dev/null +++ b/src/structures/dxbc.rs @@ -0,0 +1,56 @@ +use crate::structures::common::{self, StructureError}; + +#[derive(Debug, Default, Clone)] +pub struct DXBCHeader { + pub size: usize, + pub chunk_ids: Vec<[u8; 4]>, +} + +// http://timjones.io/blog/archive/2015/09/02/parsing-direct3d-shader-bytecode +pub fn parse_dxbc_header(data: &[u8]) -> Result { + let dxbc_header_structure = vec![ + ("magic", "u32"), + ("signature_p1", "u64"), + ("signature_p2", "u64"), + ("one", "u32"), + ("total_size", "u32"), + ("chunk_count", "u32"), + ]; + + // Parse the header + if let Ok(header) = common::parse(data, &dxbc_header_structure, "little") { + if header["one"] != 1 { + return Err(StructureError); + } + + // Sanity check: There are at least 14 known chunks, but most likely no more than 32. + // Prevents the for loop from spiraling into an OOM on the offchance that both the magic and "one" check pass on garbage data + if header["chunk_count"] > 32 { + return Err(StructureError); + } + + let header_end = common::size(&dxbc_header_structure); + + let mut chunk_ids = vec![]; + for i in 0..header["chunk_count"] { + let offset_data = data + .get((header_end + i * 4)..(header_end + i * 4) + 4) + .ok_or(StructureError)?; + let offset = u32::from_le_bytes(offset_data.try_into().unwrap()) as usize; + + chunk_ids.push( + data.get(offset..offset + 4) + .ok_or(StructureError)? + .try_into() + .unwrap(), + ); + } + + return Ok(DXBCHeader { + size: header["total_size"], + chunk_ids, + }); + } + + Err(StructureError) +}