Skip to content

Commit

Permalink
Merge pull request #733 from ReFirmLabs/dahua_zip
Browse files Browse the repository at this point in the history
Added support for Dahua ZIP archives
  • Loading branch information
devttys0 authored Nov 2, 2024
2 parents 7159259 + bceaa9c commit 516db49
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/extractors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ pub mod autel;
pub mod bzip2;
pub mod cab;
pub mod common;
pub mod dahua_zip;
pub mod dmg;
pub mod dtb;
pub mod dumpifs;
Expand Down
80 changes: 80 additions & 0 deletions src/extractors/dahua_zip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::signatures::zip::find_zip_eof;

/// Defines the internal extractor function for carving Dahua ZIP files
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::dahua_zip::dahua_zip_extractor;
///
/// match dahua_zip_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 dahua_zip_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(extract_dahua_zip),
..Default::default()
}
}

/// Carves out a Dahua ZIP file and converts it to a normal ZIP file
pub fn extract_dahua_zip(
file_data: &[u8],
offset: usize,
output_directory: Option<&String>,
) -> ExtractionResult {
const OUTFILE_NAME: &str = "dahua.zip";
const ZIP_HEADER: &[u8] = b"PK";

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

// Locate the end of the zip archive
if let Ok(zip_info) = find_zip_eof(file_data, offset) {
// Calculate total size of the zip archive, report success
result.size = Some(zip_info.eof - offset);
result.success = true;

// If extraction was requested, carve the zip archive to disk, replacing the Dahua ZIP magic bytes
// with the standard ZIP magic bytes.
if output_directory.is_some() {
// Start and end offsets of the data to carve
let start_data = offset + ZIP_HEADER.len();
let end_data = offset + result.size.unwrap();

let chroot = Chroot::new(output_directory);

// Get the data to carve
match file_data.get(start_data..end_data) {
None => {
result.success = false;
}
Some(zip_data) => {
// First write the normal ZIP header magic bytes to disk
if !chroot.create_file(OUTFILE_NAME, ZIP_HEADER) {
result.success = false;
} else {
// Append the rest of the ZIP archive to disk
result.success = chroot.append_to_file(OUTFILE_NAME, zip_data);
}
}
}
}
}

result
}
2 changes: 1 addition & 1 deletion src/extractors/gif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorTy
use crate::structures::common::StructureError;
use crate::structures::gif::{parse_gif_extension, parse_gif_header, parse_gif_image_descriptor};

/// Defines the internal extractor function for carving out JPEG images
/// Defines the internal extractor function for carving out GIF images
///
/// ```
/// use std::io::ErrorKind;
Expand Down
11 changes: 11 additions & 0 deletions src/magic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,17 @@ pub fn patterns() -> Vec<signatures::common::Signature> {
description: signatures::wince::DESCRIPTION.to_string(),
extractor: Some(extractors::wince::wince_extractor()),
},
// Dahua ZIP
signatures::common::Signature {
name: "dahua_zip".to_string(),
short: false,
magic_offset: 0,
always_display: false,
magic: signatures::dahua_zip::dahua_zip_magic(),
parser: signatures::dahua_zip::dahua_zip_parser,
description: signatures::dahua_zip::DESCRIPTION.to_string(),
extractor: Some(extractors::dahua_zip::dahua_zip_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 @@ -121,6 +121,7 @@ pub mod compressd;
pub mod copyright;
pub mod cpio;
pub mod cramfs;
pub mod dahua_zip;
pub mod deb;
pub mod dlob;
pub mod dmg;
Expand Down
27 changes: 27 additions & 0 deletions src/signatures/dahua_zip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use crate::signatures::common::{SignatureError, SignatureResult};
use crate::signatures::zip;

/// Human readable description
pub const DESCRIPTION: &str = "Dahua ZIP archive";

/// Dahua ZIP file entry magic bytes
pub fn dahua_zip_magic() -> Vec<Vec<u8>> {
// The first ZIP file entry in the Dahua ZIP file is has "DH" instead of "PK".
// Otherwise, it is a normal ZIP file.
vec![b"DH\x03\x04".to_vec()]
}

/// Validates a Dahua ZIP file entry signature
pub fn dahua_zip_parser(
file_data: &[u8],
offset: usize,
) -> Result<SignatureResult, SignatureError> {
// Parse & validate the Dahua ZIP file like a normal ZIP file
if let Ok(mut result) = zip::zip_parser(file_data, offset) {
// Replace the normal ZIP description string with our description string
result.description = result.description.replace(zip::DESCRIPTION, DESCRIPTION);
return Ok(result);
}

Err(SignatureError)
}
8 changes: 4 additions & 4 deletions src/signatures/zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ pub fn zip_parser(file_data: &[u8], offset: usize) -> Result<SignatureResult, Si
Err(SignatureError)
}

struct ZipEOCDInfo {
eof: usize,
file_count: usize,
pub struct ZipEOCDInfo {
pub eof: usize,
pub file_count: usize,
}

/// Need to grep the rest of the file data to locate the end-of-central-directory header, which tells us where the ZIP file ends.
fn find_zip_eof(file_data: &[u8], offset: usize) -> Result<ZipEOCDInfo, SignatureError> {
pub fn find_zip_eof(file_data: &[u8], offset: usize) -> Result<ZipEOCDInfo, SignatureError> {
// This magic string assumes that the disk_number and central_directory_disk_number are 0
const ZIP_EOCD_MAGIC: &[u8; 8] = b"PK\x05\x06\x00\x00\x00\x00";

Expand Down

0 comments on commit 516db49

Please sign in to comment.