1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use clap::{arg, command, ArgMatches};
use std::process;
use std::{env, path::PathBuf};
use ansi_term::Colour;
mod utils;
use utils::{file, search};
use crate::utils::{print_hexdump_output, PatternType};
pub fn setup_args() -> ArgMatches {
let integer_validator = |value: &str| match value.parse::<usize>() {
Ok(_) => Ok(()),
Err(_) => Err(String::from("the value needs to be a valid integer")),
};
command!()
.arg(
arg!(<PATTERN> "Ascii strings should be passed inside quotes like so '\"This is a string\"'
Escaping quotes '\"This is a \\\"quoted string\\\"\"'
All of these byte sequence are valid: f9b4ca, F9B4CA and f9B4Ca"
),
)
.arg(arg!(<FILE> ... "The filepath"))
.arg(
arg!(-f <filetype> ... "Filter the search by the file extensions.
Examples of input: jpg, mp3, exe")
.required(false),
)
.arg(
arg!(-c <context_bytes_size> "Defines the number of bytes that will be printed in each line.")
.required(false)
.default_value("16")
.validator(integer_validator),
)
.arg(
arg!(-p --"print-only" "Prints only the filename that contais the match.")
.id("print_only")
.requires("PATTERN"),
)
.arg(
arg!(-o --"print-offset" "Prints only the offsets of the match.")
.id("print_offset")
.requires("PATTERN"),
)
.arg(
arg!(-s --"skip-bytes" <n> "Skip n bytes before searching.")
.id("skip_bytes")
.required(false)
.default_value("0")
.validator(integer_validator),
)
.arg_required_else_help(true)
.get_matches()
}
pub fn run(args: ArgMatches) {
let filetypes: Vec<&str> = args.values_of("filetype").unwrap_or_default().collect();
let filepaths: Vec<PathBuf> = args.values_of_t("FILE").unwrap();
let files: Vec<PathBuf> = if filetypes.is_empty() {
file::get_all_files_from_paths(filepaths)
} else {
file::filter_filetypes(file::get_all_files_from_paths(filepaths), &filetypes)
};
let pattern: Vec<u8> = match PatternType::from(args.value_of("PATTERN").unwrap()) {
PatternType::Str(pattern) => pattern.into_bytes(),
PatternType::HexStr(pattern) => hex::decode(pattern).unwrap_or_else(|error| {
eprintln!("Error: {} in byte sequence!", error);
process::exit(1);
}),
};
let context_bytes_size: usize = args.value_of("context_bytes_size").unwrap().parse().unwrap();
let skip_bytes: u64 = args.value_of("skip_bytes").unwrap().parse().unwrap();
for filename in files {
let mut searcher = search::Searcher::new(&pattern, context_bytes_size, skip_bytes);
let filename = filename.to_str().unwrap();
let result = searcher.search_in_file(filename).unwrap_or_else(|error| {
eprintln!("{}: {}", filename, error);
process::exit(1);
});
if !result.is_empty() {
println!("{}", Colour::Purple.paint(filename));
}
if args.is_present("print_offset") {
result
.iter()
.map(|_match| _match.offset)
.for_each(|offset| println!("0x{:08X}", offset));
return;
}
if !args.is_present("print_only") {
print_hexdump_output(&result, context_bytes_size);
}
}
}