summaryrefslogtreecommitdiff
path: root/rust/examples
diff options
context:
space:
mode:
authorKyleMiles <krm504@nyu.edu>2021-01-21 18:35:20 +0000
committerKyleMiles <krm504@nyu.edu>2021-01-21 19:07:07 +0000
commit2fcacc55d5466a7d17bdc0b3ce988772aa6d0653 (patch)
tree9d0f2ba19117843575936f2b93e0b6a578a84dc8 /rust/examples
parenta0da07c5c2860a5bd69921d38e438bbc1d43912f (diff)
cargo fmt and all my changes
Diffstat (limited to 'rust/examples')
-rw-r--r--rust/examples/dwarfdump/Cargo.toml12
-rw-r--r--rust/examples/dwarfdump/readme.md17
-rw-r--r--rust/examples/dwarfdump/src/lib.rs393
-rw-r--r--rust/examples/flowgraph/Cargo.toml12
-rw-r--r--rust/examples/flowgraph/src/lib.rs52
5 files changed, 486 insertions, 0 deletions
diff --git a/rust/examples/dwarfdump/Cargo.toml b/rust/examples/dwarfdump/Cargo.toml
new file mode 100644
index 00000000..b8b431f4
--- /dev/null
+++ b/rust/examples/dwarfdump/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "dwarfdump"
+version = "0.1.0"
+authors = ["KyleMiles <kyle@vector35.com>"]
+edition = "2018"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+binaryninja = {path="../../"}
+gimli = "0.23.0"
diff --git a/rust/examples/dwarfdump/readme.md b/rust/examples/dwarfdump/readme.md
new file mode 100644
index 00000000..ae3a193b
--- /dev/null
+++ b/rust/examples/dwarfdump/readme.md
@@ -0,0 +1,17 @@
+# DWARF Dump Example
+
+This is actually a fully-developed plugin, rather than a measly example.
+
+Two features this does not support are: files in big endian, and .dwo files
+
+## How to use
+
+Simply `cargo build --release` in this directory, and copy the `.so` from the target directory to your plugin directory
+
+### Attribution
+
+This example makes use of:
+ - [gimli] ([gimli license] - MIT)
+
+[gimli license]: https://github.com/gimli-rs/gimli/blob/master/LICENSE-MIT
+[gimli]: https://github.com/gimli-rs/gimli
diff --git a/rust/examples/dwarfdump/src/lib.rs b/rust/examples/dwarfdump/src/lib.rs
new file mode 100644
index 00000000..d1ef288a
--- /dev/null
+++ b/rust/examples/dwarfdump/src/lib.rs
@@ -0,0 +1,393 @@
+use binaryninja::binaryview::{BinaryView, BinaryViewExt};
+use binaryninja::command::{register, Command};
+use binaryninja::databuffer::DataBuffer;
+use binaryninja::disassembly::{
+ DisassemblyTextLine, InstructionTextToken, InstructionTextTokenType,
+};
+use binaryninja::flowgraph::{BranchType, EdgeStyle, FlowGraph, FlowGraphNode, FlowGraphOption};
+// use binaryninja::Endianness;
+
+use gimli::{
+ AttributeValue::{Encoding, Flag, UnitRef},
+ // BigEndian,
+ DebuggingInformationEntry,
+ Dwarf,
+ EntriesTreeNode,
+ Error,
+ LittleEndian,
+ Reader,
+ ReaderOffset,
+ SectionId,
+ Unit,
+ UnitSectionOffset,
+};
+
+use std::fmt;
+use std::ops::Deref;
+use std::sync::Arc;
+
+static PADDING: [&'static str; 23] = [
+ "",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+];
+
+// TODO : This isn't comprehensive
+fn is_valid(view: &BinaryView) -> bool {
+ view.section_by_name(".debug_info").is_ok()
+ || view.parent_view().is_ok()
+ && view
+ .parent_view()
+ .unwrap()
+ .section_by_name(".debug_info")
+ .is_ok()
+}
+
+// gimli::read::load only takes structures containing &[u8]'s, but we need to keep the data buffer alive until it's done using that
+// I don't think that the `Arc` is needed, but I couldn't figure out how else to implement the traits properly without it
+#[derive(Clone)]
+struct DataBufferWrapper(Arc<DataBuffer>);
+
+impl DataBufferWrapper {
+ fn new(buf: DataBuffer) -> Self {
+ DataBufferWrapper(Arc::new(buf))
+ }
+}
+
+impl Deref for DataBufferWrapper {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ self.0.get_data()
+ }
+}
+
+impl fmt::Debug for DataBufferWrapper {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("DataBufferWrapper")
+ .field("0", &"I'm too lazy to do this right")
+ .finish()
+ }
+}
+
+unsafe impl gimli::StableDeref for DataBufferWrapper {}
+unsafe impl gimli::CloneStableDeref for DataBufferWrapper {}
+
+type CustomReader<Endian> = gimli::EndianReader<Endian, DataBufferWrapper>;
+
+// TODO : This is very much not comprehensive: see https://github.com/gimli-rs/gimli/blob/master/examples/dwarfdump.rs
+fn get_info_string<R: Reader>(
+ view: &BinaryView,
+ dwarf: &Dwarf<R>,
+ unit: &Unit<R>,
+ die_node: &DebuggingInformationEntry<R>,
+) -> Vec<DisassemblyTextLine> {
+ let mut disassembly_lines: Vec<DisassemblyTextLine> = Vec::with_capacity(10); // This is an estimate so "most" things won't need to resize
+
+ let label_value = match die_node.offset().to_unit_section_offset(unit) {
+ UnitSectionOffset::DebugInfoOffset(o) => o.0,
+ UnitSectionOffset::DebugTypesOffset(o) => o.0,
+ }
+ .into_u64();
+ let label_string = format!("#0x{:08x}", label_value);
+ disassembly_lines.push(DisassemblyTextLine::from(vec![
+ InstructionTextToken::new(
+ InstructionTextTokenType::GotoLabelToken,
+ label_string.as_ref(),
+ label_value,
+ ),
+ InstructionTextToken::new(InstructionTextTokenType::TextToken, ":", 0),
+ ]));
+
+ disassembly_lines.push(DisassemblyTextLine::from(vec![InstructionTextToken::new(
+ InstructionTextTokenType::TypeNameToken, // TODO : KeywordToken?
+ die_node.tag().static_string().unwrap(),
+ 0,
+ )]));
+
+ let mut attrs = die_node.attrs();
+ while let Some(attr) = attrs.next().unwrap() {
+ let mut attr_line: Vec<InstructionTextToken> = Vec::with_capacity(5);
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::IndentationToken,
+ &" ",
+ 0,
+ ));
+
+ let len;
+ if let Some(n) = attr.name().static_string() {
+ len = n.len();
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::FieldNameToken,
+ n,
+ 0,
+ ));
+ } else {
+ // This is rather unlikely, I think
+ len = 1;
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::FieldNameToken,
+ &"?",
+ 0,
+ ));
+ }
+
+ // On command line the magic number that looks good is 22, but that's too much whitespace in a basic block, so I chose 18 (22 is the max with the current padding provided)
+ if len < 18 {
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::TextToken,
+ PADDING[18 - len],
+ 0,
+ ));
+ }
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::TextToken,
+ &" = ",
+ 0,
+ ));
+
+ if let Ok(Some(addr)) = dwarf.attr_address(&unit, attr.value()) {
+ let addr_string = format!("0x{:08x}", addr);
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::IntegerToken,
+ addr_string.as_ref(),
+ addr,
+ ));
+ } else if let Ok(attr_reader) = dwarf.attr_string(&unit, attr.value()) {
+ if let Ok(attr_string) = attr_reader.to_string() {
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::StringToken,
+ attr_string.as_ref(),
+ {
+ // TODO : name() might need to become dwo_name
+ let (_, id, offset) = dwarf.lookup_offset_id(attr_reader.offset_id()).unwrap();
+ offset.into_u64() + view.section_by_name(id.name()).unwrap().start()
+ },
+ ));
+ } else {
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::TextToken,
+ &"??",
+ 0,
+ ));
+ }
+ } else if let Encoding(type_class) = attr.value() {
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::TypeNameToken,
+ type_class.static_string().unwrap(),
+ 0,
+ ));
+ } else if let UnitRef(offset) = attr.value() {
+ let addr = match offset.to_unit_section_offset(unit) {
+ UnitSectionOffset::DebugInfoOffset(o) => o.0,
+ UnitSectionOffset::DebugTypesOffset(o) => o.0,
+ }
+ .into_u64();
+ let addr_string = format!("#0x{:08x}", addr);
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::GotoLabelToken,
+ addr_string.as_ref(),
+ addr,
+ ));
+ } else if let Flag(true) = attr.value() {
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::IntegerToken,
+ &"true",
+ 1,
+ ));
+ } else if let Flag(false) = attr.value() {
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::IntegerToken,
+ &"false",
+ 1,
+ ));
+
+ // Fall-back cases
+ } else if let Some(value) = attr.u8_value() {
+ let value_string = format!("{}", value);
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::IntegerToken,
+ value_string.as_ref(),
+ value.into(),
+ ));
+ } else if let Some(value) = attr.u16_value() {
+ let value_string = format!("{}", value);
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::IntegerToken,
+ value_string.as_ref(),
+ value.into(),
+ ));
+ } else if let Some(value) = attr.udata_value() {
+ let value_string = format!("{}", value);
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::IntegerToken,
+ value_string.as_ref(),
+ value.into(),
+ ));
+ } else if let Some(value) = attr.sdata_value() {
+ let value_string = format!("{}", value);
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::IntegerToken,
+ value_string.as_ref(),
+ value as u64,
+ ));
+ } else {
+ let attr_string = format!("{:?}", attr.value());
+ attr_line.push(InstructionTextToken::new(
+ InstructionTextTokenType::TextToken,
+ attr_string.as_ref(),
+ 0,
+ ));
+ }
+ disassembly_lines.push(DisassemblyTextLine::from(attr_line));
+ }
+
+ disassembly_lines
+}
+
+fn process_tree<R: Reader>(
+ view: &BinaryView,
+ dwarf: &Dwarf<R>,
+ unit: &Unit<R>,
+ graph: &FlowGraph,
+ graph_parent: &FlowGraphNode,
+ die_node: EntriesTreeNode<R>,
+) {
+ // Namespaces only - really interesting to look at!
+ // if (die_node.entry().tag() == constants::DW_TAG_namespace)
+ // || (die_node.entry().tag() == constants::DW_TAG_class_type)
+ // || (die_node.entry().tag() == constants::DW_TAG_compile_unit)
+ // || (die_node.entry().tag() == constants::DW_TAG_subprogram)
+ // {
+ let new_node = FlowGraphNode::new(&graph);
+
+ let attr_string = get_info_string(view, dwarf, unit, die_node.entry());
+ new_node.set_disassembly_lines(&attr_string);
+
+ graph.append(&new_node);
+ graph_parent.add_outgoing_edge(
+ BranchType::UnconditionalBranch,
+ &new_node,
+ &EdgeStyle::default(),
+ );
+
+ let mut children = die_node.children();
+ while let Some(child) = children.next().unwrap() {
+ process_tree(view, dwarf, unit, graph, &new_node, child);
+ }
+ // }
+}
+
+fn dump_dwarf(bv: &BinaryView) {
+ let view;
+ if bv.section_by_name(".debug_info").is_ok() {
+ view = bv.to_owned();
+ } else {
+ view = bv.parent_view().unwrap();
+ }
+
+ // TODO : Accommodate Endianity
+ let get_section_data_little =
+ |section_id: SectionId| -> Result<CustomReader<LittleEndian>, Error> {
+ if let Ok(section) = view.section_by_name(section_id.name()) {
+ let offset = section.start();
+ let len = section.len();
+ if len == 0 {
+ return Ok(CustomReader::new(
+ DataBufferWrapper::new(DataBuffer::default()),
+ LittleEndian,
+ ));
+ }
+
+ if let Ok(read_buffer) = view.read_buffer(offset, len as usize) {
+ return Ok(CustomReader::new(
+ DataBufferWrapper::new(read_buffer),
+ LittleEndian,
+ ));
+ }
+ return Err(Error::Io);
+ } else {
+ return Ok(CustomReader::new(
+ DataBufferWrapper::new(DataBuffer::default()),
+ LittleEndian,
+ ));
+ }
+ };
+
+ let empty_reader_little = |_: SectionId| -> Result<CustomReader<LittleEndian>, Error> {
+ Ok(CustomReader::new(
+ DataBufferWrapper::new(DataBuffer::default()),
+ LittleEndian,
+ ))
+ };
+
+ let graph = FlowGraph::new();
+ graph.set_option(FlowGraphOption::FlowGraphUsesBlockHighlights, true);
+ graph.set_option(FlowGraphOption::FlowGraphUsesInstructionHighlights, true);
+
+ let graph_root = FlowGraphNode::new(&graph);
+ graph_root.set_lines(vec!["Graph Root"]);
+ graph.append(&graph_root);
+
+ let dwarf = Dwarf::load(&get_section_data_little, &empty_reader_little).unwrap();
+
+ let mut iter = dwarf.units();
+ while let Some(header) = iter.next().unwrap() {
+ let unit = dwarf.unit(header).unwrap();
+ let mut entries = unit.entries();
+ let mut depth = 0;
+
+ if let Some((delta_depth, entry)) = entries.next_dfs().unwrap() {
+ depth += delta_depth;
+ assert!(depth >= 0);
+
+ let mut tree = unit.entries_tree(Some(entry.offset())).unwrap();
+ let root = tree.root().unwrap();
+
+ process_tree(&view, &dwarf, &unit, &graph, &graph_root, root);
+ }
+ }
+
+ view.show_graph_report("DWARF", graph);
+}
+
+struct DWARFDump;
+
+impl Command for DWARFDump {
+ fn action(&self, view: &BinaryView) {
+ dump_dwarf(view);
+ }
+
+ fn valid(&self, view: &BinaryView) -> bool {
+ is_valid(view)
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn UIPluginInit() -> bool {
+ register(
+ "DWARF Dump",
+ "Show embedded DWARF info as a tree structure for you to navigate",
+ DWARFDump {},
+ );
+ true
+}
diff --git a/rust/examples/flowgraph/Cargo.toml b/rust/examples/flowgraph/Cargo.toml
new file mode 100644
index 00000000..7c7c07eb
--- /dev/null
+++ b/rust/examples/flowgraph/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "flowgraph"
+version = "0.1.0"
+authors = ["Kyle Martin <kyle@vector35.com>"]
+edition = "2018"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+binaryninja = {path="../../"}
+
diff --git a/rust/examples/flowgraph/src/lib.rs b/rust/examples/flowgraph/src/lib.rs
new file mode 100644
index 00000000..e0c47d9f
--- /dev/null
+++ b/rust/examples/flowgraph/src/lib.rs
@@ -0,0 +1,52 @@
+use binaryninja::binaryview::{BinaryView, BinaryViewExt};
+use binaryninja::command::register;
+use binaryninja::disassembly::{
+ DisassemblyTextLine, InstructionTextToken, InstructionTextTokenType,
+};
+use binaryninja::flowgraph::{
+ BranchType, EdgePenStyle, EdgeStyle, FlowGraph, FlowGraphNode, ThemeColor,
+};
+
+fn test_graph(view: &BinaryView) {
+ let graph = FlowGraph::new();
+
+ let disassembly_lines_a = vec![DisassemblyTextLine::from(vec![
+ InstructionTextToken::new(InstructionTextTokenType::TextToken, "Li"),
+ InstructionTextToken::new(InstructionTextTokenType::TextToken, "ne"),
+ InstructionTextToken::new(InstructionTextTokenType::TextToken, " 1"),
+ ])];
+
+ let node_a = FlowGraphNode::new(&graph);
+ node_a.set_disassembly_lines(&disassembly_lines_a);
+
+ let node_b = FlowGraphNode::new(&graph);
+ let disassembly_lines_b = vec![DisassemblyTextLine::from(&vec!["Li", "ne", " 2"])];
+ node_b.set_disassembly_lines(&disassembly_lines_b);
+
+ let node_c = FlowGraphNode::new(&graph);
+ node_c.set_lines(vec!["Line 3", "Line 4", "Line 5"]);
+
+ graph.append(&node_a);
+ graph.append(&node_b);
+ graph.append(&node_c);
+
+ let edge = EdgeStyle::new(EdgePenStyle::DashDotDotLine, 2, ThemeColor::AddressColor);
+ node_a.add_outgoing_edge(BranchType::UserDefinedBranch, &node_b, &edge);
+ node_a.add_outgoing_edge(
+ BranchType::UnconditionalBranch,
+ &node_c,
+ &EdgeStyle::default(),
+ );
+
+ view.show_graph_report("Rust Graph Title", graph);
+}
+
+#[no_mangle]
+pub extern "C" fn UIPluginInit() -> bool {
+ register(
+ "Rust Graph Test Title",
+ "Rust Graph Test Description",
+ test_graph,
+ );
+ true
+}