Skip to content

Commit

Permalink
Removed API breakage
Browse files Browse the repository at this point in the history
  • Loading branch information
devttys0 committed Nov 30, 2024
1 parent 27eb641 commit 052c318
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 57 deletions.
92 changes: 75 additions & 17 deletions src/binwalk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::os::windows;
#[cfg(unix)]
use std::os::unix;

use crate::common::is_offset_safe;
use crate::common::{is_offset_safe, read_file};
use crate::extractors;
use crate::magic;
use crate::signatures;
Expand Down Expand Up @@ -601,9 +601,10 @@ impl Binwalk {
pub fn extract(
&self,
file_data: &[u8],
file_path: &String,
file_name: impl Into<String>,
file_map: &Vec<signatures::common::SignatureResult>,
) -> HashMap<String, extractors::common::ExtractionResult> {
let file_path = file_name.into();
let mut extraction_results: HashMap<String, extractors::common::ExtractionResult> =
HashMap::new();

Expand All @@ -622,7 +623,7 @@ impl Binwalk {
Some(_) => {
// Run an extraction for this signature
let mut extraction_result =
extractors::common::execute(file_data, file_path, signature, &extractor);
extractors::common::execute(file_data, &file_path, signature, &extractor);

if !extraction_result.success {
debug!(
Expand Down Expand Up @@ -653,7 +654,7 @@ impl Binwalk {
// Re-run the extraction
extraction_result = extractors::common::execute(
file_data,
file_path,
&file_path,
&new_signature,
&extractor,
);
Expand All @@ -669,12 +670,12 @@ impl Binwalk {
extraction_results
}

/// Analyze a file and optionally extract the file contents.
/// Analyze a data buffer and optionally extract the file contents.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_binwalk_rs_624_0() -> Result<binwalk::Binwalk, binwalk::BinwalkError> {
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_binwalk_rs_672_0() -> Result<binwalk::Binwalk, binwalk::BinwalkError> {
/// use binwalk::{Binwalk, common};
///
/// let target_path = std::path::Path::new("tests")
Expand All @@ -688,7 +689,7 @@ impl Binwalk {
/// .display()
/// .to_string();
///
/// let file_data = common::read_file(&target_path, false).expect("Failed to read file data");
/// let file_data = common::read_file(&target_path).expect("Failed to read file data");
///
/// # std::fs::remove_dir_all(&extraction_directory);
/// let binwalker = Binwalk::configure(Some(target_path),
Expand All @@ -698,7 +699,7 @@ impl Binwalk {
/// None,
/// false)?;
///
/// let analysis_results = binwalker.analyze(&file_data, &binwalker.base_target_file, true);
/// let analysis_results = binwalker.analyze_buf(&file_data, &binwalker.base_target_file, true);
///
/// assert_eq!(analysis_results.file_map.len(), 1);
/// assert_eq!(analysis_results.extractions.len(), 1);
Expand All @@ -709,22 +710,24 @@ impl Binwalk {
/// .exists(), true);
/// # std::fs::remove_dir_all(&extraction_directory);
/// # Ok(binwalker)
/// # } _doctest_main_src_binwalk_rs_624_0(); }
/// # } _doctest_main_src_binwalk_rs_672_0(); }
/// ```
pub fn analyze(
pub fn analyze_buf(
&self,
file_data: &[u8],
target_file: &String,
target_file: impl Into<String>,
do_extraction: bool,
) -> AnalysisResults {
let file_path = target_file.into();

// Return value
let mut results: AnalysisResults = AnalysisResults {
file_path: target_file.clone(),
file_path: file_path.clone(),
..Default::default()
};

// Scan file data for signatures
debug!("Analysis start: {}", target_file);
debug!("Analysis start: {}", file_path);
results.file_map = self.scan(file_data);

// Only extract if told to, and if there were some signatures found in this file
Expand All @@ -734,19 +737,74 @@ impl Binwalk {
"Submitting {} signature results to extractor",
results.file_map.len()
);
results.extractions = self.extract(file_data, target_file, &results.file_map);
results.extractions = self.extract(file_data, &file_path, &results.file_map);
}

debug!("Analysis end: {}", target_file);
debug!("Analysis end: {}", file_path);

results
}

/// Analyze a file on disk and optionally extract its contents.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_binwalk_rs_745_0() -> Result<binwalk::Binwalk, binwalk::BinwalkError> {
/// use binwalk::Binwalk;
///
/// let target_path = std::path::Path::new("tests")
/// .join("inputs")
/// .join("gzip.bin")
/// .display()
/// .to_string();
///
/// let extraction_directory = std::path::Path::new("tests")
/// .join("extractions")
/// .display()
/// .to_string();
///
/// # std::fs::remove_dir_all(&extraction_directory);
/// let binwalker = Binwalk::configure(Some(target_path),
/// Some(extraction_directory.clone()),
/// None,
/// None,
/// None,
/// false)?;
///
/// let analysis_results = binwalker.analyze(&binwalker.base_target_file, true);
///
/// assert_eq!(analysis_results.file_map.len(), 1);
/// assert_eq!(analysis_results.extractions.len(), 1);
/// assert_eq!(std::path::Path::new(&extraction_directory)
/// .join("gzip.bin.extracted")
/// .join("0")
/// .join("decompressed.bin")
/// .exists(), true);
/// # std::fs::remove_dir_all(&extraction_directory);
/// # Ok(binwalker)
/// # } _doctest_main_src_binwalk_rs_745_0(); }
/// ```
#[allow(dead_code)]
pub fn analyze(&self, target_file: impl Into<String>, do_extraction: bool) -> AnalysisResults {
let file_path = target_file.into();

let file_data = match read_file(&file_path) {
Err(_) => {
error!("Failed to read data from {}", file_path);
b"".to_vec()
}
Ok(data) => data,
};

self.analyze_buf(&file_data, &file_path, do_extraction)
}
}

/// Initializes the extraction output directory
fn init_extraction_directory(
target_file: &String,
extraction_directory: &String,
target_file: &str,
extraction_directory: &str,
) -> Result<String, std::io::Error> {
// Create the output directory, equivalent of mkdir -p
match fs::create_dir_all(extraction_directory) {
Expand Down
78 changes: 51 additions & 27 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,75 @@ use log::{debug, error};
use std::fs::File;
use std::io::Read;

/// Read a file into memory and return its contents.
/// Read a data into memory, either from disk or from stdin, and return its contents.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_common_rs_11_0() -> Result<(), Box<dyn std::error::Error>> {
/// use binwalk::common::read_file;
/// use binwalk::common::read_input;
///
/// let file_data = read_file("/etc/passwd", false)?;
/// let file_data = read_input("/etc/passwd", false)?;
/// assert!(file_data.len() > 0);
/// # Ok(())
/// # } _doctest_main_src_common_rs_11_0(); }
/// ```
pub fn read_file(file: impl Into<String>, stdin: bool) -> Result<Vec<u8>, std::io::Error> {
pub fn read_input(file: impl Into<String>, stdin: bool) -> Result<Vec<u8>, std::io::Error> {
if stdin {
read_stdin()
} else {
read_file(file)
}
}

/// Read data from standard input and return its contents.
pub fn read_stdin() -> Result<Vec<u8>, std::io::Error> {
let mut stdin_data = Vec::new();

match std::io::stdin().read_to_end(&mut stdin_data) {
Err(e) => {
error!("Failed to read data from stdin: {}", e);
Err(e)
}
Ok(nbytes) => {
debug!("Loaded {} bytes from stdin", nbytes);
Ok(stdin_data)
}
}
}

/// Read a file data into memory and return its contents.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_common_rs_48_0() -> Result<(), Box<dyn std::error::Error>> {
/// use binwalk::common::read_file;
///
/// let file_data = read_file("/etc/passwd")?;
/// assert!(file_data.len() > 0);
/// # Ok(())
/// # } _doctest_main_src_common_rs_48_0(); }
/// ```
pub fn read_file(file: impl Into<String>) -> Result<Vec<u8>, std::io::Error> {
let mut file_data = Vec::new();
let file_path = file.into();

if stdin {
match std::io::stdin().read_to_end(&mut file_data) {
match File::open(&file_path) {
Err(e) => {
error!("Failed to open file {}: {}", file_path, e);
Err(e)
}
Ok(mut fp) => match fp.read_to_end(&mut file_data) {
Err(e) => {
error!("Failed to read data from stdin: {}", e);
error!("Failed to read file {} into memory: {}", file_path, e);
Err(e)
}
Ok(nbytes) => {
debug!("Loaded {} bytes from stdin", nbytes);
Ok(file_size) => {
debug!("Loaded {} bytes from {}", file_size, file_path);
Ok(file_data)
}
}
} else {
match File::open(&file_path) {
Err(e) => {
error!("Failed to open file {}: {}", file_path, e);
Err(e)
}
Ok(mut fp) => match fp.read_to_end(&mut file_data) {
Err(e) => {
error!("Failed to read file {} into memory: {}", file_path, e);
Err(e)
}
Ok(file_size) => {
debug!("Loaded {} bytes from {}", file_size, file_path);
Ok(file_data)
}
},
}
},
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/entropy.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::common::read_file;
use crate::common::read_input;
use entropy::shannon_entropy;
use log::error;
use plotters::prelude::*;
Expand Down Expand Up @@ -87,7 +87,7 @@ pub fn plot(file_path: impl Into<String>, stdin: bool) -> Result<FileEntropy, En
}

// Read in the target file data
if let Ok(file_data) = read_file(&target_file, stdin) {
if let Ok(file_data) = read_input(&target_file, stdin) {
let mut points: Vec<(i32, i32)> = vec![];

// Calculate the entropy for each file block
Expand Down
10 changes: 4 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,8 @@ fn main() {

json_logger.close();

// If BINWALK_RM_SYMLINK env var was set, or if data was read from stdin, delete the base_target_file symlink
if (cliargs.carve || cliargs.extract)
&& (cliargs.stdin || std::env::var(BINWALK_RM_SYMLINK).is_ok())
{
// If BINWALK_RM_SYMLINK env var was set, delete the base_target_file symlink
if (cliargs.carve || cliargs.extract) && std::env::var(BINWALK_RM_SYMLINK).is_ok() {
if let Err(e) = std::fs::remove_file(&binwalker.base_target_file) {
error!(
"Request to remove extraction symlink file {} failed: {}",
Expand Down Expand Up @@ -291,7 +289,7 @@ fn spawn_worker(
) {
pool.execute(move || {
// Read in file data
let file_data = match common::read_file(&target_file, stdin) {
let file_data = match common::read_input(&target_file, stdin) {
Err(_) => {
error!("Failed to read {} data", target_file);
b"".to_vec()
Expand All @@ -300,7 +298,7 @@ fn spawn_worker(
};

// Analyze target file, with extraction, if specified
let results = bw.analyze(&file_data, &target_file, do_extraction);
let results = bw.analyze_buf(&file_data, &target_file, do_extraction);

// If data carving was requested as part of extraction, carve analysis results to disk
if do_carve {
Expand Down
7 changes: 2 additions & 5 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use binwalk::{common, AnalysisResults, Binwalk};
use binwalk::{AnalysisResults, Binwalk};

/// Convenience function for running an integration test against the specified file, with the provided signature filter.
/// Assumes that there will be one signature result and one extraction result at file offset 0.
Expand Down Expand Up @@ -55,9 +55,6 @@ pub fn run_binwalk(signature_filter: &str, file_name: &str) -> AnalysisResults {
// Delete the output directory, if it exists
let _ = std::fs::remove_dir_all(&output_directory);

// Read in file data
let file_data = common::read_file(&file_path, false).expect("Failed to read test file");

// Configure binwalk
let binwalker = Binwalk::configure(
Some(file_path),
Expand All @@ -70,7 +67,7 @@ pub fn run_binwalk(signature_filter: &str, file_name: &str) -> AnalysisResults {
.expect("Binwalk initialization failed");

// Run analysis
let results = binwalker.analyze(&file_data, &binwalker.base_target_file, true);
let results = binwalker.analyze(&binwalker.base_target_file, true);

// Clean up the output directory
let _ = std::fs::remove_dir_all(output_directory);
Expand Down

0 comments on commit 052c318

Please sign in to comment.