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.rs443
1 files changed, 51 insertions, 392 deletions
diff --git a/plugins/idb_import/src/lib.rs b/plugins/idb_import/src/lib.rs
index 0137f198..baf9b8ee 100644
--- a/plugins/idb_import/src/lib.rs
+++ b/plugins/idb_import/src/lib.rs
@@ -1,402 +1,64 @@
-mod types;
-use std::borrow::Cow;
-use std::io::{BufRead, Cursor, Seek};
+use crate::mapper::IDBMapper;
+use crate::parse::IDBFileParser;
+use crate::settings::LoadSettings;
+use binaryninja::binary_view::AnalysisContext;
+use binaryninja::workflow::{activity, Activity, Workflow};
+use std::fs::File;
+use std::io::BufReader;
-use idb_rs::id1::ID1Section;
-use idb_rs::id2::{ID2Section, ID2SectionVariants};
-use idb_rs::{IDAKind, IDAUsize, IDBFormat};
-use types::*;
-mod addr_info;
-use addr_info::*;
+mod commands;
+pub mod mapper;
+pub mod parse;
+mod settings;
+pub mod translate;
-use binaryninja::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt};
-use binaryninja::debuginfo::{
- CustomDebugInfoParser, DebugFunctionInfo, DebugInfo, DebugInfoParser,
-};
-
-use idb_rs::id0::{ID0Section, ID0SectionVariants};
-use idb_rs::til::section::TILSection;
-use idb_rs::til::TypeVariant as TILTypeVariant;
-
-use anyhow::{anyhow, Result};
-
-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()
- .file_path()
- .extension()
- .map_or(false, |ext| ext == "i64" || ext == "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) => {
- tracing::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()
- .file_path()
- .extension()
- .map_or(false, |ext| ext == "til")
- }
- }
+fn plugin_init() -> Result<(), ()> {
+ binaryninja::tracing_init!("IDB Import");
- 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) => {
- tracing::error!("Unable to parse TIL file: {error}");
- false
- }
- }
- }
-}
+ binaryninja::command::register_command(
+ "Load IDB File",
+ "Loads an IDB file into the current view.",
+ commands::load_file::LoadIDBFile,
+ );
-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) {
- // TODO check if this is truly a EoF hit, `self.bv.len()` is not
- // reliable, it's returning a size bigger then the original file.
- return Ok(0);
- }
- let len = BinaryView::read(self.bv, buf, self.offset);
- self.offset += u64::try_from(len).unwrap();
- Ok(len)
- }
-}
+ // Register settings globally.
+ LoadSettings::register();
-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) => self.bv.len().checked_add_signed(end),
- std::io::SeekFrom::Current(next) => self.offset.checked_add_signed(next),
+ let loader_activity = |ctx: &AnalysisContext| {
+ let view = ctx.view();
+ let load_settings = LoadSettings::from_view_settings(&view);
+ let Some(file_path) = &load_settings.auto_load_file else {
+ tracing::debug!("No IDB file specified, skipping...");
+ return;
};
- let new_offset = new_offset.ok_or_else(|| {
- std::io::Error::new(
- std::io::ErrorKind::UnexpectedEof,
- "Unable to calculate new offset in BinaryViewReader",
- )
- })?;
- if !self.bv.offset_valid(new_offset) {
- return Err(std::io::Error::new(
- std::io::ErrorKind::UnexpectedEof,
- "Try to set invalid offset in BinaryViewReader",
- ));
- }
- 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<()> {
- tracing::trace!("Opening a IDB file");
- let file = BinaryViewReader {
- bv: debug_file,
- offset: 0,
- };
- tracing::trace!("Parsing a IDB file");
- let mut file = std::io::BufReader::new(file);
- let idb_kind = idb_rs::identify_idb_file(&mut file)?;
- match idb_kind {
- idb_rs::IDBFormats::Separated(sep) => {
- parse_idb_info_format(debug_info, bv, debug_file, sep, file, progress)
- }
- idb_rs::IDBFormats::InlineUncompressed(inline) => {
- parse_idb_info_format(debug_info, bv, debug_file, inline, file, progress)
- }
- idb_rs::IDBFormats::InlineCompressed(compressed) => {
- let mut buf = vec![];
- let inline = compressed.decompress_into_memory(&mut file, &mut buf)?;
- parse_idb_info_format(
- debug_info,
- bv,
- debug_file,
- inline,
- Cursor::new(&buf[..]),
- progress,
- )
- }
- }
-}
-
-fn parse_idb_info_format(
- debug_info: &mut DebugInfo,
- bv: &BinaryView,
- debug_file: &BinaryView,
- format: impl IDBFormat,
- mut idb_data: impl BufRead + Seek,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
-) -> Result<()> {
- let Some(id0_idx) = format.id0_location() else {
- return Err(anyhow!("Unable to find the ID0 section"));
- };
- let Some(id1_idx) = format.id1_location() else {
- return Err(anyhow!("Unable to find the ID1 section"));
- };
- let id2_idx = format.id2_location();
-
- if let Some(til_idx) = format.til_location() {
- tracing::trace!("Parsing the TIL section");
- let til = format.read_til(&mut idb_data, til_idx)?;
- // progress 0%-50%
- import_til_section(debug_info, debug_file, &til, progress)?;
- };
-
- let id0 = format.read_id0(&mut idb_data, id0_idx)?;
- let id1 = format.read_id1(&mut idb_data, id1_idx)?;
- let id2 = id2_idx
- .map(|id2_idx| format.read_id2(&mut idb_data, id2_idx))
- .transpose()?;
-
- match (id0, id2) {
- (ID0SectionVariants::IDA32(id0), Some(ID2SectionVariants::IDA32(id2))) => {
- parse_id0_section_info(debug_info, bv, debug_file, &id0, &id1, Some(&id2))?
- }
- (ID0SectionVariants::IDA32(id0), None) => {
- parse_id0_section_info(debug_info, bv, debug_file, &id0, &id1, None)?
- }
- (ID0SectionVariants::IDA64(id0), Some(ID2SectionVariants::IDA64(id2))) => {
- parse_id0_section_info(debug_info, bv, debug_file, &id0, &id1, Some(&id2))?
- }
- (ID0SectionVariants::IDA64(id0), None) => {
- parse_id0_section_info(debug_info, bv, debug_file, &id0, &id1, None)?
- }
- _ => unreachable!(),
- }
-
- Ok(())
-}
-
-fn parse_til_info(
- debug_info: &mut DebugInfo,
- debug_file: &BinaryView,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
-) -> Result<()> {
- tracing::trace!("Opening a TIL file");
- let file = BinaryViewReader {
- bv: debug_file,
- offset: 0,
- };
- let mut file = std::io::BufReader::new(file);
- tracing::trace!("Parsing the TIL section");
- let til = TILSection::read(&mut 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 `{}`: {:#?}",
- ty.name.as_utf8_lossy(),
- &ty.og_ty
- );
- }
- TranslateTypeResult::Error(error) => {
- tracing::error!(
- "Unable to parse type `{}`: {error}",
- ty.name.as_utf8_lossy(),
- );
- }
- TranslateTypeResult::PartiallyTranslated(_, error) => {
- if let Some(error) = error {
- tracing::error!(
- "Unable to parse type `{}` correctly: {error}",
- ty.name.as_utf8_lossy(),
- );
- } else {
- tracing::warn!(
- "Type `{}` maybe not be fully translated",
- ty.name.as_utf8_lossy(),
- );
- }
- }
- TranslateTypeResult::Translated(_) => {}
+ let Ok(file) = File::open(&file_path) else {
+ tracing::error!("Failed to open file: {}", file_path.display());
+ return;
};
- }
-
- // 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(&ty.name.as_utf8_lossy(), bn_ty, &[/* TODO */]) {
- tracing::error!("Unable to add type `{}`", ty.name.as_utf8_lossy())
+ let mut file_reader = BufReader::new(file);
+ let file_parser = IDBFileParser::new();
+ match file_parser.parse(&mut file_reader) {
+ Ok(idb_info) => {
+ IDBMapper::new(idb_info).map_to_view(&view);
}
- }
- }
-
- // 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(&ty.name.as_utf8_lossy(), bn_ty, &[/* TODO */]) {
- tracing::error!("Unable to fix type `{}`", ty.name.as_utf8_lossy())
+ Err(e) => {
+ tracing::error!("Failed to parse IDB file: {}", e);
}
}
- }
-
- Ok(())
-}
-
-fn parse_id0_section_info<K: IDAKind>(
- debug_info: &mut DebugInfo,
- bv: &BinaryView,
- debug_file: &BinaryView,
- id0: &ID0Section<K>,
- id1: &ID1Section,
- id2: Option<&ID2Section<K>>,
-) -> Result<()> {
- let ida_info_idx = id0.root_node()?;
- let ida_info = id0.ida_info(ida_info_idx)?;
- let idb_baseaddr = ida_info.addresses.loading_base.into_u64();
- let bv_baseaddr = bv.start();
- let netdelta = ida_info.netdelta();
- // just addr this value to the address to translate from ida to bn
- // NOTE this delta could wrap here and while using translating
- let addr_delta = bv_baseaddr.wrapping_sub(idb_baseaddr);
-
- for (idb_addr, info) in get_info(id0, id1, id2, netdelta)? {
- let addr = addr_delta.wrapping_add(idb_addr.into_raw().into_u64());
- // 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"[..])));
- }
-
- let bnty = ty
- .as_ref()
- .and_then(|ty| match translate_ephemeral_type(debug_file, ty) {
- TranslateTypeResult::Translated(result) => Some(result),
- TranslateTypeResult::PartiallyTranslated(result, None) => {
- tracing::warn!("Unable to fully translate the type at {addr:#x}");
- Some(result)
- }
- TranslateTypeResult::NotYet => {
- tracing::error!("Unable to translate the type at {addr:#x}");
- None
- }
- TranslateTypeResult::PartiallyTranslated(_, Some(bn_type_error))
- | TranslateTypeResult::Error(bn_type_error) => {
- tracing::error!("Unable to translate the type at {addr:#x}: {bn_type_error}",);
- None
- }
- });
-
- let label: Option<Cow<'_, str>> =
- label.as_ref().map(Cow::as_ref).map(String::from_utf8_lossy);
- match (label, &ty, bnty) {
- (label, Some(ty), bnty) if matches!(&ty.type_variant, TILTypeVariant::Function(_)) => {
- if bnty.is_none() {
- tracing::error!("Unable to convert the function type at {addr:#x}",)
- }
- if !debug_info.add_function(&DebugFunctionInfo::new(
- None,
- None,
- label.map(Cow::into_owned),
- bnty,
- Some(addr),
- None,
- vec![],
- vec![],
- )) {
- tracing::error!("Unable to add the function at {addr:#x}")
- }
- }
- (label, Some(_ty), Some(bnty)) => {
- if !debug_info.add_data_variable(addr, &bnty, label.as_ref().map(Cow::as_ref), &[])
- {
- tracing::error!("Unable to add the type at {addr:#x}")
- }
- }
- (label, Some(_ty), None) => {
- // TODO types come from the TIL sections, can we make all types be just NamedTypes?
- tracing::error!("Unable to convert type {addr:#x}");
- // TODO how to add a label without a type associated with it?
- if let Some(name) = label {
- if !debug_info.add_data_variable(
- addr,
- &binaryninja::types::Type::void(),
- Some(&name),
- &[],
- ) {
- tracing::error!("Unable to add the label at {addr:#x}")
- }
- }
- }
- (Some(name), None, None) => {
- // TODO how to add a label without a type associated with it?
- if !debug_info.add_data_variable(
- addr,
- &binaryninja::types::Type::void(),
- Some(&name),
- &[],
- ) {
- tracing::error!("Unable to add the label at {addr:#x}")
- }
- }
-
- // just comments at this address
- (None, None, None) => {}
+ };
- (_, None, Some(_)) => unreachable!(),
- }
- }
+ // Register new workflow activity to load svd information.
+ let loader_config = activity::Config::action(
+ "analysis.idb.loader",
+ "IDB Loader",
+ "This analysis step applies IDB info to the view...",
+ )
+ .eligibility(activity::Eligibility::auto().run_once(true));
+ let loader_activity = Activity::new_with_action(loader_config, loader_activity);
+ Workflow::cloned("core.module.metaAnalysis")
+ .ok_or(())?
+ .activity_before(&loader_activity, "core.module.loadDebugInfo")?
+ .register()?;
Ok(())
}
@@ -404,8 +66,5 @@ fn parse_id0_section_info<K: IDAKind>(
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn CorePluginInit() -> bool {
- binaryninja::tracing_init!("IDB Import");
- DebugInfoParser::register("IDB Parser", IDBDebugInfoParser);
- DebugInfoParser::register("TIL Parser", TILDebugInfoParser);
- true
+ plugin_init().is_ok()
}