Skip to content

Commit

Permalink
Merge pull request ReFirmLabs#786 from ReFirmLabs/dms_short_swapped
Browse files Browse the repository at this point in the history
Added DMS firmware signature; added byte-swap extractor
  • Loading branch information
devttys0 authored Nov 30, 2024
2 parents a47c0df + 940aa70 commit c5b53a3
Show file tree
Hide file tree
Showing 7 changed files with 196 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 @@ -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;
Expand Down
105 changes: 105 additions & 0 deletions src/extractors/swapped.rs
Original file line number Diff line number Diff line change
@@ -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<u8> {
let chunk_size = n * 2;
let mut chunker = data.chunks(chunk_size);
let mut swapped_data: Vec<u8> = 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
}
11 changes: 11 additions & 0 deletions src/magic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,17 @@ pub fn patterns() -> Vec<signatures::common::Signature> {
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
Expand Down
1 change: 1 addition & 0 deletions src/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
45 changes: 45 additions & 0 deletions src/signatures/dms.rs
Original file line number Diff line number Diff line change
@@ -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<u8>> {
vec![b"0><1".to_vec()]
}

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

0 comments on commit c5b53a3

Please sign in to comment.