Skip to content

Commit

Permalink
Merge pull request #753 from ReFirmLabs/keep_carved_files
Browse files Browse the repository at this point in the history
Carve file data to disk
  • Loading branch information
devttys0 authored Nov 21, 2024
2 parents 1cb781f + 11df2c6 commit bd55cfd
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 0 deletions.
85 changes: 85 additions & 0 deletions src/binwalk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,91 @@ impl Binwalk {

results
}

/// Carve signatures identified during analysis to separate files on disk.
/// Returns the number of carved files created.
/// Note that unknown blocks of file data are also carved to disk, so the number of files
/// created may be larger than the number of results defined in results.file_map.
pub fn carve(&self, results: &AnalysisResults) -> usize {
let mut carve_count: usize = 0;
let mut last_known_offset: usize = 0;
let mut unknown_bytes: Vec<(usize, usize)> = Vec::new();

// No results, don't do anything
if !results.file_map.is_empty() {
// Read in the source file
if let Ok(file_data) = read_file(&results.file_path) {
// Loop through all identified signatures in the file
for signature_result in &results.file_map {
// If there is data between the last signature and this signature, it is some chunk of unknown data
if signature_result.offset > last_known_offset {
unknown_bytes.push((
last_known_offset,
signature_result.offset - last_known_offset,
));
}

// Carve this signature's data to disk
if carve_file_data_to_disk(
&results.file_path,
&file_data,
&signature_result.name,
signature_result.offset,
signature_result.size,
) {
carve_count += 1;
}

// Update the last known offset to the end of this signature's data
last_known_offset = signature_result.offset + signature_result.size;
}

// All known signature data has been carved to disk, now carve any unknown blocks of data to disk
for (offset, size) in unknown_bytes {
if carve_file_data_to_disk(
&results.file_path,
&file_data,
"unknown",
offset,
size,
) {
carve_count += 1;
}
}
}
}

carve_count
}
}

/// Carves a block of file data to a new file on disk
fn carve_file_data_to_disk(
source_file_path: &str,
file_data: &[u8],
name: &str,
offset: usize,
size: usize,
) -> bool {
let chroot = extractors::common::Chroot::new(None);

// Carved file path will be: <source file path>_<offset>_<name>.raw
let carved_file_path = format!("{}_{}_{}.raw", source_file_path, offset, name,);

debug!("Carving {}", carved_file_path);

// Carve the data to disk
if !chroot.carve_file(&carved_file_path, file_data, offset, size) {
error!(
"Failed to carve {} [{:#X}..{:#X}] to disk",
carved_file_path,
offset,
offset + size,
);
return false;
}

true
}

/// Initializes the extraction output directory
Expand Down
4 changes: 4 additions & 0 deletions src/cliparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ pub struct CliArgs {
#[arg(short, long)]
pub extract: bool,

/// Carve raw file data to disk during extraction
#[arg(short, long)]
pub carve: bool,

/// Recursively scan extracted files
#[arg(short = 'M', long)]
pub matryoshka: bool,
Expand Down
8 changes: 8 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ fn main() {
binwalker.clone(),
target_file,
cliargs.extract,
cliargs.carve,
worker_tx.clone(),
);
}
Expand Down Expand Up @@ -267,11 +268,18 @@ fn spawn_worker(
bw: binwalk::Binwalk,
target_file: String,
do_extraction: bool,
do_carve: bool,
worker_tx: mpsc::Sender<AnalysisResults>,
) {
pool.execute(move || {
// Analyze target file, with extraction, if specified
let results = bw.analyze(&target_file, do_extraction);

// If data carving was requested as part of extraction, carve analysis results to disk
if do_extraction && do_carve {
let _ = bw.carve(&results);
}

// Report file results back to main thread
if let Err(e) = worker_tx.send(results) {
panic!(
Expand Down

0 comments on commit bd55cfd

Please sign in to comment.