diff --git a/src/magic.rs b/src/magic.rs index f29ca4c98..700f55d9c 100644 --- a/src/magic.rs +++ b/src/magic.rs @@ -1097,6 +1097,17 @@ pub fn patterns() -> Vec { description: signatures::dms::DESCRIPTION.to_string(), extractor: Some(extractors::swapped::swapped_extractor_u16()), }, + // dkbs firmware + signatures::common::Signature { + name: "dkbs".to_string(), + short: false, + magic_offset: 0, + always_display: false, + magic: signatures::dkbs::dkbs_magic(), + parser: signatures::dkbs::dkbs_parser, + description: signatures::dkbs::DESCRIPTION.to_string(), + extractor: None, + }, ]; binary_signatures diff --git a/src/signatures.rs b/src/signatures.rs index 00da4fdbe..61dcbb7ed 100644 --- a/src/signatures.rs +++ b/src/signatures.rs @@ -125,6 +125,7 @@ pub mod cramfs; pub mod csman; pub mod dahua_zip; pub mod deb; +pub mod dkbs; pub mod dlink_tlv; pub mod dlke; pub mod dlob; diff --git a/src/signatures/dkbs.rs b/src/signatures/dkbs.rs new file mode 100644 index 000000000..33ed5685e --- /dev/null +++ b/src/signatures/dkbs.rs @@ -0,0 +1,56 @@ +use crate::signatures::common::{ + SignatureError, SignatureResult, CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, +}; +use crate::structures::dkbs::parse_dkbs_header; + +/// Human readable description +pub const DESCRIPTION: &str = "DKBS firmware header"; + +/// DKBS firmware magic +pub fn dkbs_magic() -> Vec> { + vec![b"_dkbs_".to_vec()] +} + +/// Validates the DKBS header +pub fn dkbs_parser(file_data: &[u8], offset: usize) -> Result { + const MAGIC_OFFSET: usize = 7; + + // Successful return value + let mut result = SignatureResult { + description: DESCRIPTION.to_string(), + confidence: CONFIDENCE_MEDIUM, + ..Default::default() + }; + + // Sanity check the magic bytes offset + if offset >= MAGIC_OFFSET { + // Magic bytes occur 7 bytes into the actual firmware header + result.offset = offset - MAGIC_OFFSET; + + // Parse the firmware header + if let Ok(dkbs_header) = parse_dkbs_header(&file_data[result.offset..]) { + // Calculate the total bytes available after the firmware header + let available_data: usize = file_data.len() - result.offset; + + // Sanity check on the total reported DKBS firmware size + if available_data >= (dkbs_header.header_size + dkbs_header.data_size) { + // If this header starts at the beginning of the file, confidence is high + if result.offset == 0 { + result.confidence = CONFIDENCE_HIGH; + } + + // Report header size and description + result.size = dkbs_header.header_size; + result.description = format!( + "{}, board ID: {}, firmware version: {}, boot device: {}, header size: {} bytes, data size: {}", + result.description, dkbs_header.board_id, dkbs_header.version, dkbs_header.boot_device, dkbs_header.header_size, dkbs_header.data_size + ); + + // Return OK + return Ok(result); + } + } + } + + Err(SignatureError) +} diff --git a/src/structures.rs b/src/structures.rs index b20d9513a..7bf8194ed 100644 --- a/src/structures.rs +++ b/src/structures.rs @@ -109,6 +109,7 @@ pub mod cpio; pub mod cramfs; pub mod csman; pub mod deb; +pub mod dkbs; pub mod dlink_tlv; pub mod dlob; pub mod dmg; diff --git a/src/structures/dkbs.rs b/src/structures/dkbs.rs new file mode 100644 index 000000000..aa065762e --- /dev/null +++ b/src/structures/dkbs.rs @@ -0,0 +1,61 @@ +use crate::common::get_cstring; +use crate::structures::common::{self, StructureError}; + +/// Struct to store DKBS header info +#[derive(Debug, Default, Clone)] +pub struct DKBSHeader { + pub data_size: usize, + pub header_size: usize, + pub board_id: String, + pub version: String, + pub boot_device: String, +} + +/// Parses a DKBS header +pub fn parse_dkbs_header(dkbs_data: &[u8]) -> Result { + // Header is a fixed size + const HEADER_SIZE: usize = 0xA0; + + // Constant offsets for strings and known header fields + const BOARD_ID_START: usize = 0; + const BOARD_ID_END: usize = 0x20; + const VERSION_START: usize = 0x28; + const VERSION_END: usize = 0x48; + const BOOT_DEVICE_START: usize = 0x70; + const BOOT_DEVICE_END: usize = 0x90; + const DATA_SIZE_START: usize = 0x68; + const DATA_SIZE_END: usize = DATA_SIZE_START + 4; + + let data_size_field = vec![("size", "u32")]; + + let mut header = DKBSHeader { + header_size: HEADER_SIZE, + ..Default::default() + }; + + // Available data should be at least big enough for the header to fit + if dkbs_data.len() >= HEADER_SIZE { + // Parse the version, board ID, and boot device strings + header.version = get_cstring(&dkbs_data[VERSION_START..VERSION_END]); + header.board_id = get_cstring(&dkbs_data[BOARD_ID_START..BOARD_ID_END]); + header.boot_device = get_cstring(&dkbs_data[BOOT_DEVICE_START..BOOT_DEVICE_END]); + + // Sanity check to make sure the strings were retrieved + if !header.version.is_empty() + && !header.board_id.is_empty() + && !header.boot_device.is_empty() + { + // Parse the payload size field + if let Ok(data_size) = common::parse( + &dkbs_data[DATA_SIZE_START..DATA_SIZE_END], + &data_size_field, + "big", + ) { + header.data_size = data_size["size"]; + return Ok(header); + } + } + } + + Err(StructureError) +}