1+ use crate :: processor:: {
2+ new_processing_state_background_thread, CompressionTypeField , FileDataKindField ,
3+ FileFilterField , WarpFileProcessor ,
4+ } ;
5+ use crate :: report:: { ReportGenerator , ReportKindField } ;
16use binaryninja:: background_task:: BackgroundTask ;
27use binaryninja:: command:: ProjectCommand ;
38use binaryninja:: interaction:: { Form , FormInputField } ;
9+ use binaryninja:: project:: folder:: ProjectFolder ;
410use binaryninja:: project:: Project ;
511use binaryninja:: rc:: Ref ;
612use regex:: Regex ;
13+ use std:: path:: { Path , PathBuf } ;
714use std:: thread;
815use 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
1518pub 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