Skip to content

Commit 38a175c

Browse files
rbranemesare
authored andcommitted
Implement custom interactive handler in Rust API
1 parent bfbf771 commit 38a175c

File tree

11 files changed

+1695
-519
lines changed

11 files changed

+1695
-519
lines changed

plugins/dwarf/dwarf_export/src/lib.rs

Lines changed: 85 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,26 @@
11
mod edit_distance;
22

3-
use gimli::{
4-
constants,
5-
write::{
6-
Address, AttributeValue, DwarfUnit, EndianVec, Expression, Range, RangeList, Sections,
7-
UnitEntryId,
8-
},
9-
};
10-
use object::{write, Architecture, BinaryFormat, SectionKind};
11-
use std::fs;
12-
3+
use binaryninja::interaction::form::{Form, FormInputField};
134
use binaryninja::logger::Logger;
145
use binaryninja::{
156
binary_view::{BinaryView, BinaryViewBase, BinaryViewExt},
167
command::{register_command, Command},
178
confidence::Conf,
18-
interaction,
19-
interaction::{FormResponses, FormResponses::Index},
209
rc::Ref,
2110
symbol::SymbolType,
2211
types::{MemberAccess, StructureType, Type, TypeClass},
2312
};
13+
use gimli::{
14+
constants,
15+
write::{
16+
Address, AttributeValue, DwarfUnit, EndianVec, Expression, Range, RangeList, Sections,
17+
UnitEntryId,
18+
},
19+
};
2420
use log::{error, info, LevelFilter};
21+
use object::{write, Architecture, BinaryFormat, SectionKind};
22+
use std::fs;
23+
use std::path::{Path, PathBuf};
2524

2625
fn export_type(
2726
name: String,
@@ -598,10 +597,11 @@ fn export_data_vars(
598597
}
599598
}
600599

