summaryrefslogtreecommitdiff
path: root/plugins/idb_import/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/idb_import/src/lib.rs')
-rw-r--r--plugins/idb_import/src/lib.rs344
1 files changed, 344 insertions, 0 deletions
diff --git a/plugins/idb_import/src/lib.rs b/plugins/idb_import/src/lib.rs
new file mode 100644
index 00000000..ddd22ea5
--- /dev/null
+++ b/plugins/idb_import/src/lib.rs
@@ -0,0 +1,344 @@
+mod types;
+use types::*;
+mod addr_info;
+use addr_info::*;
+
+use binaryninja::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt};
+use binaryninja::debuginfo::{
+ CustomDebugInfoParser, DebugFunctionInfo, DebugInfo, DebugInfoParser,
+};
+
+use idb_rs::id0::{ID0Section, IDBParam1, IDBParam2};
+use idb_rs::til::section::TILSection;
+use idb_rs::til::Type as TILType;
+
+use log::{error, trace, warn, LevelFilter};
+
+use anyhow::Result;
+use binaryninja::logger::Logger;
+
+struct IDBDebugInfoParser;
+impl CustomDebugInfoParser for IDBDebugInfoParser {
+ fn is_valid(&self, view: &BinaryView) -> bool {
+ if let Some(project_file) = view.file().project_file() {
+ project_file.name().as_str().ends_with(".i64")
+ || project_file.name().as_str().ends_with(".idb")
+ } else {
+ view.file().filename().as_str().ends_with(".i64")
+ || view.file().filename().as_str().ends_with(".idb")
+ }
+ }
+
+ fn parse_info(
+ &self,
+ debug_info: &mut DebugInfo,
+ bv: &BinaryView,
+ debug_file: &BinaryView,
+ progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
+ ) -> bool {
+ match parse_idb_info(debug_info, bv, debug_file, progress) {
+ Ok(()) => true,
+ Err(error) => {
+ error!("Unable to parse IDB file: {error}");
+ false
+ }
+ }
+ }
+}
+
+struct TILDebugInfoParser;
+impl CustomDebugInfoParser for TILDebugInfoParser {
+ fn is_valid(&self, view: &BinaryView) -> bool {
+ if let Some(project_file) = view.file().project_file() {
+ project_file.name().as_str().ends_with(".til")
+ } else {
+ view.file().filename().as_str().ends_with(".til")
+ }
+ }
+
+ fn parse_info(
+ &self,
+ debug_info: &mut DebugInfo,
+ _bv: &BinaryView,
+ debug_file: &BinaryView,
+ progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
+ ) -> bool {
+ match parse_til_info(debug_info, debug_file, progress) {
+ Ok(()) => true,
+ Err(error) => {
+ error!("Unable to parse TIL file: {error}");
+ false
+ }
+ }
+ }
+}
+
+struct BinaryViewReader<'a> {
+ bv: &'a BinaryView,
+ offset: u64,
+}
+impl std::io::Read for BinaryViewReader<'_> {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ if !self.bv.offset_valid(self.offset) {
+ return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""));
+ }
+ let len = self.bv.read(buf, self.offset);
+ self.offset += u64::try_from(len).unwrap();
+ Ok(len)
+ }
+}
+
+impl std::io::Seek for BinaryViewReader<'_> {
+ fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
+ let new_offset = match pos {
+ std::io::SeekFrom::Start(offset) => Some(offset),
+ std::io::SeekFrom::End(end) => u64::try_from(self.bv.len())
+ .unwrap()
+ .checked_add_signed(end),
+ std::io::SeekFrom::Current(next) => self.offset.checked_add_signed(next),
+ };
+ let new_offset =
+ new_offset.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""))?;
+ if !self.bv.offset_valid(new_offset) {
+ return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""));
+ }
+ self.offset = new_offset;
+ Ok(new_offset)
+ }
+}
+
+fn parse_idb_info(
+ debug_info: &mut DebugInfo,
+ bv: &BinaryView,
+ debug_file: &BinaryView,
+ progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
+) -> Result<()> {
+ trace!("Opening a IDB file");
+ let file = BinaryViewReader {
+ bv: debug_file,
+ offset: 0,
+ };
+ trace!("Parsing a IDB file");
+ let file = std::io::BufReader::new(file);
+ let mut parser = idb_rs::IDBParser::new(file)?;
+ if let Some(til_section) = parser.til_section_offset() {
+ trace!("Parsing the TIL section");
+ let til = parser.read_til_section(til_section)?;
+ // progress 0%-50%
+ import_til_section(debug_info, debug_file, &til, progress)?;
+ }
+
+ if let Some(id0_section) = parser.id0_section_offset() {
+ trace!("Parsing the ID0 section");
+ let id0 = parser.read_id0_section(id0_section)?;
+ // progress 50%-100%
+ parse_id0_section_info(debug_info, bv, debug_file, &id0)?;
+ }
+
+ Ok(())
+}
+
+fn parse_til_info(
+ debug_info: &mut DebugInfo,
+ debug_file: &BinaryView,
+ progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
+) -> Result<()> {
+ trace!("Opening a TIL file");
+ let file = BinaryViewReader {
+ bv: debug_file,
+ offset: 0,
+ };
+ let file = std::io::BufReader::new(file);
+ trace!("Parsing the TIL section");
+ let til = TILSection::parse(file)?;
+ import_til_section(debug_info, debug_file, &til, progress)
+}
+
+pub fn import_til_section(
+ debug_info: &mut DebugInfo,
+ debug_file: &BinaryView,
+ til: &TILSection,
+ progress: impl Fn(usize, usize) -> Result<(), ()>,
+) -> Result<()> {
+ let types = types::translate_til_types(debug_file.default_arch().unwrap(), til, progress)?;
+
+ // print any errors
+ for ty in &types {
+ match &ty.ty {
+ TranslateTypeResult::NotYet => {
+ panic!(
+ "type could not be processed `{}`: {:#?}",
+ &String::from_utf8_lossy(&ty.name),
+ &ty.og_ty
+ );
+ }
+ TranslateTypeResult::Error(error) => {
+ error!(
+ "Unable to parse type `{}`: {error}",
+ &String::from_utf8_lossy(&ty.name)
+ );
+ }
+ TranslateTypeResult::PartiallyTranslated(_, error) => {
+ if let Some(error) = error {
+ error!(
+ "Unable to parse type `{}` correctly: {error}",
+ &String::from_utf8_lossy(&ty.name)
+ );
+ } else {
+ warn!(
+ "Type `{}` maybe not be fully translated",
+ &String::from_utf8_lossy(&ty.name)
+ );
+ }
+ }
+ TranslateTypeResult::Translated(_) => {}
+ };
+ }
+
+ // add all type to binary ninja
+ for ty in &types {
+ if let TranslateTypeResult::Translated(bn_ty)
+ | TranslateTypeResult::PartiallyTranslated(bn_ty, _) = &ty.ty
+ {
+ if !debug_info.add_type(String::from_utf8_lossy(&ty.name), bn_ty, &[/* TODO */]) {
+ error!(
+ "Unable to add type `{}`",
+ &String::from_utf8_lossy(&ty.name)
+ )
+ }
+ }
+ }
+
+ // add a second time to fix the references LOL
+ for ty in &types {
+ if let TranslateTypeResult::Translated(bn_ty)
+ | TranslateTypeResult::PartiallyTranslated(bn_ty, _) = &ty.ty
+ {
+ if !debug_info.add_type(String::from_utf8_lossy(&ty.name), bn_ty, &[/* TODO */]) {
+ error!(
+ "Unable to fix type `{}`",
+ &String::from_utf8_lossy(&ty.name)
+ )
+ }
+ }
+ }
+
+ Ok(())
+}
+
+fn parse_id0_section_info(
+ debug_info: &mut DebugInfo,
+ bv: &BinaryView,
+ debug_file: &BinaryView,
+ id0: &ID0Section,
+) -> Result<()> {
+ let version = match id0.ida_info()? {
+ idb_rs::id0::IDBParam::V1(IDBParam1 { version, .. })
+ | idb_rs::id0::IDBParam::V2(IDBParam2 { version, .. }) => version,
+ };
+
+ for (addr, info) in get_info(id0, version)? {
+ // just in case we change this struct in the future, this line will for us to review this code
+ // TODO merge this data with folder locations
+ let AddrInfo {
+ comments,
+ label,
+ ty,
+ } = info;
+ // TODO set comments to address here
+ for function in &bv.functions_containing(addr) {
+ function.set_comment_at(
+ addr,
+ String::from_utf8_lossy(&comments.join(&b"\n"[..])).to_string(),
+ );
+ }
+
+ let bnty = ty
+ .as_ref()
+ .and_then(|ty| match translate_ephemeral_type(debug_file, ty) {
+ TranslateTypeResult::Translated(result) => Some(result),
+ TranslateTypeResult::PartiallyTranslated(result, None) => {
+ warn!("Unable to fully translate the type at {addr:#x}");
+ Some(result)
+ }
+ TranslateTypeResult::NotYet => {
+ error!("Unable to translate the type at {addr:#x}");
+ None
+ }
+ TranslateTypeResult::PartiallyTranslated(_, Some(bn_type_error))
+ | TranslateTypeResult::Error(bn_type_error) => {
+ error!("Unable to translate the type at {addr:#x}: {bn_type_error}",);
+ None
+ }
+ });
+
+ match (label, &ty, bnty) {
+ (_, Some(TILType::Function(_)), bnty) => {
+ if bnty.is_none() {
+ error!("Unable to convert the function type at {addr:#x}",)
+ }
+ if !debug_info.add_function(DebugFunctionInfo::new(
+ None,
+ None,
+ label.map(str::to_string),
+ bnty,
+ Some(addr),
+ None,
+ vec![],
+ vec![],
+ )) {
+ error!("Unable to add the function at {addr:#x}")
+ }
+ }
+ (_, Some(_ty), Some(bnty)) => {
+ if !debug_info.add_data_variable(addr, &bnty, label, &[]) {
+ error!("Unable to add the type at {addr:#x}")
+ }
+ }
+ (_, Some(_ty), None) => {
+ // TODO types come from the TIL sections, can we make all types be just NamedTypes?
+ error!("Unable to convert type {addr:#x}");
+ // TODO how to add a label without a type associacted with it?
+ if let Some(name) = label {
+ if !debug_info.add_data_variable(
+ addr,
+ &binaryninja::types::Type::void(),
+ Some(name),
+ &[],
+ ) {
+ error!("Unable to add the label at {addr:#x}")
+ }
+ }
+ }
+ (Some(name), None, None) => {
+ // TODO how to add a label without a type associacted with it?
+ if !debug_info.add_data_variable(
+ addr,
+ &binaryninja::types::Type::void(),
+ Some(name),
+ &[],
+ ) {
+ error!("Unable to add the label at {addr:#x}")
+ }
+ }
+
+ // just comments at this address
+ (None, None, None) => {}
+
+ (_, None, Some(_)) => unreachable!(),
+ }
+ }
+
+ Ok(())
+}
+
+#[allow(non_snake_case)]
+#[no_mangle]
+pub extern "C" fn CorePluginInit() -> bool {
+ Logger::new("IDB Import")
+ .with_level(LevelFilter::Error)
+ .init();
+ DebugInfoParser::register("IDB Parser", IDBDebugInfoParser);
+ DebugInfoParser::register("TIL Parser", TILDebugInfoParser);
+ true
+}