diff --git a/src/extractors.rs b/src/extractors.rs index 3b0a297ab..02dc8e9b5 100644 --- a/src/extractors.rs +++ b/src/extractors.rs @@ -178,6 +178,7 @@ pub mod shrs; pub mod squashfs; pub mod srec; pub mod svg; +pub mod swapped; pub mod tarball; pub mod trx; pub mod tsk; diff --git a/src/extractors/swapped.rs b/src/extractors/swapped.rs new file mode 100644 index 000000000..1cb5102b1 --- /dev/null +++ b/src/extractors/swapped.rs @@ -0,0 +1,105 @@ +use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType}; + +/// Defines the internal extractor function for u16 swapped firmware images +/// +/// ``` +/// use std::io::ErrorKind; +/// use std::process::Command; +/// use binwalk::extractors::common::ExtractorType; +/// use binwalk::extractors::swapped::swapped_extractor_u16; +/// +/// match swapped_extractor_u16().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 swapped_extractor_u16() -> Extractor { + Extractor { + utility: ExtractorType::Internal(extract_swapped_u16), + ..Default::default() + } +} + +/// Extract firmware where every two bytes have been swapped +pub fn extract_swapped_u16( + file_data: &[u8], + offset: usize, + output_directory: Option<&String>, +) -> ExtractionResult { + const SWAP_BYTE_COUNT: usize = 2; + extract_swapped(file_data, offset, output_directory, SWAP_BYTE_COUNT) +} + +/// Extract a block of data where every n bytes have been swapped +fn extract_swapped( + file_data: &[u8], + offset: usize, + output_directory: Option<&String>, + n: usize, +) -> ExtractionResult { + const OUTPUT_FILE_NAME: &str = "swapped.bin"; + + let mut result = ExtractionResult { + ..Default::default() + }; + + if let Some(data) = file_data.get(offset..) { + let swapped_data = byte_swap(data, n); + + result.success = !swapped_data.is_empty(); + + if result.success { + result.size = Some(swapped_data.len()); + + // Write to file, if requested + if output_directory.is_some() { + let chroot = Chroot::new(output_directory); + result.success = chroot.create_file(OUTPUT_FILE_NAME, &swapped_data); + } + } + } + + result +} + +/// Swap every n bytes of the provided data +/// +/// ## Example: +/// +/// ``` +/// use binwalk::extractors::swapped::byte_swap; +/// +/// assert_eq!(byte_swap(b"ABCD", 2), b"CDAB"); +/// ``` +pub fn byte_swap(data: &[u8], n: usize) -> Vec { + let chunk_size = n * 2; + let mut chunker = data.chunks(chunk_size); + let mut swapped_data: Vec = Vec::new(); + + loop { + match chunker.next() { + None => { + break; + } + Some(chunk) => { + if chunk.len() != chunk_size { + break; + } + + swapped_data.extend(chunk[n..].to_vec()); + swapped_data.extend(chunk[0..n].to_vec()); + } + } + } + + swapped_data +} diff --git a/src/magic.rs b/src/magic.rs index 3ce2e94cd..f29ca4c98 100644 --- a/src/magic.rs +++ b/src/magic.rs @@ -1086,6 +1086,17 @@ pub fn patterns() -> Vec { description: signatures::uboot::DESCRIPTION.to_string(), extractor: None, }, + // dms firmware + signatures::common::Signature { + name: "dms".to_string(), + short: false, + magic_offset: 0, + always_display: false, + magic: signatures::dms::dms_magic(), + parser: signatures::dms::dms_parser, + description: signatures::dms::DESCRIPTION.to_string(), + extractor: Some(extractors::swapped::swapped_extractor_u16()), + }, ]; binary_signatures diff --git a/src/signatures.rs b/src/signatures.rs index d789a95fc..00da4fdbe 100644 --- a/src/signatures.rs +++ b/src/signatures.rs @@ -129,6 +129,7 @@ pub mod dlink_tlv; pub mod dlke; pub mod dlob; pub mod dmg; +pub mod dms; pub mod dtb; pub mod dxbc; pub mod ecos; diff --git a/src/signatures/dms.rs b/src/signatures/dms.rs new file mode 100644 index 000000000..fa7e232e9 --- /dev/null +++ b/src/signatures/dms.rs @@ -0,0 +1,45 @@ +use crate::extractors::swapped::byte_swap; +use crate::signatures::common::{SignatureError, SignatureResult, CONFIDENCE_MEDIUM}; +use crate::structures::dms::parse_dms_header; + +/// Human readable description +pub const DESCRIPTION: &str = "DMS firmware image"; + +/// DMS firmware image magic bytes +pub fn dms_magic() -> Vec> { + vec![b"0><1".to_vec()] +} + +/// Validates the DMS header +pub fn dms_parser(file_data: &[u8], offset: usize) -> Result { + const MIN_SIZE: usize = 0x100; + const BYTE_SWAP_SIZE: usize = 2; + const MAGIC_OFFSET: usize = 4; + + // Successful return value + let mut result = SignatureResult { + description: DESCRIPTION.to_string(), + confidence: CONFIDENCE_MEDIUM, + ..Default::default() + }; + + // The magic bytes start at offset 4 + if offset >= MAGIC_OFFSET { + result.offset = offset - MAGIC_OFFSET; + + if let Some(dms_data) = file_data.get(result.offset..result.offset + MIN_SIZE) { + // DMS firmware images have every 2 bytes swapped + let swapped_data = byte_swap(dms_data, BYTE_SWAP_SIZE); + + // Validate the DMS firmware header + if let Ok(dms_header) = parse_dms_header(&swapped_data) { + result.size = dms_header.image_size; + result.description = + format!("{}, total size: {} bytes", result.description, result.size); + return Ok(result); + } + } + } + + Err(SignatureError) +} diff --git a/src/structures.rs b/src/structures.rs index 808b7fbe3..b20d9513a 100644 --- a/src/structures.rs +++ b/src/structures.rs @@ -112,6 +112,7 @@ pub mod deb; pub mod dlink_tlv; pub mod dlob; pub mod dmg; +pub mod dms; pub mod dtb; pub mod dxbc; pub mod efigpt; diff --git a/src/structures/dms.rs b/src/structures/dms.rs new file mode 100644 index 000000000..b2ce840e5 --- /dev/null +++ b/src/structures/dms.rs @@ -0,0 +1,32 @@ +use crate::structures::common::{self, StructureError}; + +/// Struct to store DMS header info +#[derive(Debug, Default, Clone)] +pub struct DMSHeader { + pub image_size: usize, +} + +/// Parses a DMS header +pub fn parse_dms_header(dms_data: &[u8]) -> Result { + const MAGIC_P1: usize = 0x4D47; + const MAGIC_P2: usize = 0x3C31303E; + + let dms_structure = vec![ + ("unknown1", "u16"), + ("magic_p1", "u16"), + ("magic_p2", "u32"), + ("unknown2", "u32"), + ("image_size", "u32"), + ]; + + // Parse the first half of the header + if let Ok(dms_header) = common::parse(dms_data, &dms_structure, "big") { + if dms_header["magic_p1"] == MAGIC_P1 && dms_header["magic_p2"] == MAGIC_P2 { + return Ok(DMSHeader { + image_size: dms_header["image_size"], + }); + } + } + + Err(StructureError) +}