Skip to content

Commit

Permalink
Merge pull request #759 from ReFirmLabs/dlke_firmware
Browse files Browse the repository at this point in the history
DLKE firmware
  • Loading branch information
devttys0 authored Nov 22, 2024
2 parents 7251f36 + 7aa2ad6 commit b37cc9b
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 9 deletions.
1 change: 1 addition & 0 deletions src/extractors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ pub mod common;
pub mod csman;
pub mod dahua_zip;
pub mod dlink_tlv;
pub mod dlke;
pub mod dmg;
pub mod dtb;
pub mod dumpifs;
Expand Down
86 changes: 86 additions & 0 deletions src/extractors/dlke.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::structures::jboot::parse_jboot_arm_header;

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

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

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

// Parse the first header, which describes the size of the firmware signature
if let Some(dlke_sig_header_data) = file_data.get(offset..) {
if let Ok(dlke_signature_header) = parse_jboot_arm_header(dlke_sig_header_data) {
// Second header should immediately follow the first
if let Some(dlke_crypt_header_data) = file_data
.get(offset + dlke_signature_header.header_size + dlke_signature_header.data_size..)
{
// Parse the second header, which describes the size of the encrypted data
if let Ok(dlke_crypt_header) = parse_jboot_arm_header(dlke_crypt_header_data) {
result.success = true;
result.size = Some(
dlke_signature_header.header_size
+ dlke_signature_header.data_size
+ dlke_crypt_header.header_size
+ dlke_crypt_header.data_size,
);

if output_directory.is_some() {
let chroot = Chroot::new(output_directory);

if !chroot.carve_file(
SIGNATURE_FILE_NAME,
dlke_sig_header_data,
dlke_signature_header.header_size,
dlke_signature_header.data_size,
) || !chroot.carve_file(
ENCRYPTED_FILE_NAME,
dlke_crypt_header_data,
dlke_crypt_header.header_size,
dlke_crypt_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 @@ -998,6 +998,17 @@ pub fn patterns() -> Vec<signatures::common::Signature> {
description: signatures::dlink_tlv::DESCRIPTION.to_string(),
extractor: Some(extractors::dlink_tlv::dlink_tlv_extractor()),
},
// DLKE encrypted firmware
signatures::common::Signature {
name: "dlke".to_string(),
short: false,
magic_offset: 0,
always_display: false,
magic: signatures::dlke::dlke_magic(),
parser: signatures::dlke::dlke_parser,
description: signatures::dlke::DESCRIPTION.to_string(),
extractor: Some(extractors::dlke::dlke_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 @@ -125,6 +125,7 @@ pub mod csman;
pub mod dahua_zip;
pub mod deb;
pub mod dlink_tlv;
pub mod dlke;
pub mod dlob;
pub mod dmg;
pub mod dtb;
Expand Down
47 changes: 47 additions & 0 deletions src/signatures/dlke.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::signatures::common::{SignatureError, SignatureResult, CONFIDENCE_HIGH};
use crate::structures::jboot::parse_jboot_arm_header;

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

/// DLKE encrypted firmware images always start with these bytes
pub fn dlke_magic() -> Vec<Vec<u8>> {
// These magic bytes are technically the ROM-ID field of a JBOOT header
vec![b"DLK6E8202001".to_vec(), b"DLK6E6110002".to_vec()]
}

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

// Parse the first header, which describes the size of the firmware signature
if let Ok(dlke_signature_header) = parse_jboot_arm_header(&file_data[offset..]) {
// Second header should immediately follow the first
if let Some(dlke_crypt_header_data) = file_data
.get(offset + dlke_signature_header.header_size + dlke_signature_header.data_size..)
{
// Parse the second header, which describes the size of the encrypted data
if let Ok(dlke_crypt_header) = parse_jboot_arm_header(dlke_crypt_header_data) {
result.size = dlke_signature_header.header_size
+ dlke_signature_header.data_size
+ dlke_crypt_header.header_size
+ dlke_crypt_header.data_size;
result.description = format!(
"{}, signature size: {} bytes, encrypted data size: {} bytes",
result.description,
dlke_signature_header.data_size,
dlke_crypt_header.data_size
);
return Ok(result);
}
}
}

Err(SignatureError)
}
5 changes: 1 addition & 4 deletions src/signatures/jboot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ pub const JBOOT_SCH2_DESCRIPTION: &str = "JBOOT SCH2 header";

/// JBOOT firmware header magic bytes
pub fn jboot_arm_magic() -> Vec<Vec<u8>> {
vec![
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x42\x48\x02\x00\x00\x00"
.to_vec(),
]
vec![b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x42\x48".to_vec()]
}

/// JBOOT STAG header magic bytes
Expand Down
9 changes: 4 additions & 5 deletions src/structures/jboot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ pub fn parse_jboot_arm_header(jboot_data: &[u8]) -> Result<JBOOTArmHeader, Struc
const LPVS_VALUE: usize = 1;
const MBZ_VALUE: usize = 0;
const HEADER_ID_VALUE: usize = 0x4842;
const HEADER_VERSION_VALUE: usize = 2;
const HEADER_MAX_VERSION_VALUE: usize = 4;

let arm_structure = vec![
("drange", "u16"),
("image_checksum", "u16"),
("reserved1", "u32"),
("block_size", "u32"),
("reserved2", "u32"),
("reserved3", "u16"),
("lpvs", "u8"),
Expand Down Expand Up @@ -58,8 +58,7 @@ pub fn parse_jboot_arm_header(jboot_data: &[u8]) -> Result<JBOOTArmHeader, Struc
// Parse the header structure
if let Ok(arm_header) = common::parse(header_data, &arm_structure, "little") {
// Make sure the reserved fields are NULL
if arm_header["reserved1"] == 0
&& arm_header["reserved2"] == 0
if arm_header["reserved2"] == 0
&& arm_header["reserved3"] == 0
&& arm_header["reserved4"] == 0
&& arm_header["reserved5"] == 0
Expand All @@ -71,7 +70,7 @@ pub fn parse_jboot_arm_header(jboot_data: &[u8]) -> Result<JBOOTArmHeader, Struc
if arm_header["lpvs"] == LPVS_VALUE
&& arm_header["mbz"] == MBZ_VALUE
&& arm_header["header_id"] == HEADER_ID_VALUE
&& arm_header["header_version"] == HEADER_VERSION_VALUE
&& arm_header["header_version"] <= HEADER_MAX_VERSION_VALUE
{
return Ok(JBOOTArmHeader {
header_size,
Expand Down

0 comments on commit b37cc9b

Please sign in to comment.