Skip to content

Commit

Permalink
Added support for uImage headers with invalid CRC's (D-link)
Browse files Browse the repository at this point in the history
  • Loading branch information
devttys0 committed Nov 8, 2024
1 parent b29699b commit b404ac4
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 35 deletions.
35 changes: 21 additions & 14 deletions src/extractors/uimage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,33 @@ pub fn extract_uimage(
let image_data_start = offset + uimage_header.header_size;
let image_data_end = image_data_start + uimage_header.data_size;

// Get the raw image data after the uImage header and validate the data CRC
// Get the raw image data after the uImage header to validate the data CRC
if let Some(image_data) = file_data.get(image_data_start..image_data_end) {
if crc32(image_data) == (uimage_header.data_checksum as u32) {
result.success = true;
result.size = Some(uimage_header.header_size + uimage_header.data_size);
result.success = true;
result.size = Some(uimage_header.header_size);

// If extraction was requested, carve the uImage data out to a file
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);
let mut file_base_name: String = DEFAULT_OUTPUT_FILE_NAME.to_string();
// Check the data CRC
let data_crc_valid: bool =
crc32(image_data) == (uimage_header.data_checksum as u32);

// Use the name specified in the uImage header as the file name, if one was provided
if !uimage_header.name.is_empty() {
file_base_name = uimage_header.name.replace(" ", "_");
}
// If the data CRC is valid, include the size of the data in the reported size
if data_crc_valid {
result.size = Some(result.size.unwrap() + uimage_header.data_size);
}

let output_file = format!("{}.{}", file_base_name, OUTPUT_FILE_EXT);
// If extraction was requested and the data CRC is valid, carve the uImage data out to a file
if data_crc_valid && output_directory.is_some() {
let chroot = Chroot::new(output_directory);
let mut file_base_name: String = DEFAULT_OUTPUT_FILE_NAME.to_string();

result.success = chroot.create_file(&output_file, image_data);
// Use the name specified in the uImage header as the file name, if one was provided
if !uimage_header.name.is_empty() {
file_base_name = uimage_header.name.replace(" ", "_");
}

let output_file = format!("{}.{}", file_base_name, OUTPUT_FILE_EXT);

result.success = chroot.create_file(&output_file, image_data);
}
}
}
Expand Down
20 changes: 17 additions & 3 deletions src/signatures/uimage.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::common::epoch_to_string;
use crate::extractors::uimage::extract_uimage;
use crate::signatures::common::{SignatureError, SignatureResult, CONFIDENCE_HIGH};
use crate::signatures::common::{
SignatureError, SignatureResult, CONFIDENCE_HIGH, CONFIDENCE_LOW, CONFIDENCE_MEDIUM,
};
use crate::structures::uimage::parse_uimage_header;

/// Human readable description
Expand Down Expand Up @@ -29,9 +31,10 @@ pub fn uimage_parser(file_data: &[u8], offset: usize) -> Result<SignatureResult,
if let Some(uimage_size) = dry_run.size {
// Extraction dry-run ok, parse the header to display some useful info
if let Ok(uimage_header) = parse_uimage_header(&file_data[offset..]) {
// Configure SignatureResult; decline extraction if data size is 0 (looking at you, DD-WRT)
result.size = uimage_size;
result.extraction_declined = uimage_header.data_size == 0;
// Decline extraction if the header CRC does not match, or if the reported data size is 0
result.extraction_declined =
!uimage_header.header_crc_valid || uimage_header.data_size == 0;
result.description = format!("{}, header size: {} bytes, data size: {} bytes, compression: {}, CPU: {}, OS: {}, image type: {}, load address: {:#X}, entry point: {:#X}, creation time: {}, image name: \"{}\"",
result.description,
uimage_header.header_size,
Expand All @@ -44,6 +47,17 @@ pub fn uimage_parser(file_data: &[u8], offset: usize) -> Result<SignatureResult,
uimage_header.entry_point_address,
epoch_to_string(uimage_header.timestamp as u32),
uimage_header.name);
// If the header CRC is invalid, adjust the reported confidence level and report the checksum mis-match
if !uimage_header.header_crc_valid {
// If the uImage header was otherwise valid and starts at file offset 0 then we're still fairly confident in the result
if result.offset == 0 {
result.confidence = CONFIDENCE_MEDIUM;
} else {
result.confidence = CONFIDENCE_LOW;
}

result.description = format!("{}, invalid checksum", result.description);
}

return Ok(result);
}
Expand Down
36 changes: 18 additions & 18 deletions src/structures/uimage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub struct UImageHeader {
pub cpu_type: String,
pub os_type: String,
pub image_type: String,
pub header_crc_valid: bool,
}

/// Pase a uImage header
Expand Down Expand Up @@ -163,25 +164,24 @@ pub fn parse_uimage_header(uimage_data: &[u8]) -> Result<UImageHeader, Structure
&& valid_image_types.contains_key(&uimage_header["image_type"])
&& valid_compression_types.contains_key(&uimage_header["compression_type"])
{
// Finally, validate the header CRC
// Get the header bytes to validate the CRC
if let Some(crc_data) = uimage_data.get(0..UIMAGE_HEADER_SIZE) {
if calculate_uimage_header_checksum(crc_data) == uimage_header["header_crc"] {
return Ok(UImageHeader {
header_size: UIMAGE_HEADER_SIZE,
name: get_cstring(&uimage_data[UIMAGE_NAME_OFFSET..]),
data_size: uimage_header["data_size"],
data_checksum: uimage_header["data_crc"],
timestamp: uimage_header["creation_timestamp"],
load_address: uimage_header["load_address"],
entry_point_address: uimage_header["entry_point_address"],
compression_type: valid_compression_types
[&uimage_header["compression_type"]]
.to_string(),
cpu_type: valid_cpu_types[&uimage_header["cpu_type"]].to_string(),
os_type: valid_os_types[&uimage_header["os_type"]].to_string(),
image_type: valid_image_types[&uimage_header["image_type"]].to_string(),
});
}
return Ok(UImageHeader {
header_size: UIMAGE_HEADER_SIZE,
name: get_cstring(&uimage_data[UIMAGE_NAME_OFFSET..]),
data_size: uimage_header["data_size"],
data_checksum: uimage_header["data_crc"],
timestamp: uimage_header["creation_timestamp"],
load_address: uimage_header["load_address"],
entry_point_address: uimage_header["entry_point_address"],
compression_type: valid_compression_types[&uimage_header["compression_type"]]
.to_string(),
cpu_type: valid_cpu_types[&uimage_header["cpu_type"]].to_string(),
os_type: valid_os_types[&uimage_header["os_type"]].to_string(),
image_type: valid_image_types[&uimage_header["image_type"]].to_string(),
header_crc_valid: calculate_uimage_header_checksum(crc_data)
== uimage_header["header_crc"],
});
}
}
}
Expand Down

0 comments on commit b404ac4

Please sign in to comment.