Skip to content

Implement idb_import dependency prompts. #6760

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
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
234 changes: 182 additions & 52 deletions plugins/idb_import/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
mod types;
use std::fs::File;
use std::io::BufReader;

use binaryninja::background_task::BackgroundTask;
use binaryninja::command::Command;
use binaryninja::string::BnStrCompatible;
use binaryninja::type_library::TypeLibrary;
use binaryninja::types::QualifiedName;
use types::*;
mod addr_info;
use addr_info::*;
Expand All @@ -14,7 +22,7 @@ use idb_rs::til::TypeVariant as TILTypeVariant;

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

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

struct IDBDebugInfoParser;
Expand All @@ -36,7 +44,7 @@ impl CustomDebugInfoParser for IDBDebugInfoParser {
debug_file: &BinaryView,
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
) -> bool {
match parse_idb_info(debug_info, bv, debug_file, progress) {
match import_idb_info(debug_info, bv, debug_file, progress) {
Ok(()) => true,
Err(error) => {
error!("Unable to parse IDB file: {error}");
Expand All @@ -59,11 +67,11 @@ impl CustomDebugInfoParser for TILDebugInfoParser {
fn parse_info(
&self,
debug_info: &mut DebugInfo,
_bv: &BinaryView,
bv: &BinaryView,
debug_file: &BinaryView,
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
) -> bool {
match parse_til_info(debug_info, debug_file, progress) {
match import_til_info_from_debug_file(debug_info, bv, debug_file, progress) {
Ok(()) => true,
Err(error) => {
error!("Unable to parse TIL file: {error}");
Expand All @@ -73,6 +81,20 @@ impl CustomDebugInfoParser for TILDebugInfoParser {
}
}

struct LoadTilFile;

impl Command for LoadTilFile {
fn action(&self, view: &BinaryView) {
if let Err(error) = background_import_til(view) {
error!("Unable to convert TIL file: {error}");
}
}

fn valid(&self, _view: &BinaryView) -> bool {
true
}
}

struct BinaryViewReader<'a> {
bv: &'a BinaryView,
offset: u64,
Expand Down Expand Up @@ -105,69 +127,195 @@ impl std::io::Seek for BinaryViewReader<'_> {
}
}

fn parse_idb_info(
fn import_idb_info<P: Fn(usize, usize) -> Result<(), ()>>(
debug_info: &mut DebugInfo,
bv: &BinaryView,
debug_file: &BinaryView,
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
progress: P,
) -> Result<()> {
trace!("Opening a IDB file");
let file = BinaryViewReader {
let file = BufReader::new(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() {
// TODO handle dependency, create a function for that with closures
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)?;
let filename = debug_file.file().filename();
// TODO progress 0%-50%
import_til_to_type_library(til, filename, bv, 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%
// TODO progress 50%-100%
parse_id0_section_info(debug_info, bv, debug_file, &id0)?;
}

Ok(())
}

fn parse_til_info(
debug_info: &mut DebugInfo,
fn import_til_info_from_debug_file<P: Fn(usize, usize) -> Result<(), ()>>(
_debug_info: &mut DebugInfo,
bv: &BinaryView,
debug_file: &BinaryView,
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
progress: P,
) -> Result<()> {
trace!("Opening a TIL file");
let file = BinaryViewReader {
bv: debug_file,
offset: 0,
};
let filename = debug_file.file().filename();
let mut file = std::io::BufReader::new(file);
trace!("Parsing the TIL section");
let til = TILSection::read(&mut file, idb_rs::IDBSectionCompression::None)?;
import_til_section(debug_info, debug_file, &til, progress)

import_til_to_type_library(til, filename, bv, progress)
}

pub fn import_til_section(
debug_info: &mut DebugInfo,
debug_file: &BinaryView,
til: &TILSection,
progress: impl Fn(usize, usize) -> Result<(), ()>,
fn background_import_til(view: &BinaryView) -> Result<()> {
let moved_view = view.to_owned();
binaryninja::worker_thread::execute_on_worker_thread_interactive(c"Til Import", move || {
if let Err(err) = interactive_import_til(&moved_view) {
error!("Unable to import TIL: {err}");
}
});
Ok(())
}

fn interactive_import_til(view: &BinaryView) -> Result<()> {
let bt = BackgroundTask::new("Import TIL", true);
let Some(file) =
binaryninja::interaction::get_open_filename_input("Select a .til file", "*.til")
else {
return Ok(());
};

let filename = file.file_name().unwrap().to_string_lossy();
let mut file = BufReader::new(File::open(&file)?);
let til = TILSection::read(&mut file, idb_rs::IDBSectionCompression::None)?;

let progress = |current, total| {
if bt.is_cancelled() {
return Err(());
}
bt.set_progress_text(format!(
"Import TIL progress: {}%",
((current as f32 / total as f32) * 100f32) as u32
));
Ok(())
};
import_til_to_type_library(til, filename, view, progress)?;
bt.finish();
Ok(())
}

fn import_til_to_type_library<S, P>(
til: TILSection,
type_lib_name: S,
view: &BinaryView,
progress: P,
) -> Result<()>
where
S: BnStrCompatible,
P: Fn(usize, usize) -> Result<(), ()>,
{
let default_arch = view
.default_arch()
.ok_or_else(|| anyhow!("Unable to get the default arch"))?;
let mut type_lib = TypeLibrary::new(default_arch, type_lib_name);

// TODO create a background task to not freeze bn, also create a progress
// user interface feedback
import_til_file(til, view, &mut type_lib, progress)?;
if !type_lib.finalize() {
return Err(anyhow!("Unable to finalize TypeLibrary"));
};
view.add_type_library(&type_lib);
Ok(())
}

fn import_til_file<P: Fn(usize, usize) -> Result<(), ()>>(
til: TILSection,
view: &BinaryView,
type_library: &mut TypeLibrary,
progress: P,
) -> Result<()> {
trace!("Parsing the TIL section");
// TODO
let mut tils = vec![til];
import_til_dependency(&mut tils, 0, &progress)?;
import_til_section(type_library, view, &tils, progress)
}

fn import_til_dependency<P: Fn(usize, usize) -> Result<(), ()>>(
tils: &mut Vec<TILSection>,
til_idx: usize,
_progress: &P,
) -> Result<()> {
let names: Vec<_> = tils[til_idx].header.dependencies.to_vec();
for name in names {
let name = name.as_utf8_lossy();
let message = format!("Select the dependency \"{name}.til\"",);
let Some(dep_file) = binaryninja::interaction::get_open_filename_input(&message, "*.til")
else {
return Err(anyhow!("Unable to get the dependency {name}"));
};

let mut dep_file = BufReader::new(File::open(&dep_file)?);
let dep_til = TILSection::read(&mut dep_file, idb_rs::IDBSectionCompression::None)?;
tils.push(dep_til);
}
if tils.len() > til_idx + 1 {
// add dependencies of dependencies
// TODO handle progress
// TODO identify ciclycal dependencies
import_til_dependency(tils, til_idx + 1, _progress)
.context("While importing dependency {name}")?;
}
Ok(())
}

fn import_til_section<P: Fn(usize, usize) -> Result<(), ()>>(
type_library: &mut TypeLibrary,
view: &BinaryView,
tils: &[TILSection],
progress: P,
) -> Result<()> {
let types = types::translate_til_types(debug_file.default_arch().unwrap(), til, progress)?;
let default_arch = view
.default_arch()
.ok_or_else(|| anyhow!("Unable to get the default arch"))?;
let types = types::translate_til_types(default_arch, tils, progress)?;

// print any errors
print_til_convertsion_errors(&types)?;

// add all type to the type library
for ty in &types {
if let TranslateTypeResult::Translated(bn_ty)
| TranslateTypeResult::PartiallyTranslated(bn_ty, _) = &ty.ty
{
let name = QualifiedName::new(vec![ty.name.as_utf8_lossy().to_string()]);
type_library.add_named_type(name, bn_ty);
}
}

Ok(())
}

fn print_til_convertsion_errors(types: &[TranslatesIDBType]) -> Result<()> {
for ty in types {
match &ty.ty {
TranslateTypeResult::NotYet => {
panic!(
"type could not be processed `{}`: {:#?}",
// NOTE this should be unreachable
error!(
"Unable to finish parsing type `{}`",
ty.name.as_utf8_lossy(),
&ty.og_ty
);
}
TranslateTypeResult::Error(error) => {
Expand All @@ -179,7 +327,7 @@ pub fn import_til_section(
TranslateTypeResult::PartiallyTranslated(_, error) => {
if let Some(error) = error {
error!(
"Unable to parse type `{}` correctly: {error}",
"Unable to correctly parse type `{}`: {error}",
ty.name.as_utf8_lossy(),
);
} else {
Expand All @@ -192,29 +340,6 @@ pub fn import_til_section(
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(ty.name.as_utf8_lossy(), bn_ty, &[/* TODO */]) {
error!("Unable to add type `{}`", ty.name.as_utf8_lossy())
}
}
}

// 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 */]) {
error!("Unable to fix type `{}`", ty.name.as_utf8_lossy())
}
}
}

Ok(())
}

Expand Down Expand Up @@ -330,7 +455,12 @@ 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);
binaryninja::command::register_command(
c"Import TIL types",
c"Convert and import a TIL file into a TypeLibrary",
LoadTilFile,
);
DebugInfoParser::register(c"IDB Parser", IDBDebugInfoParser);
DebugInfoParser::register(c"TIL Parser", TILDebugInfoParser);
true
}
Loading