601-
fn present_form(bv_arch: &str) -> Vec<FormResponses> {
602-
// TODO : Verify inputs (like save location) so that we can fail early
603-
// TODO : Add Language field
604-
// TODO : Choose to export types/functions/etc
600+
// TODO : Verify inputs (like save location) so that we can fail early
601+
// TODO : Add Language field
602+
// TODO : Choose to export types/functions/etc
603+
// TODO: Add actual / better support for formats other than elf?
604+
fn create_export_form(bv_arch: &str) -> Form {
605605
let archs = [
606606
"Unknown",
607607
"AArch64",
@@ -627,80 +627,37 @@ fn present_form(bv_arch: &str) -> Vec<FormResponses> {
627627
"Wasm32",
628628
"Xtensa",
629629
];
630-
interaction::FormInputBuilder::new()
631-
.save_file_field(
632-
"Save Location",
633-
Some("Debug Files (*.dwo *.debug);;All Files (*)"),
634-
None,
635-
None,
636-
)
637-
.choice_field(
638-
"Architecture",
639-
&archs,
640-
archs
641-
.iter()
642-
.enumerate()
643-
.min_by(|&(_, arch_name_1), &(_, arch_name_2)| {
644-
edit_distance::distance(bv_arch, arch_name_1)
645-
.cmp(&edit_distance::distance(bv_arch, arch_name_2))
646-
})
647-
.map(|(index, _)| index),
648-
)
649-
// Add actual / better support for formats other than elf?
650-
// .choice_field(
651-
// "Container Format",
652-
// &["Coff", "Elf", "MachO", "Pe", "Wasm", "Xcoff"],
653-
// None,
654-
// )
655-
.get_form_input("Export as DWARF")
630+
631+
let mut form = Form::new("Export as DWARF");
632+
form.add_field(FormInputField::SaveFileName {
633+
prompt: "Save Location".to_string(),
634+
extension: Some("Debug Files (*.dwo *.debug);;All Files (*)".to_string()),
635+
default_name: None,
636+
default: None,
637+
value: None,
638+
});
639+
form.add_field(FormInputField::Choice {
640+
prompt: "Architecture".to_string(),
641+
choices: archs.iter().map(|arch| arch.to_string()).collect(),
642+
default: archs
643+
.iter()
644+
.enumerate()
645+
.min_by(|&(_, arch_name_1), &(_, arch_name_2)| {
646+
edit_distance::distance(bv_arch, arch_name_1)
647+
.cmp(&edit_distance::distance(bv_arch, arch_name_2))
648+
})
649+
.map(|(index, _)| index),
650+
value: 0,
651+
});
652+
form
656653
}
657654

658655
fn write_dwarf<T: gimli::Endianity>(
659-
responses: Vec<FormResponses>,
656+
file_path: &Path,
657+
arch: Architecture,
660658
endian: T,
661659
dwarf: &mut DwarfUnit,
662660
) {
663-
if responses.len() < 2 {
664-
return;
665-
}
666-
667-
let arch = match responses[1] {
668-
Index(0) => Architecture::Unknown,
669-
Index(1) => Architecture::Aarch64,
670-
Index(2) => Architecture::Aarch64_Ilp32,
671-
Index(3) => Architecture::Arm,
672-
Index(4) => Architecture::Avr,
673-
Index(5) => Architecture::Bpf,
674-
Index(6) => Architecture::I386,
675-
Index(7) => Architecture::X86_64,
676-
Index(8) => Architecture::X86_64_X32,
677-
Index(9) => Architecture::Hexagon,
678-
Index(10) => Architecture::LoongArch64,
679-
Index(11) => Architecture::Mips,
680-
Index(12) => Architecture::Mips64,
681-
Index(13) => Architecture::Msp430,
682-
Index(14) => Architecture::PowerPc,
683-
Index(15) => Architecture::PowerPc64,
684-
Index(16) => Architecture::Riscv32,
685-
Index(17) => Architecture::Riscv64,
686-
Index(18) => Architecture::S390x,
687-
Index(19) => Architecture::Sbf,
688-
Index(20) => Architecture::Sparc64,
689-
Index(21) => Architecture::Wasm32,
690-
Index(22) => Architecture::Xtensa,
691-
_ => Architecture::Unknown,
692-
};
693-
694-
// let format = match responses[2] {
695-
// Index(0) => BinaryFormat::Coff,
696-
// Index(1) => BinaryFormat::Elf,
697-
// Index(2) => BinaryFormat::MachO,
698-
// Index(3) => BinaryFormat::Pe,
699-
// Index(4) => BinaryFormat::Wasm,
700-
// Index(5) => BinaryFormat::Xcoff,
701-
// _ => BinaryFormat::Elf,
702-
// };
703-
704661
// TODO : Look in to other options (mangling, flags, etc (see Object::new))
705662
let mut out_object = write::Object::new(
706663
BinaryFormat::Elf,
@@ -738,16 +695,14 @@ fn write_dwarf<T: gimli::Endianity>(
738695
})
739696
.unwrap();
740697

741-
if let interaction::FormResponses::String(filename) = &responses[0] {
742-
if let Ok(out_data) = out_object.write() {
743-
if let Err(err) = fs::write(filename, out_data) {
744-
error!("Failed to write DWARF file: {}", err);
745-
} else {
746-
info!("Successfully saved as DWARF to `{}`", filename);
747-
}
698+
if let Ok(out_data) = out_object.write() {
699+
if let Err(err) = fs::write(file_path, out_data) {
700+
error!("Failed to write DWARF file: {}", err);
748701
} else {
749-
error!("Failed to write DWARF with requested settings");
702+
info!("Successfully saved as DWARF to `{:?}`", file_path);
750703
}
704+
} else {
705+
error!("Failed to write DWARF with requested settings");
751706
}
752707
}
753708

@@ -757,7 +712,42 @@ fn export_dwarf(bv: &BinaryView) {
757712
} else {
758713
String::from("Unknown")
759714
};
760-
let responses = present_form(arch_name.as_str());
715+
let mut export_form = create_export_form(arch_name.as_str());
716+
if !export_form.prompt() {
717+
return;
718+
}
719+
720+
let arch_field = export_form.get_field_with_name("Architecture").unwrap();
721+
let arch_field_idx = arch_field.try_value_index().unwrap_or_default();
722+
let arch = match arch_field_idx {
723+
0 => Architecture::Unknown,
724+
1 => Architecture::Aarch64,
725+
2 => Architecture::Aarch64_Ilp32,
726+
3 => Architecture::Arm,
727+
4 => Architecture::Avr,
728+
5 => Architecture::Bpf,
729+
6 => Architecture::I386,
730+
7 => Architecture::X86_64,
731+
8 => Architecture::X86_64_X32,
732+
9 => Architecture::Hexagon,
733+
10 => Architecture::LoongArch64,
734+
11 => Architecture::Mips,
735+
12 => Architecture::Mips64,
736+
13 => Architecture::Msp430,
737+
14 => Architecture::PowerPc,
738+
15 => Architecture::PowerPc64,
739+
16 => Architecture::Riscv32,
740+
17 => Architecture::Riscv64,
741+
18 => Architecture::S390x,
742+
19 => Architecture::Sbf,
743+
20 => Architecture::Sparc64,
744+
21 => Architecture::Wasm32,
745+
22 => Architecture::Xtensa,
746+
_ => Architecture::Unknown,
747+
};
748+
749+
let save_loc_field = export_form.get_field_with_name("Save Location").unwrap();
750+
let save_loc_path = PathBuf::from(save_loc_field.try_value_string().unwrap_or_default());
761751

762752
let encoding = gimli::Encoding {
763753
format: gimli::Format::Dwarf32,
@@ -782,9 +772,9 @@ fn export_dwarf(bv: &BinaryView) {
782772
// TODO: Sections? Segments?
783773

784774
if bv.default_endianness() == binaryninja::Endianness::LittleEndian {
785-
write_dwarf(responses, gimli::LittleEndian, &mut dwarf);
775+
write_dwarf(&save_loc_path, arch, gimli::LittleEndian, &mut dwarf);
786776
} else {
787-
write_dwarf(responses, gimli::BigEndian, &mut dwarf);
777+
write_dwarf(&save_loc_path, arch, gimli::BigEndian, &mut dwarf);
788778
};
789779
}
790780

rust/src/binary_view.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,46 @@ pub trait BinaryViewExt: BinaryViewBase {
11031103
unsafe { BNApplyDebugInfo(self.as_ref().handle, debug_info.handle) }
11041104
}
11051105

1106+
fn show_plaintext_report(&self, title: &str, plaintext: &str) {
1107+
let title = title.to_cstr();
1108+
let plaintext = plaintext.to_cstr();
1109+
unsafe {
1110+
BNShowPlainTextReport(
1111+
self.as_ref().handle,
1112+
title.as_ref().as_ptr() as *mut _,
1113+
plaintext.as_ref().as_ptr() as *mut _,
1114+
)
1115+
}
1116+
}
1117+
1118+
fn show_markdown_report(&self, title: &str, contents: &str, plaintext: &str) {
1119+
let title = title.to_cstr();
1120+
let contents = contents.to_cstr();
1121+
let plaintext = plaintext.to_cstr();
1122+
unsafe {
1123+
BNShowMarkdownReport(
1124+
self.as_ref().handle,
1125+
title.as_ref().as_ptr() as *mut _,
1126+
contents.as_ref().as_ptr() as *mut _,
1127+
plaintext.as_ref().as_ptr() as *mut _,
1128+
)
1129+
}
1130+
}
1131+
1132+
fn show_html_report(&self, title: &str, contents: &str, plaintext: &str) {
1133+
let title = title.to_cstr();
1134+
let contents = contents.to_cstr();
1135+
let plaintext = plaintext.to_cstr();
1136+
unsafe {
1137+
BNShowHTMLReport(
1138+
self.as_ref().handle,
1139+
title.as_ref().as_ptr() as *mut _,
1140+
contents.as_ref().as_ptr() as *mut _,
1141+
plaintext.as_ref().as_ptr() as *mut _,
1142+
)
1143+
}
1144+
}
1145+
11061146
fn show_graph_report(&self, raw_name: &str, graph: &FlowGraph) {
11071147
let raw_name = raw_name.to_cstr();
11081148
unsafe {

rust/src/function.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -480,15 +480,15 @@ impl Function {
480480
/// Get the language representation of the function.
481481
///
482482
/// * `language` - The language representation, ex. "Pseudo C".
483-
pub fn language_representation<S: BnStrCompatible>(
483+
pub fn language_representation(
484484
&self,
485-
language: S,
485+
language: &str,
486486
) -> Option<Ref<CoreLanguageRepresentationFunction>> {
487-
let lang_name = language.into_bytes_with_nul();
487+
let lang_name = language.to_cstr();
488488
let repr = unsafe {
489489
BNGetFunctionLanguageRepresentation(
490490
self.handle,
491-
lang_name.as_ref().as_ptr() as *const c_char,
491+
lang_name.as_ptr(),
492492
)
493493
};
494494
NonNull::new(repr)
@@ -498,15 +498,15 @@ impl Function {
498498
/// Get the language representation of the function, if available.
499499
///
500500
/// * `language` - The language representation, ex. "Pseudo C".
501-
pub fn language_representation_if_available<S: BnStrCompatible>(
501+
pub fn language_representation_if_available(
502502
&self,
503-
language: S,
503+
language: &str,
504504
) -> Option<Ref<CoreLanguageRepresentationFunction>> {
505-
let lang_name = language.into_bytes_with_nul();
505+
let lang_name = language.to_cstr();
506506
let repr = unsafe {
507507
BNGetFunctionLanguageRepresentationIfAvailable(
508508
self.handle,
509-
lang_name.as_ref().as_ptr() as *const c_char,
509+
lang_name.as_ptr(),
510510
)
511511
};
512512
NonNull::new(repr)

0 commit comments

Comments
 (0)