diff --git a/src/extractors.rs b/src/extractors.rs index 87b19ff52..7942f7e7b 100644 --- a/src/extractors.rs +++ b/src/extractors.rs @@ -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; diff --git a/src/extractors/dahua_zip.rs b/src/extractors/dahua_zip.rs new file mode 100644 index 000000000..7a13ca1d6 --- /dev/null +++ b/src/extractors/dahua_zip.rs @@ -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 +} diff --git a/src/extractors/gif.rs b/src/extractors/gif.rs index 0ec4beaa8..d92a069a6 100644 --- a/src/extractors/gif.rs +++ b/src/extractors/gif.rs @@ -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; diff --git a/src/magic.rs b/src/magic.rs index a3336104c..5fbe1b81a 100644 --- a/src/magic.rs +++ b/src/magic.rs @@ -943,6 +943,17 @@ pub fn patterns() -> Vec { 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 diff --git a/src/signatures.rs b/src/signatures.rs index 0fbc596e8..51d322251 100644 --- a/src/signatures.rs +++ b/src/signatures.rs @@ -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; diff --git a/src/signatures/dahua_zip.rs b/src/signatures/dahua_zip.rs new file mode 100644 index 000000000..cba58fe00 --- /dev/null +++ b/src/signatures/dahua_zip.rs @@ -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> { + // 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 { + // 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) +} diff --git a/src/signatures/zip.rs b/src/signatures/zip.rs index abefea286..812827704 100644 --- a/src/signatures/zip.rs +++ b/src/signatures/zip.rs @@ -36,13 +36,13 @@ pub fn zip_parser(file_data: &[u8], offset: usize) -> Result Result { +pub fn find_zip_eof(file_data: &[u8], offset: usize) -> Result { // 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";