Skip to content

Commit fa3c81f

Browse files
committed
[WARP] Add more options to creating signatures in projects
1 parent f428bf5 commit fa3c81f

File tree

2 files changed

+216
-75
lines changed

2 files changed

+216
-75
lines changed

plugins/warp/src/plugin/project.rs

Lines changed: 129 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1+
use crate::processor::{
2+
new_processing_state_background_thread, CompressionTypeField, FileDataKindField,
3+
FileFilterField, WarpFileProcessor,
4+
};
5+
use crate::report::{ReportGenerator, ReportKindField};
16
use binaryninja::background_task::BackgroundTask;
27
use binaryninja::command::ProjectCommand;
38
use binaryninja::interaction::{Form, FormInputField};
9+
use binaryninja::project::folder::ProjectFolder;
410
use binaryninja::project::Project;
511
use binaryninja::rc::Ref;
612
use regex::Regex;
13+
use std::path::{Path, PathBuf};
714
use std::thread;
815
use std::time::Instant;
9-
10-
use crate::processor::{
11-
new_processing_state_background_thread, FileDataKindField, FileFilterField, WarpFileProcessor,
12-
};
13-
use crate::report::{ReportGenerator, ReportKindField};
16+
use warp::WarpFile;
1417

1518
pub struct CreateSignaturesForm {
1619
form: Form,
@@ -22,6 +25,9 @@ impl CreateSignaturesForm {
2225
form.add_field(Self::file_data_field());
2326
form.add_field(Self::file_filter_field());
2427
form.add_field(Self::generated_report_field());
28+
form.add_field(Self::compression_type_field());
29+
form.add_field(Self::save_individual_files_field());
30+
form.add_field(Self::skip_existing_warp_files_field());
2531
// TODO: Threads (we run the analysis in the background)
2632
Self { form }
2733
}
@@ -50,6 +56,48 @@ impl CreateSignaturesForm {
5056
ReportKindField::from_form(&self.form).unwrap_or_default()
5157
}
5258

59+
pub fn compression_type_field() -> FormInputField {
60+
CompressionTypeField::default().to_field()
61+
}
62+
63+
pub fn compression_type(&self) -> CompressionTypeField {
64+
CompressionTypeField::from_form(&self.form).unwrap_or_default()
65+
}
66+
67+
pub fn save_individual_files_field() -> FormInputField {
68+
FormInputField::Checkbox {
69+
prompt: "Save individual files".to_string(),
70+
default: None,
71+
value: false,
72+
}
73+
}
74+
75+
pub fn save_individual_files(&self) -> bool {
76+
let field = self.form.get_field_with_name("Save individual files");
77+
let field_value = field.and_then(|f| f.try_value_int()).unwrap_or(0);
78+
match field_value {
79+
1 => true,
80+
_ => false,
81+
}
82+
}
83+
84+
pub fn skip_existing_warp_files_field() -> FormInputField {
85+
FormInputField::Checkbox {
86+
prompt: "Skip existing WARP files".to_string(),
87+
default: Some(true),
88+
value: false,
89+
}
90+
}
91+
92+
pub fn skip_existing_warp_files(&self) -> bool {
93+
let field = self.form.get_field_with_name("Skip existing WARP files");
94+
let field_value = field.and_then(|f| f.try_value_int()).unwrap_or(0);
95+
match field_value {
96+
1 => true,
97+
_ => false,
98+
}
99+
}
100+
53101
pub fn prompt(&mut self) -> bool {
54102
self.form.prompt()
55103
}
@@ -65,47 +113,91 @@ impl CreateSignatures {
65113
}
66114
let file_data_kind = form.file_data_kind();
67115
let report_kind = form.generated_report_kind();
116+
let compression_type = form.compression_type();
117+
let save_individual_files = form.save_individual_files();
118+
119+
// Save the warp file to the project.
120+
let save_warp_file = move |project: &Project,
121+
folder: Option<&ProjectFolder>,
122+
name: &str,
123+
warp_file: &WarpFile| {
124+
if project
125+
.create_file(&warp_file.to_bytes(), folder, name, "")
126+
.is_err()
127+
{
128+
log::error!("Failed to create project file!");
129+
}
68130

69-
let mut processor = WarpFileProcessor::new().with_file_data(file_data_kind);
70-
71-
// This thread will show the state in a background task.
72-
let background_task = BackgroundTask::new("Processing started...", true);
73-
new_processing_state_background_thread(background_task.clone(), processor.state());
131+
let report = ReportGenerator::new();
132+
if let Some(generated) = report.report(&report_kind, &warp_file) {
133+
let ext = report.report_extension(&report_kind).unwrap_or_default();
134+
let file_name = format!("{}_report.{}", name, ext);
135+
if project
136+
.create_file(&generated.into_bytes(), folder, &file_name, "Warp file")
137+
.is_err()
138+
{
139+
log::error!("Failed to create project file!");
140+
}
141+
}
142+
};
143+
144+
// Optional callback for saving off the individual project files.
145+
let callback_project = project.clone();
146+
let save_individual_files_cb = move |path: &Path, file: &WarpFile| {
147+
if file.chunks.is_empty() {
148+
log::debug!("Skipping empty file: {}", path.display());
149+
return;
150+
}
151+
// The path returned will be the one on disk, so we will go and grab the project for it.
152+
let Some(project_file) = callback_project.file_by_path(path) else {
153+
log::error!("Failed to find project file for path: {}", path.display());
154+
return;
155+
};
156+
let project_file = project_file.to_owned();
157+
let file_name = format!("{}.warp", project_file.name());
158+
let project_folder = project_file.folder();
159+
save_warp_file(
160+
&callback_project,
161+
project_folder.as_deref(),
162+
&file_name,
163+
file,
164+
);
165+
};
166+
167+
let mut processor = WarpFileProcessor::new()
168+
.with_file_data(file_data_kind)
169+
.with_compression_type(compression_type);
170+
171+
if save_individual_files {
172+
processor = processor.with_processed_file_callback(save_individual_files_cb);
173+
}
74174

175+
// Construct the user-supplied file filter, we also have an appended filter for warp files.
176+
let mut filter = form.file_filter();
177+
// This checkbox is here as this is very common to filter for.
178+
// And we want to do it by default.
179+
if form.skip_existing_warp_files() {
180+
let warp_filter = Regex::new(".*\\.warp").unwrap();
181+
filter = match filter {
182+
Some(existing) => {
183+
let combined = format!("{}|.*\\.warp", existing.as_str());
184+
Some(Regex::new(&combined).unwrap())
185+
}
186+
None => Some(warp_filter),
187+
};
188+
}
75189
if let Some(filter) = form.file_filter() {
76190
processor = processor.with_file_filter(filter);
77191
}
78192

193+
// This thread will show the state in a background task.
194+
let background_task = BackgroundTask::new("Processing started...", true);
195+
new_processing_state_background_thread(background_task.clone(), processor.state());
196+
79197
let start = Instant::now();
80198
match processor.process_project(&project) {
81199
Ok(warp_file) => {
82-
// Print the processor string into the description of the file, so we know how it was generated.
83-
let processor_str = format!("{:#?}", &processor);
84-
85-
// TODO: File name needs to be configurable.
86-
if project
87-
.create_file(
88-
&warp_file.to_bytes(),
89-
None,
90-
"generated.warp",
91-
&processor_str,
92-
)
93-
.is_err()
94-
{
95-
log::error!("Failed to create project file!");
96-
}
97-
98-
let report = ReportGenerator::new();
99-
if let Some(generated) = report.report(&report_kind, &warp_file) {
100-
let ext = report.report_extension(&report_kind).unwrap_or_default();
101-
let file_name = format!("report.{}", ext);
102-
if project
103-
.create_file(&generated.into_bytes(), None, &file_name, "Warp file")
104-
.is_err()
105-
{
106-
log::error!("Failed to create project file!");
107-
}
108-
}
200+
save_warp_file(&project, None, "generated.warp", &warp_file);
109201
}
110202
Err(e) => {
111203
log::error!("Failed to process project: {}", e);

0 commit comments

Comments
 (0)