diff --git a/Cargo.lock b/Cargo.lock index b7969da81..a4d2af845 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "aho-corasick" version = "1.1.3" @@ -103,6 +109,7 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" name = "binwalk" version = "3.1.1" dependencies = [ + "adler32", "aho-corasick", "base64", "bzip2", diff --git a/Cargo.toml b/Cargo.toml index 06d2cd3bc..ae2819fee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ threadpool = "1.8.1" serde_json = "1.0" env_logger = "0.11.5" flate2 = "1.0.34" +adler32 = "1.2.0" md5 = "0.7.0" miniz_oxide = "0.8.0" aho-corasick = "1.1.3" diff --git a/src/extractors/gzip.rs b/src/extractors/gzip.rs index b7a3004c0..994c273e8 100644 --- a/src/extractors/gzip.rs +++ b/src/extractors/gzip.rs @@ -37,18 +37,24 @@ pub fn gzip_decompress( offset: usize, output_directory: Option<&String>, ) -> ExtractionResult { + let mut exresult = ExtractionResult { + ..Default::default() + }; + // Parse the gzip header if let Ok(gzip_header) = parse_gzip_header(&file_data[offset..]) { // Deflate compressed data starts at the end of the gzip header let deflate_data_start: usize = offset + gzip_header.size; if file_data.len() > deflate_data_start { - return inflate::inflate_decompressor(file_data, deflate_data_start, output_directory); + let inflate_result = + inflate::inflate_decompressor(file_data, deflate_data_start, output_directory); + if inflate_result.success { + exresult.success = true; + exresult.size = Some(inflate_result.size); + } } } - // Return failure - ExtractionResult { - ..Default::default() - } + exresult } diff --git a/src/extractors/inflate.rs b/src/extractors/inflate.rs index 1a773af82..648406eac 100644 --- a/src/extractors/inflate.rs +++ b/src/extractors/inflate.rs @@ -1,33 +1,32 @@ -use crate::extractors::common::{Chroot, ExtractionResult}; +use crate::extractors::common::Chroot; +use adler32::RollingAdler32; use flate2::bufread::DeflateDecoder; use std::io::Read; -/* - * The inflate_decompressor extractor is currently not directly used by any signature definitions. - * -use crate::extractors::common::{ Extractor, ExtractorType }; - -// Defines the internal extractor function for decompressing raw deflate data -pub fn inflate_extractor() -> Extractor { - return Extractor { utility: ExtractorType::Internal(inflate_decompressor), ..Default::default() }; +#[derive(Debug, Default, Clone)] +pub struct DeflateResult { + pub size: usize, + pub adler32: u32, + pub success: bool, } -*/ -/// Internal extractor for inflating deflated data. +/// Decompressor for inflating deflated data. +/// For internal use, does not conform to the standard extractor format. pub fn inflate_decompressor( file_data: &[u8], offset: usize, output_directory: Option<&String>, -) -> ExtractionResult { +) -> DeflateResult { // Size of decompression buffer const BLOCK_SIZE: usize = 8192; // Output file for decompressed data const OUTPUT_FILE_NAME: &str = "decompressed.bin"; - let mut result = ExtractionResult { + let mut result = DeflateResult { ..Default::default() }; + let mut adler32_checksum = RollingAdler32::new(); let mut decompressed_buffer = [0; BLOCK_SIZE]; let mut decompressor = DeflateDecoder::new(&file_data[offset..]); @@ -49,12 +48,16 @@ pub fn inflate_decompressor( break; } Ok(n) => { - // Decompressed a block of data, if extraction was requested write the decompressed block to the output file - if n > 0 && output_directory.is_some() { - let chroot = Chroot::new(output_directory); - if !chroot.append_to_file(OUTPUT_FILE_NAME, &decompressed_buffer[0..n]) { - // If writing data to file fails, break - break; + // Decompressed a block of data, update checksum and if extraction was requested write the decompressed block to the output file + if n > 0 { + adler32_checksum.update_buffer(&decompressed_buffer[0..n]); + + if output_directory.is_some() { + let chroot = Chroot::new(output_directory); + if !chroot.append_to_file(OUTPUT_FILE_NAME, &decompressed_buffer[0..n]) { + // If writing data to file fails, break + break; + } } } @@ -63,7 +66,8 @@ pub fn inflate_decompressor( // If some data was actually decompressed, report success and the number of input bytes consumed if decompressor.total_out() > 0 { result.success = true; - result.size = Some(decompressor.total_in() as usize); + result.adler32 = adler32_checksum.hash(); + result.size = decompressor.total_in() as usize; } // Nothing else to do, break diff --git a/src/extractors/zlib.rs b/src/extractors/zlib.rs index e6d99bd95..88c9e6bcd 100644 --- a/src/extractors/zlib.rs +++ b/src/extractors/zlib.rs @@ -42,15 +42,31 @@ pub fn zlib_decompress( // Size of the zlib header const HEADER_SIZE: usize = 2; + let mut exresult = ExtractionResult { + ..Default::default() + }; + // Do the decompression, ignoring the ZLIB header - let mut result = + let inflate_result = inflate::inflate_decompressor(file_data, offset + HEADER_SIZE, output_directory); - // If the decompression reported the size of the deflate data, update the reported size - // to include the ZLIB header and checksum fields - if let Some(deflate_size) = result.size { - result.size = Some(HEADER_SIZE + deflate_size + CHECKSUM_SIZE); + // Check that the data decompressed OK + if inflate_result.success { + // Calculate the ZLIB checksum offsets + let checksum_start = offset + HEADER_SIZE + inflate_result.size; + let checksum_end = checksum_start + CHECKSUM_SIZE; + + // Get the ZLIB checksum + if let Some(adler32_checksum_bytes) = file_data.get(checksum_start..checksum_end) { + let reported_checksum = u32::from_be_bytes(adler32_checksum_bytes.try_into().unwrap()); + + // Make sure the checksum matches + if reported_checksum == inflate_result.adler32 { + exresult.success = true; + exresult.size = Some(HEADER_SIZE + inflate_result.size + CHECKSUM_SIZE); + } + } } - result + exresult }