Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wince #727

Merged
merged 2 commits into from
Oct 30, 2024
Merged

Wince #727

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/extractors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ pub mod ubi;
pub mod uefi;
pub mod uimage;
pub mod vxworks;
pub mod wince;
pub mod yaffs2;
pub mod zip;
pub mod zlib;
Expand Down
131 changes: 131 additions & 0 deletions src/extractors/wince.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use crate::common::is_offset_safe;
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::structures::wince::{parse_wince_block_header, parse_wince_header};

/// Defines the internal extractor function for extracting Windows CE images
pub fn wince_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(wince_dump),
..Default::default()
}
}

/// Internal extractor for extracting data blocks from Windows CE images
pub fn wince_dump(
file_data: &[u8],
offset: usize,
output_directory: Option<&String>,
) -> ExtractionResult {
let mut result = ExtractionResult {
..Default::default()
};

// Parse the file header
if let Some(wince_data) = file_data.get(offset..) {
if let Ok(wince_header) = parse_wince_header(wince_data) {
// Get the block data, immediately following the file header
if let Some(wince_block_data) = wince_data.get(wince_header.header_size..) {
// Process all blocks in the block data
if let Some(data_blocks) = process_wince_blocks(wince_block_data) {
// The first block entry's address should equal the WinCE header's base address
if data_blocks.entries[0].address == wince_header.base_address {
// Block processing was successful
result.success = true;
result.size = Some(wince_header.header_size + data_blocks.total_size);

// If extraction was requested, extract each block to a file on disk
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);

for block in data_blocks.entries {
let block_file_name = format!("{:X}.bin", block.address);

// If file carving fails, report a failure to extract
if !chroot.carve_file(
block_file_name,
wince_block_data,
block.offset,
block.size,
) {
result.success = false;
break;
}
}
}
}
}
}
}
}

result
}

/// Stores info about each WinCE block
#[derive(Debug, Default, Clone)]
struct BlockInfo {
pub address: usize,
pub offset: usize,
pub size: usize,
}

/// Stores info about all WinCE blocks
#[derive(Debug, Default, Clone)]
struct BlockData {
pub total_size: usize,
pub entries: Vec<BlockInfo>,
}

/// Process all WinCE blocks
fn process_wince_blocks(blocks_data: &[u8]) -> Option<BlockData> {
// Arbitrarily chosen, just to make sure more than one or two blocks were processed and sane
const MIN_ENTRIES_COUNT: usize = 5;

let mut blocks = BlockData {
..Default::default()
};

let mut next_offset: usize = 0;
let mut previous_offset = None;
let available_data = blocks_data.len();

// Process all blocks until the end block is reached, or an error is encountered
while is_offset_safe(available_data, next_offset, previous_offset) {
// Parse this block's header
match parse_wince_block_header(&blocks_data[next_offset..]) {
Err(_) => {
break;
}
Ok(block_header) => {
// Include the block header size in the total size of the block data
blocks.total_size += block_header.header_size;

// A block header address of NULL indicates EOF
if block_header.address == 0 {
// Sanity check the number of blocks processed
if blocks.entries.len() > MIN_ENTRIES_COUNT {
return Some(blocks);
} else {
break;
}
} else {
// Include this block's size in the total size of the block data
blocks.total_size += block_header.data_size;

// Add this block to the list of block entries
blocks.entries.push(BlockInfo {
address: block_header.address,
offset: next_offset + block_header.header_size,
size: block_header.data_size,
});

// Update the offsets for the next loop iteration
previous_offset = Some(next_offset);
next_offset += block_header.header_size + block_header.data_size;
}
}
}
}

None
}
11 changes: 11 additions & 0 deletions src/magic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,17 @@ pub fn patterns() -> Vec<signatures::common::Signature> {
description: signatures::btrfs::DESCRIPTION.to_string(),
extractor: None,
},
// WinCE
signatures::common::Signature {
name: "wince".to_string(),
short: false,
magic_offset: 0,
always_display: false,
magic: signatures::wince::wince_magic(),
parser: signatures::wince::wince_parser,
description: signatures::wince::DESCRIPTION.to_string(),
extractor: Some(extractors::wince::wince_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 @@ -173,6 +173,7 @@ pub mod ubi;
pub mod uefi;
pub mod uimage;
pub mod vxworks;
pub mod wince;
pub mod xz;
pub mod yaffs;
pub mod zip;
Expand Down
45 changes: 45 additions & 0 deletions src/signatures/wince.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use crate::extractors::wince::wince_dump;
use crate::signatures::common::{SignatureError, SignatureResult, CONFIDENCE_HIGH};
use crate::structures::wince::parse_wince_header;

/// Human readable description
pub const DESCRIPTION: &str = "Windows CE binary image";

/// Windows CE magic bytes
pub fn wince_magic() -> Vec<Vec<u8>> {
vec![b"B000FF\n".to_vec()]
}

/// Validates the Windows CE header
pub fn wince_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()
};

// Do an extraction dry-run
let dry_run = wince_dump(file_data, offset, None);

if dry_run.success {
if let Some(total_size) = dry_run.size {
result.size = total_size;

// Parse the WinCE header to get some useful info to display
if let Ok(wince_header) = parse_wince_header(&file_data[offset..]) {
result.description = format!(
"{}, base address: {:#X}, image size: {} bytes, file size: {} bytes",
result.description,
wince_header.base_address,
wince_header.image_size,
result.size
);
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 @@ -148,6 +148,7 @@ pub mod ubi;
pub mod uefi;
pub mod uimage;
pub mod vxworks;
pub mod wince;
pub mod xz;
pub mod yaffs;
pub mod zip;
Expand Down
53 changes: 53 additions & 0 deletions src/structures/wince.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use crate::structures::common::{self, StructureError};

/// Struct to store WindowsCE header info
#[derive(Debug, Default, Clone)]
pub struct WinCEHeader {
pub base_address: usize,
pub image_size: usize,
pub header_size: usize,
}

/// Parses a Windows CE header
pub fn parse_wince_header(wince_data: &[u8]) -> Result<WinCEHeader, StructureError> {
let wince_header_structure = vec![
("magic_p1", "u32"),
("magic_p2", "u24"),
("image_start", "u32"),
("image_size", "u32"),
];

// Parse the WinCE header
if let Ok(wince_header) = common::parse(wince_data, &wince_header_structure, "little") {
return Ok(WinCEHeader {
base_address: wince_header["image_start"],
image_size: wince_header["image_size"],
header_size: common::size(&wince_header_structure),
});
}

Err(StructureError)
}

/// Struct to store WindowsCE block info
#[derive(Debug, Default, Clone)]
pub struct WinCEBlock {
pub address: usize,
pub data_size: usize,
pub header_size: usize,
}

/// Parse a WindowsCE block header
pub fn parse_wince_block_header(block_data: &[u8]) -> Result<WinCEBlock, StructureError> {
let wince_block_structure = vec![("address", "u32"), ("size", "u32"), ("checksum", "u32")];

if let Ok(block_header) = common::parse(block_data, &wince_block_structure, "little") {
return Ok(WinCEBlock {
address: block_header["address"],
data_size: block_header["size"],
header_size: common::size(&wince_block_structure),
});
}

Err(StructureError)
}