Skip to content

Commit 940aa70

Browse files
committed
Added DMS firmware signature; added byte-swap extractor
1 parent a47c0df commit 940aa70

File tree

7 files changed

+196
-0
lines changed

7 files changed

+196
-0
lines changed

src/extractors.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ pub mod shrs;
178178
pub mod squashfs;
179179
pub mod srec;
180180
pub mod svg;
181+
pub mod swapped;
181182
pub mod tarball;
182183
pub mod trx;
183184
pub mod tsk;

src/extractors/swapped.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
2+
3+
/// Defines the internal extractor function for u16 swapped firmware images
4+
///
5+
/// ```
6+
/// use std::io::ErrorKind;
7+
/// use std::process::Command;
8+
/// use binwalk::extractors::common::ExtractorType;
9+
/// use binwalk::extractors::swapped::swapped_extractor_u16;
10+
///
11+
/// match swapped_extractor_u16().utility {
12+
/// ExtractorType::None => panic!("Invalid extractor type of None"),
13+
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
14+
/// ExtractorType::External(cmd) => {
15+
/// if let Err(e) = Command::new(&cmd).output() {
16+
/// if e.kind() == ErrorKind::NotFound {
17+
/// panic!("External extractor '{}' not found", cmd);
18+
/// } else {
19+
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
20+
/// }
21+
/// }
22+
/// }
23+
/// }
24+
/// ```
25+
pub fn swapped_extractor_u16() -> Extractor {
26+
Extractor {
27+
utility: ExtractorType::Internal(extract_swapped_u16),
28+
..Default::default()
29+
}
30+
}
31+
32+
/// Extract firmware where every two bytes have been swapped
33+
pub fn extract_swapped_u16(
34+
file_data: &[u8],
35+
offset: usize,
36+
output_directory: Option<&String>,
37+
) -> ExtractionResult {
38+
const SWAP_BYTE_COUNT: usize = 2;
39+
extract_swapped(file_data, offset, output_directory, SWAP_BYTE_COUNT)
40+
}
41+
42+
/// Extract a block of data where every n bytes have been swapped
43+
fn extract_swapped(
44+
file_data: &[u8],
45+
offset: usize,
46+
output_directory: Option<&String>,
47+
n: usize,
48+
) -> ExtractionResult {
49+
const OUTPUT_FILE_NAME: &str = "swapped.bin";
50+
51+
let mut result = ExtractionResult {
52+
..Default::default()
53+
};
54+
55+
if let Some(data) = file_data.get(offset..) {
56+
let swapped_data = byte_swap(data, n);
57+
58+
result.success = !swapped_data.is_empty();
59+
60+
if result.success {
61+
result.size = Some(swapped_data.len());
62+
63+
// Write to file, if requested
64+
if output_directory.is_some() {
65+
let chroot = Chroot::new(output_directory);
66+
result.success = chroot.create_file(OUTPUT_FILE_NAME, &swapped_data);
67+
}
68+
}
69+
}
70+
71+
result
72+
}
73+
74+
/// Swap every n bytes of the provided data
75+
///
76+
/// ## Example:
77+
///
78+
/// ```
79+
/// use binwalk::extractors::swapped::byte_swap;
80+
///
81+
/// assert_eq!(byte_swap(b"ABCD", 2), b"CDAB");
82+
/// ```
83+
pub fn byte_swap(data: &[u8], n: usize) -> Vec<u8> {
84+
let chunk_size = n * 2;
85+
let mut chunker = data.chunks(chunk_size);
86+
let mut swapped_data: Vec<u8> = Vec::new();
87+
88+
loop {
89+
match chunker.next() {
90+
None => {
91+
break;
92+
}
93+
Some(chunk) => {
94+
if chunk.len() != chunk_size {
95+
break;
96+
}
97+
98+
swapped_data.extend(chunk[n..].to_vec());
99+
swapped_data.extend(chunk[0..n].to_vec());
100+
}
101+
}
102+
}
103+
104+
swapped_data
105+
}

