summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/idb_import/src/mapper.rs119
-rw-r--r--plugins/idb_import/src/parse.rs117
2 files changed, 213 insertions, 23 deletions
diff --git a/plugins/idb_import/src/mapper.rs b/plugins/idb_import/src/mapper.rs
index 4301ca9c..36c980da 100644
--- a/plugins/idb_import/src/mapper.rs
+++ b/plugins/idb_import/src/mapper.rs
@@ -1,15 +1,19 @@
//! Map the IDB data we parsed into the [`BinaryView`].
-use crate::parse::{CommentInfo, FunctionInfo, IDBInfo, LabelInfo, NameInfo, SegmentInfo};
+use crate::parse::{
+ BaseAddressInfo, CommentInfo, ExportInfo, FunctionInfo, IDBInfo, LabelInfo, NameInfo,
+ SegmentInfo,
+};
use crate::translate::TILTranslator;
use binaryninja::architecture::Architecture;
use binaryninja::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt};
use binaryninja::qualified_name::QualifiedName;
use binaryninja::rc::Ref;
use binaryninja::section::{SectionBuilder, Semantics};
-use binaryninja::symbol::{Symbol, SymbolType};
+use binaryninja::symbol::{Binding, Symbol, SymbolType};
use binaryninja::types::Type;
use idb_rs::id0::SegmentType;
+use idb_rs::til::TypeVariant;
use std::collections::HashSet;
/// Maps IDB data into a [`BinaryView`].
@@ -36,8 +40,28 @@ impl IDBMapper {
// Rebase the address from ida -> binja without this rebased views will fail to map.
let bn_base_address = view.start();
- let ida_base_address = id0.base_address.unwrap_or(bn_base_address);
- let base_address_delta = bn_base_address.wrapping_sub(ida_base_address);
+ let base_address_delta = match id0.base_address {
+ // There is no base address in the IDA file, so we assume everything is relative and rebase.
+ BaseAddressInfo::None => bn_base_address,
+ BaseAddressInfo::BaseSegment(start_addr) => bn_base_address.wrapping_sub(start_addr),
+ BaseAddressInfo::BaseSection(section_addr) => {
+ let bn_section_addr = view
+ .sections()
+ .iter()
+ .min_by_key(|s| s.start())
+ .map(|s| s.start());
+ match bn_section_addr {
+ Some(bn_section) => bn_section.wrapping_sub(section_addr),
+ None => bn_base_address,
+ }
+ }
+ };
+
+ tracing::debug!(
+ "Rebasing for {:0x} with delta {:0x}",
+ bn_base_address,
+ base_address_delta
+ );
let rebase = |addr: u64| -> u64 { addr.wrapping_add(base_address_delta) };
for segment in &id0.segments {
@@ -70,10 +94,16 @@ impl IDBMapper {
self.map_func_to_view(view, &til_translator, &rebased_func);
}
+ for export in &id0.exports {
+ let mut rebased_export = export.clone();
+ rebased_export.address = rebase(export.address);
+ self.map_export_to_view(view, &til_translator, &rebased_export);
+ }
+
// TODO: The below undo and ignore is not thread safe, this means that the mapper itself
// TODO: should be the only thing running at the time of the mapping process.
let undo = view.file().begin_undo_actions(true);
- for comment in &id0.comments {
+ for comment in &self.info.merged_comments() {
let mut rebased_comment = comment.clone();
rebased_comment.address = rebase(comment.address);
self.map_comment_to_view(view, &rebased_comment);
@@ -103,11 +133,11 @@ impl IDBMapper {
}
match til_translator.translate_type_info(&ty.tinfo) {
Ok(bn_ty) => {
- tracing::debug!("Mapping type: {:?}", ty);
+ tracing::debug!("Mapping type: {}", ty.name);
view.define_auto_type(&ty_name, "IDA", &bn_ty);
}
Err(err) => {
- tracing::warn!("Failed to map type {:?}: {}", ty, err)
+ tracing::warn!("Failed to map type {}: {}", ty.name, err)
}
}
}
@@ -183,6 +213,11 @@ impl IDBMapper {
pub fn map_segment_to_view(&self, view: &BinaryView, segment: &SegmentInfo) {
let semantics = match segment.ty {
SegmentType::Norm => Semantics::DefaultSection,
+ // One issue is that an IDA section named 'extern' is _actually_ a synthetic section, so we
+ // should not map it.
+ SegmentType::Xtrn if segment.name == "extern" => {
+ return;
+ }
SegmentType::Xtrn => {
// IDA definition of extern is an actual section like '.idata' whereas extern in BN
// is a synthetic section, do NOT use [`Semantics::External`].
@@ -226,12 +261,62 @@ impl IDBMapper {
view.add_section(section);
}
+ pub fn map_export_to_view(
+ &self,
+ view: &BinaryView,
+ til_translator: &TILTranslator,
+ export: &ExportInfo,
+ ) {
+ let within_code_section = view
+ .sections_at(export.address)
+ .iter()
+ .find(|s| s.semantics() == Semantics::ReadOnlyCode)
+ .is_some();
+ let is_func_ty = export
+ .ty
+ .as_ref()
+ .is_some_and(|ty| matches!(ty.type_variant, TypeVariant::Function(_)));
+
+ if within_code_section && is_func_ty {
+ tracing::debug!("Mapping function export: {:0x}", export.address);
+ let func_info = FunctionInfo {
+ name: Some(export.name.clone()),
+ ty: export.ty.clone(),
+ address: export.address,
+ is_library: false,
+ is_no_return: false,
+ };
+ self.map_func_to_view(view, til_translator, &func_info);
+ } else {
+ tracing::debug!("Mapping data export: {:0x}", export.address);
+ let name_info = NameInfo {
+ label: Some(export.name.clone()),
+ ty: export.ty.clone(),
+ address: export.address,
+ exported: true,
+ };
+ self.map_name_to_view(view, til_translator, &name_info);
+ }
+ }
+
pub fn map_func_to_view(
&self,
view: &BinaryView,
til_translator: &TILTranslator,
func: &FunctionInfo,
) {
+ // We need to skip things that hit the extern section, since they do not have a bearing in the
+ // actual context of the binary, and can be derived differently between IDA and Binja.
+ let within_extern_section = view
+ .sections_at(func.address)
+ .iter()
+ .find(|s| s.semantics() == Semantics::External)
+ .is_some();
+ if within_extern_section {
+ tracing::debug!("Skipping function in extern section: {:0x}", func.address);
+ return;
+ }
+
let Some(bn_func) = view.add_auto_function(func.address) else {
tracing::warn!("Failed to add function for {:0x}", func.address);
return;
@@ -277,6 +362,18 @@ impl IDBMapper {
til_translator: &TILTranslator,
name: &NameInfo,
) {
+ // We need to skip things that hit the extern section, since they do not have a bearing in the
+ // actual context of the binary, and can be derived differently between IDA and Binja.
+ let within_extern_section = view
+ .sections_at(name.address)
+ .iter()
+ .find(|s| s.semantics() == Semantics::External)
+ .is_some();
+ if within_extern_section {
+ tracing::debug!("Skipping name in extern section: {:0x}", name.address);
+ return;
+ }
+
// Currently, we only want to use name info to map data variables, so skip anything in code.
let within_code_section = view
.sections_at(name.address)
@@ -289,7 +386,13 @@ impl IDBMapper {
}
if let Some(label) = &name.label {
- let symbol = Symbol::builder(SymbolType::Data, &label, name.address).create();
+ let binding = name
+ .exported
+ .then_some(Binding::Global)
+ .unwrap_or(Binding::None);
+ let symbol = Symbol::builder(SymbolType::Data, &label, name.address)
+ .binding(binding)
+ .create();
tracing::debug!("Mapping name label: {:0x} => {}", name.address, symbol);
view.define_auto_symbol(&symbol);
}
diff --git a/plugins/idb_import/src/parse.rs b/plugins/idb_import/src/parse.rs
index 72ac1baa..95a2fdd0 100644
--- a/plugins/idb_import/src/parse.rs
+++ b/plugins/idb_import/src/parse.rs
@@ -1,6 +1,6 @@
//! Parse the provided IDB / TIL file and extract information into a struct for further processing.
-use idb_rs::addr_info::AddressInfo;
+use idb_rs::addr_info::{all_address_info, AddressInfo};
use idb_rs::id0::function::{FuncIdx, FuncordsIdx, IDBFunctionType};
use idb_rs::id0::{ID0Section, Netdelta, SegmentType};
use idb_rs::id1::ID1Section;
@@ -27,7 +27,13 @@ pub struct FunctionInfo {
pub address: u64,
pub is_library: bool,
pub is_no_return: bool,
- pub is_entry: bool,
+}
+
+#[derive(Debug, Clone, Serialize)]
+pub struct ExportInfo {
+ pub name: String,
+ pub address: u64,
+ pub ty: Option<idb_rs::til::Type>,
}
#[derive(Debug, Clone, Serialize)]
@@ -35,6 +41,7 @@ pub struct NameInfo {
pub address: u64,
pub ty: Option<idb_rs::til::Type>,
pub label: Option<String>,
+ pub exported: bool,
}
#[derive(Debug, Clone, Serialize)]
@@ -57,12 +64,27 @@ pub struct FunctionCordInfo {
}
#[derive(Debug, Clone, Serialize)]
+pub enum BaseAddressInfo {
+ /// The base address is not specified in the IDB.
+ None,
+ /// The base address is the absolute address of the first byte of the executable.
+ ///
+ /// To get a delta, calculate the difference between the base address and the address of the lowest segment.
+ BaseSegment(u64),
+ /// The base address is the address of the first byte of the lowest section.
+ ///
+ /// To get a delta, calculate the difference between the base address and the address of the lowest section.
+ BaseSection(u64),
+}
+
+#[derive(Debug, Clone, Serialize)]
pub struct ID0Info {
- pub base_address: Option<u64>,
+ pub base_address: BaseAddressInfo,
pub segments: Vec<SegmentInfo>,
pub functions: Vec<FunctionInfo>,
pub comments: Vec<CommentInfo>,
pub labels: Vec<LabelInfo>,
+ pub exports: Vec<ExportInfo>,
}
#[derive(Debug, Clone, Serialize)]
@@ -71,6 +93,7 @@ pub struct DirTreeInfo {
pub types: Vec<TILTypeInfo>,
/// Contains both function and data names (along with their types).
pub names: Vec<NameInfo>,
+ pub comments: Vec<CommentInfo>,
}
#[derive(Debug, Clone, Serialize, Default)]
@@ -124,6 +147,7 @@ impl IDBInfo {
address: label.address,
ty: None,
label: Some(label.label.clone()),
+ exported: false,
});
}
@@ -132,6 +156,7 @@ impl IDBInfo {
address: func.address,
ty: func.ty.clone(),
label: func.name.clone(),
+ exported: false,
});
}
}
@@ -172,6 +197,24 @@ impl IDBInfo {
});
types
}
+
+ pub fn merged_comments(&self) -> Vec<CommentInfo> {
+ let mut comments = Vec::new();
+ if let Some(id0) = &self.id0 {
+ comments.extend(id0.comments.clone());
+ }
+ if let Some(dir_tree) = &self.dir_tree {
+ comments.extend(dir_tree.comments.clone());
+ }
+ comments.sort_by_key(|c| c.address);
+ comments.dedup_by(|a, b| {
+ if a.address != b.address {
+ return false;
+ }
+ a.is_repeatable == b.is_repeatable
+ });
+ comments
+ }
}
/// Parsed the IDB data into [`IDBInfo`].
@@ -314,32 +357,32 @@ impl IDBFileParser {
address: func_start,
is_library: func.flags.is_lib(),
is_no_return: func.flags.is_no_return(),
- is_entry: false,
});
}
}
}
}
+ let mut exports = Vec::new();
if let Ok(entry_points) = id0.entry_points(&root_info) {
for entry in entry_points {
- // TODO: What to do with entry.forwarded?
- functions.push(FunctionInfo {
- name: Some(entry.name),
- ty: entry.entry_type,
+ exports.push(ExportInfo {
+ name: entry.name,
address: entry.address.into_u64(),
- is_library: false,
- is_no_return: false,
- is_entry: true,
+ ty: entry.entry_type,
});
}
}
- let base_address = match root_info.addresses.loading_base.into_u64() {
+ let min_ea = root_info.addresses.min_ea.into_raw().into_u64();
+ let loading_base = root_info.addresses.loading_base.into_u64();
+ let base_address = match (loading_base, min_ea) {
+ (0, 0) => BaseAddressInfo::None,
// An IDB with zero loading base is possibly not loaded there.
// For example, see the FlawedGrace.idb in the idb-rs resources directory.
- 0 => None,
- loading_base => Some(loading_base.into_u64()),
+ // Instead, we will want to use the lowest section address.
+ (0, min_ea) => BaseAddressInfo::BaseSection(min_ea),
+ (loading_base, _) => BaseAddressInfo::BaseSegment(loading_base.into_u64()),
};
Ok(ID0Info {
@@ -348,6 +391,7 @@ impl IDBFileParser {
functions,
comments,
labels,
+ exports,
})
}
@@ -421,10 +465,51 @@ impl IDBFileParser {
address: func_addr,
is_library: false,
is_no_return: false,
- is_entry: false,
}))
};
+ let comment_info_from_addr = |addr_info: &AddressInfo<K>| -> Vec<CommentInfo> {
+ let mut comments = Vec::new();
+ if let Some(comment) = addr_info.comment() {
+ comments.push(CommentInfo {
+ address: addr_info.address().into_raw().into_u64(),
+ comment: comment.to_string(),
+ is_repeatable: false,
+ });
+ }
+ if let Some(comment) = addr_info.comment_repeatable() {
+ comments.push(CommentInfo {
+ address: addr_info.address().into_raw().into_u64(),
+ comment: comment.to_string(),
+ is_repeatable: true,
+ })
+ }
+ if let Some(pre_comments) = addr_info.comment_pre() {
+ for comment in pre_comments {
+ comments.push(CommentInfo {
+ address: addr_info.address().into_raw().into_u64(),
+ comment: comment.to_string(),
+ is_repeatable: false,
+ })
+ }
+ }
+ if let Some(post_comments) = addr_info.comment_post() {
+ for comment in post_comments {
+ comments.push(CommentInfo {
+ address: addr_info.address().into_raw().into_u64(),
+ comment: comment.to_string(),
+ is_repeatable: false,
+ })
+ }
+ }
+ comments
+ };
+
+ let mut comments = Vec::new();
+ for (addr_info, _) in all_address_info(id0, id1, id2, netdelta) {
+ comments.extend(comment_info_from_addr(&addr_info));
+ }
+
let mut functions = Vec::new();
if let Some(func_dir_tree) = id0.dirtree_function_address()? {
func_dir_tree.visit_leafs(|addr_raw| {
@@ -446,6 +531,7 @@ impl IDBFileParser {
address: info.address().into_raw().into_u64(),
ty: info.tinfo(&root_info).ok().flatten().map(|t| t.clone()),
label: info.label().ok().flatten().map(|s| s.to_string()),
+ exported: false,
});
}
});
@@ -466,6 +552,7 @@ impl IDBFileParser {
functions,
types,
names,
+ comments,
})
}
}