Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/idb_import/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ crate-type = ["cdylib"]
anyhow = { version = "1.0.86", features = ["backtrace"] }
binaryninja.workspace = true
binaryninjacore-sys.workspace = true
idb-rs = { git = "https://github.com/Vector35/idb-rs", tag = "0.1.10" }
idb-rs = { git = "https://github.com/Vector35/idb-rs", tag = "0.1.12" }
log = "0.4"
75 changes: 34 additions & 41 deletions plugins/idb_import/src/addr_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,49 @@ use std::collections::HashMap;

use anyhow::Result;

use idb_rs::id0::ID0Section;
use idb_rs::{til, IDAKind};
use idb_rs::addr_info::all_address_info;
use idb_rs::id0::{ID0Section, Netdelta};
use idb_rs::id1::ID1Section;
use idb_rs::id2::ID2Section;
use idb_rs::{til, Address, IDAKind};

#[derive(Default)]
pub struct AddrInfo<'a> {
// TODO does binja diferenciate comments types on the API?
pub comments: Vec<&'a [u8]>,
pub label: Option<Cow<'a, str>>,
// TODO does binja differentiate comments types on the API?
pub comments: Vec<Vec<u8>>,
pub label: Option<Cow<'a, [u8]>>,
// TODO make this a ref
pub ty: Option<til::Type>,
}

pub fn get_info<K: IDAKind>(
id0: &ID0Section<K>,
version: u16,
) -> Result<HashMap<K::Usize, AddrInfo<'_>>> {
use idb_rs::id0::FunctionsAndComments::*;
let mut addr_info: HashMap<K::Usize, AddrInfo> = HashMap::new();

// the old style comments, most likely empty on new versions
let old_comments = id0.functions_and_comments()?.filter_map(|fc| match fc {
Err(e) => Some(Err(e)),
Ok(Comment { address, comment }) => Some(Ok((address, comment))),
Ok(Name | Function(_) | Unknown { .. }) => None,
});
for old_comment in old_comments {
let (addr, comment) = old_comment?;
let comment = comment.message();
addr_info.entry(addr).or_default().comments.push(comment);
}
pub fn get_info<'a, K: IDAKind>(
id0: &'a ID0Section<K>,
id1: &ID1Section,
id2: Option<&ID2Section<K>>,
netdelta: Netdelta<K>,
) -> Result<HashMap<Address<K>, AddrInfo<'a>>> {
let mut addr_info: HashMap<Address<K>, AddrInfo> = HashMap::new();

// comments defined on the address information
for info in id0.address_info(version)? {
use idb_rs::id0::AddressInfo::*;
let (addr, info) = info?;
let entry = addr_info.entry(addr).or_default();
match info {
Comment(comments) => entry.comments.push(comments.message()),
Label(name) => {
if let Some(_old) = entry.label.replace(name) {
panic!("Duplicated label for an address should be impossible this is most likelly a programing error")
}
}
TilType(ty) => {
if let Some(_old) = entry.ty.replace(ty) {
panic!("Duplicated type for an address should be impossible this is most likelly a programing error")
}
}
Other { .. } => {}
DefinedStruct(_) => {}
for (info, _info_size) in all_address_info(id0, id1, id2, netdelta) {
let entry = addr_info.entry(info.address()).or_default();
if let Some(comment) = info.comment() {
entry.comments.push(comment.to_vec());
}
if let Some(comment) = info.comment_repeatable() {
entry.comments.push(comment.to_vec());
}
if let Some(comment) = info.comment_pre() {
entry.comments.extend(comment.map(|line| line.to_vec()));
}
if let Some(comment) = info.comment_post() {
entry.comments.extend(comment.map(|line| line.to_vec()));
}
if let Some(label) = info.label()? {
entry.label = Some(label);
}
if let Some(ty) = info.tinfo()? {
entry.ty = Some(ty);
}
}

Expand Down
141 changes: 100 additions & 41 deletions plugins/idb_import/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
mod types;
use idb_rs::{IDAKind, IDAUsize};
use std::borrow::Cow;
use std::io::{BufRead, Cursor, Seek};

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::*;
Expand All @@ -9,13 +14,13 @@ use binaryninja::debuginfo::{
CustomDebugInfoParser, DebugFunctionInfo, DebugInfo, DebugInfoParser,
};

use idb_rs::id0::{ID0Section, IDBParam1, IDBParam2};
use idb_rs::id0::{ID0Section, ID0SectionVariants};
use idb_rs::til::section::TILSection;
use idb_rs::til::TypeVariant as TILTypeVariant;

use log::{error, trace, warn, LevelFilter};

use anyhow::Result;
use anyhow::{anyhow, Result};
use binaryninja::logger::Logger;

struct IDBDebugInfoParser;
Expand Down Expand Up @@ -81,9 +86,11 @@ struct BinaryViewReader<'a> {
impl std::io::Read for BinaryViewReader<'_> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

impl Seek for BinaryReader {
/// Seek to the specified position.
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
match pos {
SeekFrom::Current(offset) => self.seek_to_relative_offset(offset),
SeekFrom::Start(offset) => self.seek_to_offset(offset),
SeekFrom::End(end_offset) => {
// We do NOT need to add the image base here as
// the reader (unlike the writer) can set the virtual base.
let offset =
self.view
.len()
.checked_add_signed(end_offset)
.ok_or(std::io::Error::new(
ErrorKind::Other,
"Seeking from end overflowed",
))?;
self.seek_to_offset(offset);
}
};
Ok(self.offset())
}
}
impl Read for BinaryReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let len = buf.len();
let result = unsafe { BNReadData(self.handle, buf.as_mut_ptr() as *mut _, len) };
if !result {
Err(std::io::Error::new(ErrorKind::Other, "Read out of bounds"))
} else {
Ok(len)
}
}
}