src/magic.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,17 @@ pub fn patterns() -> Vec<signatures::common::Signature> {
10861086
description: signatures::uboot::DESCRIPTION.to_string(),
10871087
extractor: None,
10881088
},
1089+
// dms firmware
1090+
signatures::common::Signature {
1091+
name: "dms".to_string(),
1092+
short: false,
1093+
magic_offset: 0,
1094+
always_display: false,
1095+
magic: signatures::dms::dms_magic(),
1096+
parser: signatures::dms::dms_parser,
1097+
description: signatures::dms::DESCRIPTION.to_string(),
1098+
extractor: Some(extractors::swapped::swapped_extractor_u16()),
1099+
},
10891100
];
10901101

10911102
binary_signatures

src/signatures.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ pub mod dlink_tlv;
129129
pub mod dlke;
130130
pub mod dlob;
131131
pub mod dmg;
132+
pub mod dms;
132133
pub mod dtb;
133134
pub mod dxbc;
134135
pub mod ecos;

src/signatures/dms.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use crate::extractors::swapped::byte_swap;
2+
use crate::signatures::common::{SignatureError, SignatureResult, CONFIDENCE_MEDIUM};
3+
use crate::structures::dms::parse_dms_header;
4+
5+
/// Human readable description
6+
pub const DESCRIPTION: &str = "DMS firmware image";
7+
8+
/// DMS firmware image magic bytes
9+
pub fn dms_magic() -> Vec<Vec<u8>> {
10+
vec![b"0><1".to_vec()]
11+
}
12+
13+
/// Validates the DMS header
14+
pub fn dms_parser(file_data: &[u8], offset: usize) -> Result<SignatureResult, SignatureError> {
15+
const MIN_SIZE: usize = 0x100;
16+
const BYTE_SWAP_SIZE: usize = 2;
17+
const MAGIC_OFFSET: usize = 4;
18+
19+
// Successful return value
20+
let mut result = SignatureResult {
21+
description: DESCRIPTION.to_string(),
22+
confidence: CONFIDENCE_MEDIUM,
23+
..Default::default()
24+
};
25+
26+
// The magic bytes start at offset 4
27+
if offset >= MAGIC_OFFSET {
28+
result.offset = offset - MAGIC_OFFSET;
29+
30+
if let Some(dms_data) = file_data.get(result.offset..result.offset + MIN_SIZE) {
31+
// DMS firmware images have every 2 bytes swapped
32+
let swapped_data = byte_swap(dms_data, BYTE_SWAP_SIZE);
33+
34+
// Validate the DMS firmware header
35+
if let Ok(dms_header) = parse_dms_header(&swapped_data) {
36+
result.size = dms_header.image_size;
37+
result.description =
38+
format!("{}, total size: {} bytes", result.description, result.size);
39+
return Ok(result);
40+
}
41+
}
42+
}
43+
44+
Err(SignatureError)
45+
}

src/structures.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ pub mod deb;
112112
pub mod dlink_tlv;
113113
pub mod dlob;
114114
pub mod dmg;
115+
pub mod dms;
115116
pub mod dtb;
116117
pub mod dxbc;
117118
pub mod efigpt;

src/structures/dms.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use crate::structures::common::{self, StructureError};
2+
3+
/// Struct to store DMS header info
4+
#[derive(Debug, Default, Clone)]
5+
pub struct DMSHeader {
6+
pub image_size: usize,
7+
}
8+
9+
/// Parses a DMS header
10+
pub fn parse_dms_header(dms_data: &[u8]) -> Result<DMSHeader, StructureError> {
11+
const MAGIC_P1: usize = 0x4D47;
12+
const MAGIC_P2: usize = 0x3C31303E;
13+
14+
let dms_structure = vec![
15+
("unknown1", "u16"),
16+
("magic_p1", "u16"),
17+
("magic_p2", "u32"),
18+
("unknown2", "u32"),
19+
("image_size", "u32"),
20+
];
21+
22+
// Parse the first half of the header
23+
if let Ok(dms_header) = common::parse(dms_data, &dms_structure, "big") {
24+
if dms_header["magic_p1"] == MAGIC_P1 && dms_header["magic_p2"] == MAGIC_P2 {
25+
return Ok(DMSHeader {
26+
image_size: dms_header["image_size"],
27+
});
28+
}
29+
}
30+
31+
Err(StructureError)
32+
}

0 commit comments

Comments
 (0)