use crate::lift::Lifter;
use crate::lift::headless::{Headless, Invocation};
use crate::{Error, Result};
use std::ffi::OsString;
use std::path::{Path, PathBuf};
pub const DEFAULT_BINARY_NINJA_SCRIPT: &str =
include_str!("../../assets/lifters/binaryninja/scripts/BnilLifter.py");
pub struct BinaryNinjaLifter {
home: PathBuf,
level: &'static str,
script: Option<PathBuf>,
intermediate_dir: Option<PathBuf>,
}
impl BinaryNinjaLifter {
pub fn new(
home: PathBuf,
level: &'static str,
script: Option<PathBuf>,
intermediate_dir: Option<PathBuf>,
) -> Self {
Self {
home,
level,
script,
intermediate_dir,
}
}
}
impl Lifter for BinaryNinjaLifter {
fn lift(&self, input: &Path, output: &Path) -> Result<()> {
let bnpython = self.home.join("bnpython3");
if !bnpython.exists() {
return Err(Error::Parse(format!(
"Binary Ninja's headless Python not found at {:?}",
bnpython
)));
}
Headless {
tool: "Binary Ninja",
program: &bnpython,
script: self.script.as_deref(),
default_script: ("BnilLifter.py", DEFAULT_BINARY_NINJA_SCRIPT),
work_dir: self.intermediate_dir.as_deref(),
}
.lift(input, output, |i| command_line(self.level, i))
}
}
fn command_line(level: &'static str, i: &Invocation) -> Vec<OsString> {
vec![
i.script_dir.join(&i.script_name).into_os_string(),
i.input.clone().into_os_string(),
level.into(),
format!("{}.json", i.name).into(),
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_the_built_in_script_is_compiled_in() {
assert!(
DEFAULT_BINARY_NINJA_SCRIPT.contains("import binaryninja"),
"the built-in script is not the lifter"
);
}
#[test]
fn test_the_script_is_given_its_arguments_in_the_order_it_reads_them() {
let i = Invocation {
input: PathBuf::from("/bin/sample"),
name: "sample".to_string(),
work_dir: PathBuf::from("/work"),
script_dir: PathBuf::from("/scripts"),
script_name: OsString::from("BnilLifter.py"),
};
assert_eq!(
command_line("mlil", &i),
vec![
i.script_dir.join(&i.script_name).into_os_string(),
i.input.clone().into_os_string(),
OsString::from("mlil"),
OsString::from("sample.json"),
]
);
}
#[test]
fn test_a_missing_headless_python_is_named() {
let lifter = BinaryNinjaLifter::new(
PathBuf::from("/oinkie-no-such-binary-ninja"),
"llil",
None,
None,
);
let err = lifter
.lift(Path::new("in"), Path::new("out"))
.unwrap_err()
.to_string();
assert!(
err.contains("bnpython3"),
"does not name what is missing: {err}"
);
}
}