Skip to content

Commit

Permalink
Added SHRS firmware signature
Browse files Browse the repository at this point in the history
  • Loading branch information
devttys0 committed Nov 26, 2024
1 parent 75c32b8 commit 88639a5
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ aho-corasick = "1.1.3"
serde = { version = "1.0", features = ["derive"]}
clap = { version = "4.5.16", features = ["derive"] }
xxhash-rust = { version = "0.8.12", features = ["xxh32"] }
hex = "0.4.3"

[dependencies.uuid]
version = "1.10.0"
Expand Down
1 change: 1 addition & 0 deletions src/extractors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ pub mod rar;
pub mod riff;
pub mod romfs;
pub mod sevenzip;
pub mod shrs;
pub mod squashfs;
pub mod srec;
pub mod svg;
Expand Down
71 changes: 71 additions & 0 deletions src/extractors/shrs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::structures::shrs::parse_shrs_header;

/// Defines the internal extractor function for carving out D-Link SHRS firmware images
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::shrs::shrs_extractor;
///
/// match shrs_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 shrs_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(extract_shrs_image),
..Default::default()
}
}

/// Internal extractor for carve pieces of encrypted SHRS firmware images to disk
pub fn extract_shrs_image(
file_data: &[u8],
offset: usize,
output_directory: Option<&String>,
) -> ExtractionResult {
const IV_FILE_NAME: &str = "iv.bin";
const ENCRYPTED_FILE_NAME: &str = "encrypted.bin";

let mut result = ExtractionResult {
..Default::default()
};

// Parse the header
if let Some(shrs_header_data) = file_data.get(offset..) {
if let Ok(shrs_header) = parse_shrs_header(shrs_header_data) {
result.success = true;
result.size = Some(shrs_header.header_size + shrs_header.data_size);

// Carve out the IV and encrypted data blob
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);

if !chroot.create_file(IV_FILE_NAME, &shrs_header.iv)
|| !chroot.carve_file(
ENCRYPTED_FILE_NAME,
file_data,
shrs_header.header_size,
shrs_header.data_size,
)
{
result.success = false;
}
}
}
}

result
}
11 changes: 11 additions & 0 deletions src/magic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,17 @@ pub fn patterns() -> Vec<signatures::common::Signature> {
description: signatures::dlke::DESCRIPTION.to_string(),
extractor: Some(extractors::dlke::dlke_extractor()),
},
// SHRS encrypted firmware
signatures::common::Signature {
name: "shrs".to_string(),
short: false,
magic_offset: 0,
always_display: false,
magic: signatures::shrs::shrs_magic(),
parser: signatures::shrs::shrs_parser,
description: signatures::shrs::DESCRIPTION.to_string(),
extractor: Some(extractors::shrs::shrs_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 @@ -169,6 +169,7 @@ pub mod rsa;
pub mod rtk;
pub mod seama;
pub mod sevenzip;
pub mod shrs;
pub mod squashfs;
pub mod srec;
pub mod svg;
Expand Down
42 changes: 42 additions & 0 deletions src/signatures/shrs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crate::signatures::common::{
SignatureError, SignatureResult, CONFIDENCE_LOW, CONFIDENCE_MEDIUM,
};
use crate::structures::shrs::parse_shrs_header;

/// Human readable description
pub const DESCRIPTION: &str = "SHRS encrypted firmware";

/// SHRS firmware images always start with these bytes
pub fn shrs_magic() -> Vec<Vec<u8>> {
vec![b"SHRS".to_vec()]
}

/// Validates the SHRS header
pub fn shrs_parser(file_data: &[u8], offset: usize) -> Result<SignatureResult, SignatureError> {
// Successful return value
let mut result = SignatureResult {
offset,
description: DESCRIPTION.to_string(),
confidence: CONFIDENCE_LOW,
..Default::default()
};

if let Ok(shrs_header) = parse_shrs_header(&file_data[offset..]) {
result.size = shrs_header.header_size + shrs_header.data_size;
result.description = format!(
"{}, header size: {} bytes, encrypted data size: {} bytes, IV: {}",
result.description,
shrs_header.header_size,
shrs_header.data_size,
hex::encode(shrs_header.iv),
);

if offset == 0 {
result.confidence = CONFIDENCE_MEDIUM;
}

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 @@ -144,6 +144,7 @@ pub mod romfs;
pub mod rtk;
pub mod seama;
pub mod sevenzip;
pub mod shrs;
pub mod squashfs;
pub mod svg;
pub mod tplink;
Expand Down
36 changes: 36 additions & 0 deletions src/structures/shrs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::structures::common::{self, StructureError};

/// Struct to store SHRS firmware header info
#[derive(Debug, Default, Clone)]
pub struct SHRSHeader {
pub iv: Vec<u8>,
pub data_size: usize,
pub header_size: usize,
}

/// Parses an SHRS header
pub fn parse_shrs_header(shrs_data: &[u8]) -> Result<SHRSHeader, StructureError> {
const IV_START: usize = 12;
const IV_END: usize = IV_START + 16;
const HEADER_SIZE: usize = 0x6DC;

let shrs_structure = vec![
("magic", "u32"),
("unknown1", "u32"),
("encrypted_data_size", "u32"),
// 16-byte IV immediately follows
];

// Parse the header
if let Ok(shrs_header) = common::parse(shrs_data, &shrs_structure, "big") {
if let Some(iv_bytes) = shrs_data.get(IV_START..IV_END) {
return Ok(SHRSHeader {
iv: iv_bytes.to_vec(),
data_size: shrs_header["encrypted_data_size"],
header_size: HEADER_SIZE,
});
}
}

Err(StructureError)
}

0 comments on commit 88639a5

Please sign in to comment.