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 DMS firmware signature; added byte-swap extractor #786

Merged
merged 1 commit into from
Nov 30, 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 @@ -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)
}