As per the discussion we had in this PR swap BinaryViewReader out for the one in bindings

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, ""));
// 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);
let len = BinaryView::read(self.bv, buf, self.offset);
self.offset += u64::try_from(len).unwrap();
Ok(len)
}
Expand All @@ -96,10 +103,17 @@ impl std::io::Seek for BinaryViewReader<'_> {
std::io::SeekFrom::End(end) => self.bv.len().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, ""))?;
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, ""));
return Err(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"Try to set invalid offset in BinaryViewReader",
));
}
self.offset = new_offset;
Ok(new_offset)
Expand All @@ -118,27 +132,73 @@ fn parse_idb_info(
offset: 0,
};
trace!("Parsing a IDB file");
let file = std::io::BufReader::new(file);
let mut parser = idb_rs::IDBParserVariants::new(file)?;
if let Some(til_section) = parser.til_section_offset() {
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() {
trace!("Parsing the TIL section");
let til = parser.read_til_section(til_section)?;
let til = format.read_til(&mut idb_data, til_idx)?;
// 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%
match id0 {
idb_rs::IDAVariants::IDA32(id0) => {
parse_id0_section_info::<idb_rs::IDA32>(debug_info, bv, debug_file, &id0)?
}
idb_rs::IDAVariants::IDA64(id0) => {
parse_id0_section_info::<idb_rs::IDA64>(debug_info, bv, debug_file, &id0)?
}
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(())
Expand Down Expand Up @@ -231,23 +291,20 @@ fn parse_id0_section_info<K: IDAKind>(
bv: &BinaryView,
debug_file: &BinaryView,
id0: &ID0Section<K>,
id1: &ID1Section,
id2: Option<&ID2Section<K>>,
) -> Result<()> {
let (version, idb_baseaddr) = match id0.ida_info()? {
idb_rs::id0::IDBParam::V1(IDBParam1 {
version, baseaddr, ..
})
| idb_rs::id0::IDBParam::V2(IDBParam2 {
version, baseaddr, ..
}) => (version, baseaddr.into_u64()),
};

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 wrapp here and while using translating
// 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, version)? {
let addr = addr_delta.wrapping_add(idb_addr.into_u64());
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 {
Expand Down Expand Up @@ -279,6 +336,8 @@ fn parse_id0_section_info<K: IDAKind>(
}
});

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() {
Expand All @@ -287,7 +346,7 @@ fn parse_id0_section_info<K: IDAKind>(
if !debug_info.add_function(&DebugFunctionInfo::new(
None,
None,
label.map(|x| x.to_string()),
label.map(Cow::into_owned),
bnty,
Some(addr),
None,
Expand All @@ -298,15 +357,15 @@ fn parse_id0_section_info<K: IDAKind>(
}
}
(label, Some(_ty), Some(bnty)) => {
let label: Option<&str> = label.as_ref().map(|x| x.as_ref());
if !debug_info.add_data_variable(addr, &bnty, label, &[]) {
if !debug_info.add_data_variable(addr, &bnty, label.as_ref().map(Cow::as_ref), &[])
{
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?
error!("Unable to convert type {addr:#x}");
// TODO how to add a label without a type associacted with it?
// TODO how to add a label without a type associated with it?
if let Some(name) = label {
if !debug_info.add_data_variable(
addr,
Expand All @@ -319,7 +378,7 @@ fn parse_id0_section_info<K: IDAKind>(
}
}
(Some(name), None, None) => {
// TODO how to add a label without a type associacted with it?
// TODO how to add a label without a type associated with it?
if !debug_info.add_data_variable(
addr,
&binaryninja::types::Type::void(),
Expand Down
33 changes: 26 additions & 7 deletions plugins/idb_import/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use binaryninja::types::{
};
use idb_rs::til::function::CallingConvention as TILCallingConvention;
use idb_rs::til::pointer::Pointer as TILPointer;
use idb_rs::til::r#enum::EnumMembers;
use idb_rs::til::{
array::Array as TILArray, function::Function as TILFunction, r#enum::Enum as TILEnum,
r#struct::Struct as TILStruct, r#struct::StructMember as TILStructMember,
Expand Down Expand Up @@ -486,14 +487,32 @@ impl<F: Fn(usize, usize) -> Result<(), ()>> TranslateIDBTypes<'_, F> {

fn translate_enum(&self, ty_enum: &TILEnum) -> Ref<Type> {
let mut eb = EnumerationBuilder::new();
for (i, member) in ty_enum.members.iter().enumerate() {
let name = member
.name
.as_ref()
.map(|name| name.as_utf8_lossy().to_string())
.unwrap_or_else(|| format!("member_{i}"));
eb.insert(&name, member.value);
match &ty_enum.members {
EnumMembers::Regular(enum_members) => {
for (i, member) in enum_members.iter().enumerate() {
let name = member
.name
.as_ref()
.map(|name| name.as_utf8_lossy().to_string())
.unwrap_or_else(|| format!("member_{i}"));
eb.insert(&name, member.value);
}
}
EnumMembers::Groups(enum_groups) => {
for group in enum_groups {
// TODO implement groups to the importer
for (i, member) in group.sub_fields.iter().enumerate() {
let name = member
.name
.as_ref()
.map(|name| name.as_utf8_lossy().to_string())
.unwrap_or_else(|| format!("member_{i}"));
eb.insert(&name, member.value);
}
}
}
}

Type::enumeration(
&eb.finalize(),
// TODO: This looks bad, look at the comment in [`Type::width`].
Expand Down
Loading