Skip to content

Commit

Permalink
Merge pull request #751 from cohaereo/dxbc-magic
Browse files Browse the repository at this point in the history
Added support for DirectX shader bytecode files
  • Loading branch information
devttys0 authored Nov 20, 2024
2 parents bad23fe + 5781db7 commit b7abdab
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/extractors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
59 changes: 59 additions & 0 deletions src/extractors/dxbc.rs
Original file line number Diff line number Diff line change
@@ -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
}
11 changes: 11 additions & 0 deletions src/magic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,17 @@ pub fn patterns() -> Vec<signatures::common::Signature> {
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
Expand Down
1 change: 1 addition & 0 deletions src/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
45 changes: 45 additions & 0 deletions src/signatures/dxbc.rs
Original file line number Diff line number Diff line change
@@ -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<u8>> {
vec![b"DXBC".to_vec()]
}

/// Validates the DXBC header
pub fn dxbc_parser(file_data: &[u8], offset: usize) -> Result<SignatureResult, SignatureError> {
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)
}
1 change: 1 addition & 0 deletions src/structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
56 changes: 56 additions & 0 deletions src/structures/dxbc.rs
Original file line number Diff line number Diff line change
@@ -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<DXBCHeader, StructureError> {
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)
}

0 comments on commit b7abdab

Please sign in to comment.