Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for DirectX shader bytecode files #751

Merged
merged 1 commit into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}