From daccc336f1d270160321718189f258685a5a3683 Mon Sep 17 00:00:00 2001 From: devttys0 Date: Fri, 22 Nov 2024 07:11:40 -0500 Subject: [PATCH 1/4] Added DLKE firmware signature and file carver --- src/extractors.rs | 1 + src/extractors/dlke.rs | 86 +++++++++++++++++++++++++++++++++++++++++ src/magic.rs | 11 ++++++ src/signatures.rs | 1 + src/signatures/dlke.rs | 47 ++++++++++++++++++++++ src/structures/jboot.rs | 10 ++--- 6 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 src/extractors/dlke.rs create mode 100644 src/signatures/dlke.rs diff --git a/src/extractors.rs b/src/extractors.rs index efff7df89..54345bfe9 100644 --- a/src/extractors.rs +++ b/src/extractors.rs @@ -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; diff --git a/src/extractors/dlke.rs b/src/extractors/dlke.rs new file mode 100644 index 000000000..31c8dfe21 --- /dev/null +++ b/src/extractors/dlke.rs @@ -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 +} diff --git a/src/magic.rs b/src/magic.rs index 772e69071..0e2aa49a2 100644 --- a/src/magic.rs +++ b/src/magic.rs @@ -998,6 +998,17 @@ pub fn patterns() -> Vec { 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 diff --git a/src/signatures.rs b/src/signatures.rs index 298897604..f44724a8b 100644 --- a/src/signatures.rs +++ b/src/signatures.rs @@ -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; diff --git a/src/signatures/dlke.rs b/src/signatures/dlke.rs new file mode 100644 index 000000000..ded452e85 --- /dev/null +++ b/src/signatures/dlke.rs @@ -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> { + // 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 { + // 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) +} diff --git a/src/structures/jboot.rs b/src/structures/jboot.rs index ea192b6ab..1299620f7 100644 --- a/src/structures/jboot.rs +++ b/src/structures/jboot.rs @@ -22,12 +22,12 @@ pub fn parse_jboot_arm_header(jboot_data: &[u8]) -> Result Result Result Date: Fri, 22 Nov 2024 07:14:28 -0500 Subject: [PATCH 2/4] Removed debug print --- src/structures/jboot.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/structures/jboot.rs b/src/structures/jboot.rs index 1299620f7..086e42d59 100644 --- a/src/structures/jboot.rs +++ b/src/structures/jboot.rs @@ -57,7 +57,6 @@ pub fn parse_jboot_arm_header(jboot_data: &[u8]) -> Result Date: Fri, 22 Nov 2024 07:25:18 -0500 Subject: [PATCH 3/4] Fixed jboot signature magic --- src/signatures/jboot.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signatures/jboot.rs b/src/signatures/jboot.rs index fdc8c6bc3..db4d64b5e 100644 --- a/src/signatures/jboot.rs +++ b/src/signatures/jboot.rs @@ -14,7 +14,7 @@ pub const JBOOT_SCH2_DESCRIPTION: &str = "JBOOT SCH2 header"; /// JBOOT firmware header magic bytes pub fn jboot_arm_magic() -> Vec> { vec![ - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x42\x48\x02\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x42\x48" .to_vec(), ] } From 7aa2ad6c98fb6aec621a8149a7991d39d7dc0591 Mon Sep 17 00:00:00 2001 From: devttys0 Date: Fri, 22 Nov 2024 08:07:12 -0500 Subject: [PATCH 4/4] Fixed code formatting --- src/signatures/jboot.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/signatures/jboot.rs b/src/signatures/jboot.rs index db4d64b5e..796361c31 100644 --- a/src/signatures/jboot.rs +++ b/src/signatures/jboot.rs @@ -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![ - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x42\x48" - .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