diff --git a/Cargo.lock b/Cargo.lock index bc5a0e349..cd0779529 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "ar" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69" + [[package]] name = "arbitrary" version = "1.4.1" @@ -170,6 +176,7 @@ version = "3.1.1" dependencies = [ "adler32", "aho-corasick", + "ar", "base64", "bzip2 0.4.4", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 4aa374917..44fc087b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ description = "Analyzes data for embedded file types" keywords = ["binwalk", "firmware", "analysis"] [dependencies] +ar = "0.9" log = "0.4.22" base64 = "0.22.1" chrono = "0.4.38" @@ -29,7 +30,7 @@ adler32 = "1.2.0" md5 = "0.7.0" miniz_oxide = "0.8.0" aho-corasick = "1.1.3" -serde = { version = "1.0", features = ["derive"]} +serde = { version = "1.0", features = ["derive"] } clap = { version = "4.5.16", features = ["derive"] } xxhash-rust = { version = "0.8.12", features = ["xxh32"] } hex = "0.4.3" diff --git a/src/extractors.rs b/src/extractors.rs index 4810a1d11..fe6762bd9 100644 --- a/src/extractors.rs +++ b/src/extractors.rs @@ -141,6 +141,7 @@ pub mod androidsparse; pub mod apfs; +pub mod ar; pub mod arcadyan; pub mod autel; pub mod bmp; diff --git a/src/extractors/ar.rs b/src/extractors/ar.rs new file mode 100644 index 000000000..1a2bf236b --- /dev/null +++ b/src/extractors/ar.rs @@ -0,0 +1,63 @@ +use std::io::{Cursor, Read}; + +use crate::extractors::{ + self, + common::{Chroot, ExtractionResult}, +}; + +/// Describes how to run the ar utility to extract GNU/BSD/DEB archives +/// +/// ``` +/// use std::io::ErrorKind; +/// use std::process::Command; +/// use binwalk::extractors::common::ExtractorType; +/// use binwalk::extractors::deb::deb_extractor; +/// +/// match ar_extractor().utility { +/// ExtractorType::Internal(func) => func(file_data, offset, Some(output_directory)), +/// _ => unreachable!("Invalid extractor type"), +/// } +/// ``` +pub fn ar_extractor() -> extractors::common::Extractor { + extractors::common::Extractor { + utility: extractors::common::ExtractorType::Internal(extract_ar_file), + extension: "deb".to_string(), + do_not_recurse: false, + ..Default::default() + } +} + +pub fn extract_ar_file( + file_data: &[u8], + offset: usize, + output_directory: Option<&str>, +) -> ExtractionResult { + let mut result = ExtractionResult { + success: true, + size: Some(0), + do_not_recurse: false, + ..Default::default() + }; + + let mut reader = Cursor::new(file_data); + reader.set_position(offset as u64); + let mut archive = ar::Archive::new(reader); + while let Some(entry_result) = archive.next_entry() { + if let Ok(mut entry) = entry_result { + if !output_directory.is_none() { + let chroot = Chroot::new(output_directory); + + // it would be nicer if Chroot::create_file were to take a impl Cursor instead + // of a Vec, then we would use less memory: + let mut data = vec![]; + if let Ok(size) = entry.read_to_end(&mut data) { + result.size.as_mut().map(|x| *x += size); + result.success &= chroot + .create_file(String::from_utf8_lossy(entry.header().identifier()), &data); + } + } + } + } + + result +} diff --git a/src/magic.rs b/src/magic.rs index ed316f5eb..c1b8e8927 100644 --- a/src/magic.rs +++ b/src/magic.rs @@ -24,7 +24,7 @@ pub fn patterns() -> Vec { magic: signatures::deb::deb_magic(), parser: signatures::deb::deb_parser, description: signatures::deb::DESCRIPTION.to_string(), - extractor: None, + extractor: Some(extractors::ar::ar_extractor()), }, // 7-zip signatures::common::Signature {