From e6292008948279c9b9a60564d15c422d0b996c6b Mon Sep 17 00:00:00 2001 From: KyleMiles Date: Mon, 28 Jun 2021 22:13:43 -0400 Subject: [PATCH] Resolves #2521 --- rust/README.md | 55 +------------- rust/binaryninjacore-sys/build.rs | 92 ++++------------------- rust/examples/basic_script/CMakeLists.txt | 85 +++++++++++++++++++++ rust/examples/basic_script/build.rs | 67 +++++++++++++++++ rust/examples/dwarfdump/build.rs | 67 +++++++++++++++++ rust/examples/flowgraph/build.rs | 67 +++++++++++++++++ rust/examples/template/CMakeLists.txt | 89 ++++++++++++++++++++++ rust/examples/template/Cargo.toml | 16 ++++ rust/examples/template/README.md | 17 +++++ rust/examples/template/build.rs | 67 +++++++++++++++++ rust/examples/template/src/main.rs | 46 ++++++++++++ 11 files changed, 537 insertions(+), 131 deletions(-) create mode 100644 rust/examples/basic_script/CMakeLists.txt create mode 100644 rust/examples/basic_script/build.rs create mode 100644 rust/examples/dwarfdump/build.rs create mode 100644 rust/examples/flowgraph/build.rs create mode 100644 rust/examples/template/CMakeLists.txt create mode 100644 rust/examples/template/Cargo.toml create mode 100644 rust/examples/template/README.md create mode 100644 rust/examples/template/build.rs create mode 100644 rust/examples/template/src/main.rs diff --git a/rust/README.md b/rust/README.md index 60be28b2d..39b5e9b0b 100644 --- a/rust/README.md +++ b/rust/README.md @@ -4,7 +4,7 @@ > :warning: **These bindings are in a very early beta, only have partial support for the core APIs and are still actively under development. Compatibility _will_ break and conventions _will_ change! They are being used for core Binary Ninja features however, so we expect much of what is already there to be reliable enough to build on, just don't be surprised if your plugins/scripts need to hit a moving target.** -> :warning: This project requires Rust Nightly to build with those fancy linker arguments +> :warning: This project requires **Rust Nightly** to build with those fancy linker arguments ## Dependencies @@ -16,58 +16,7 @@ Rust **Nightly** ## How to use -### To write a plugin: - -`Cargo.toml`: -``` -[lib] -crate-type = ["cdylib"] - -[dependencies] -binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"} -``` - -`src/main.rs`: -See the `./examples/`. Plugin registration commands are in `binaryninja::command::*` - - -### To write a headless script: - -`Cargo.toml`: -``` -[dependencies] -binaryninja = { git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"} -``` - -`src/main.rs`: -``` -use binaryninja::version; -use binaryninja::architecture::Architecture; -use binaryninja::binaryview::{BinaryViewBase, BinaryViewExt}; - -fn main() { - println!("BinaryNinja Version: `{}`", version()); - - println!("Loading plugins..."); // This loads all the core architecture, platform, etc plugins - binaryninja::headless::init(); - - println!("Loading binary..."); - let bv = binaryninja::open_view("/bin/cat").expect("Couldn't open `/bin/cat`"); - - println!("Filename: `{}`", bv.metadata().filename()); - println!("File size: `{:#x}`", bv.len()); - println!("Function count: {}", bv.functions().len()); - - for func in &bv.functions() { - println!(" `{}`:", func.symbol().full_name()); - } - - // Important! You need to call shutdown or your script will hang forever - binaryninja::headless::shutdown(); -} -``` - -All headless scripts should call both `binaryninja::headless::init()` and `binaryninja::headless::shutdown()`. +See [`examples/template`](examples/template). --- diff --git a/rust/binaryninjacore-sys/build.rs b/rust/binaryninjacore-sys/build.rs index 38a656e62..70bd1fa77 100644 --- a/rust/binaryninjacore-sys/build.rs +++ b/rust/binaryninjacore-sys/build.rs @@ -6,43 +6,6 @@ use std::io::BufRead; use std::io::BufReader; use std::path::PathBuf; -#[cfg(target_os = "macos")] -static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun"); - -#[cfg(target_os = "linux")] -static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun"); - -#[cfg(windows)] -static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun"); - -// Check last run location for path to BinaryNinja; Otherwise check the default install locations -fn link_path() -> PathBuf { - use std::io::prelude::*; - - let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap()); - let lastrun = PathBuf::from(&home).join(LASTRUN_PATH.1); - - File::open(lastrun) - .and_then(|f| { - let mut binja_path = String::new(); - let mut reader = BufReader::new(f); - - reader.read_line(&mut binja_path)?; - Ok(PathBuf::from(binja_path.trim())) - }) - .unwrap_or_else(|_| { - #[cfg(target_os = "macos")] - return PathBuf::from("/Applications/Binary Ninja.app/Contents/MacOS"); - - #[cfg(target_os = "linux")] - return home.join("binaryninja"); - - #[cfg(windows)] - return PathBuf::from(env::var("PROGRAMFILES").unwrap()) - .join("Vector35\\BinaryNinja\\"); - }) -} - fn main() { println!("cargo:rerun-if-changed=../../binaryninjacore.h"); @@ -54,38 +17,6 @@ fn main() { let llvm_version = env::var("LLVM_VERSION"); let llvm_install_dir = env::var("LLVM_INSTALL_DIR"); - // Use BINARYNINJADIR first for custom BN builds/configurations (BN devs/build server), fallback on defaults - let install_path = env::var("BINARYNINJADIR") - .map(PathBuf::from) - .unwrap_or_else(|_| link_path()); - - #[cfg(target_os = "linux")] - println!( - "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-l:libbinaryninjacore.so.1", - install_path.to_str().unwrap(), - install_path.to_str().unwrap(), - ); - - #[cfg(not(target_os = "linux"))] - println!( - "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-lbinaryninjacore", - install_path.to_str().unwrap(), - install_path.to_str().unwrap(), - ); - - // TODO : Clean this up even more - #[warn(unused_assignments)] - let is_mac = { - #[cfg(target_os = "macos")] - { - true - } - #[cfg(not(target_os = "macos"))] - { - false - } - }; - let current_line = "#define BN_CURRENT_UI_ABI_VERSION "; let minimum_line = "#define BN_MINIMUM_UI_ABI_VERSION "; let mut current_version = "0".to_string(); @@ -100,8 +31,6 @@ fn main() { } } - // Difference between global LLVM/Clang install and custom LLVM/Clang install... - // First option is for the build server, second option is being nice to our dev who have `LLVM_INSTALL_DIR` set, third is for people with "normal" setups (and Macs) let mut bindings = bindgen::builder() .header("../../binaryninjacore.h") .clang_arg("-std=c++17") @@ -121,13 +50,20 @@ fn main() { minimum_version )) .rustified_enum("BN.*"); - if let (false, Ok(llvm_dir), Ok(llvm_version)) = (is_mac, llvm_dir, llvm_version) { - let llvm_include_path = format!("-I{}/clang/{}/include", llvm_dir, llvm_version); - bindings = bindings.clang_arg(llvm_include_path); - } else if let (false, Ok(llvm_install_dir)) = (is_mac, llvm_install_dir) { - let llvm_include_path = format!("-I{}/12.0.0/lib/clang/12.0.0/include", llvm_install_dir); - env::set_var("LIBCLANG_PATH", format!("{}/12.0.0/lib", llvm_install_dir)); - bindings = bindings.clang_arg(llvm_include_path); + + // Difference between global LLVM/Clang install and custom LLVM/Clang install... + // First option is for the build server, second option is being nice to our dev who have `LLVM_INSTALL_DIR` set, default is for people with "normal" setups (and Macs) + #[cfg(not(target_os = "macos"))] + { + if let (Ok(llvm_dir), Ok(llvm_version)) = (llvm_dir, llvm_version) { + let llvm_include_path = format!("-I{}/clang/{}/include", llvm_dir, llvm_version); + bindings = bindings.clang_arg(llvm_include_path); + } else if let Ok(llvm_install_dir) = llvm_install_dir { + let llvm_include_path = + format!("-I{}/12.0.0/lib/clang/12.0.0/include", llvm_install_dir); + env::set_var("LIBCLANG_PATH", format!("{}/12.0.0/lib", llvm_install_dir)); + bindings = bindings.clang_arg(llvm_include_path); + } } bindings diff --git a/rust/examples/basic_script/CMakeLists.txt b/rust/examples/basic_script/CMakeLists.txt new file mode 100644 index 000000000..740209ec3 --- /dev/null +++ b/rust/examples/basic_script/CMakeLists.txt @@ -0,0 +1,85 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +if(CMAKE_BUILD_TYPE MATCHES Debug) + set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/debug) + set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target) +else() + set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/release) + set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target --release) +endif() + +project(test_headless) + +file(GLOB PLUGIN_SOURCES + ${PROJECT_SOURCE_DIR}/Cargo.toml + ${PROJECT_SOURCE_DIR}/src/*.rs) + +file(GLOB API_SOURCES + ${PROJECT_SOURCE_DIR}/../../../binaryninjacore.h + ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/build.rs + ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/src/* + ${PROJECT_SOURCE_DIR}/../../Cargo.toml + ${PROJECT_SOURCE_DIR}/../../src/*.rs) + +set(OUTPUT_FILE basic_script${CMAKE_EXECUTABLE_SUFFIX}) +set(OUTPUT_PATH ${CMAKE_BINARY_DIR}/out/bin/${OUTPUT_FILE}) + +add_custom_target(test_headless ALL DEPENDS ${OUTPUT_PATH}) +add_dependencies(test_headless binaryninjaapi) + +find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin) +set(INSTALL_UPDATE_NIGHTLY ${RUSTUP_PATH} install nightly) + +if(APPLE) + if(UNIVERSAL) + add_custom_command( + OUTPUT ${OUTPUT_PATH} + COMMAND ${INSTALL_UPDATE_NIGHTLY} + COMMAND ${CMAKE_COMMAND} -E env + MACOSX_DEPLOYMENT_TARGET=10.14 LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} + ${RUSTUP_PATH} run nightly cargo build --target=aarch64-apple-darwin ${CARGO_OPTS} + COMMAND ${CMAKE_COMMAND} -E env + MACOSX_DEPLOYMENT_TARGET=10.14 LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} + ${RUSTUP_PATH} run nightly cargo build --target=x86_64-apple-darwin ${CARGO_OPTS} + COMMAND cp ${TARGET_DIR}/${OUTPUT_FILE} ${OUTPUT_PATH} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}) + else() + if(CMAKE_BUILD_TYPE MATCHES Debug) + set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/debug/${CMAKE_STATIC_LIBRARY_PREFIX}test_headless${CMAKE_SHARED_LIBRARY_SUFFIX}) + else() + set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release/${CMAKE_STATIC_LIBRARY_PREFIX}test_headless${CMAKE_SHARED_LIBRARY_SUFFIX}) + endif() + + add_custom_command( + OUTPUT ${OUTPUT_PATH} + COMMAND ${INSTALL_UPDATE_NIGHTLY} + COMMAND ${CMAKE_COMMAND} -E env + MACOSX_DEPLOYMENT_TARGET=10.14 LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} + ${RUSTUP_PATH} run nightly cargo build --target=x86_64-apple-darwin ${CARGO_OPTS} + COMMAND cp ${TARGET_DIR}/${OUTPUT_FILE} ${OUTPUT_PATH} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}) + endif() +elseif(WIN32) + add_custom_command( + OUTPUT ${OUTPUT_PATH} + COMMAND ${INSTALL_UPDATE_NIGHTLY} + COMMAND ${CMAKE_COMMAND} -E env + LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} + ${RUSTUP_PATH} run nightly cargo build ${CARGO_OPTS} + COMMAND cp ${TARGET_DIR}/${OUTPUT_FILE} ${OUTPUT_PATH} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}) +else() + add_custom_command( + OUTPUT ${OUTPUT_PATH} + COMMAND ${INSTALL_UPDATE_NIGHTLY} + COMMAND ${CMAKE_COMMAND} -E env + LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} + ${RUSTUP_PATH} run nightly cargo build ${CARGO_OPTS} + COMMAND cp ${TARGET_DIR}/${OUTPUT_FILE} ${OUTPUT_PATH} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}) +endif() diff --git a/rust/examples/basic_script/build.rs b/rust/examples/basic_script/build.rs new file mode 100644 index 000000000..7ad93884d --- /dev/null +++ b/rust/examples/basic_script/build.rs @@ -0,0 +1,67 @@ +use std::env; +use std::fs::File; +use std::io::BufReader; +use std::path::PathBuf; + +#[cfg(target_os = "macos")] +static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun"); + +#[cfg(target_os = "linux")] +static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun"); + +#[cfg(windows)] +static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun"); + +// Check last run location for path to BinaryNinja; Otherwise check the default install locations +fn link_path() -> PathBuf { + use std::io::prelude::*; + + let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap()); + let lastrun = PathBuf::from(&home).join(LASTRUN_PATH.1); + + File::open(lastrun) + .and_then(|f| { + let mut binja_path = String::new(); + let mut reader = BufReader::new(f); + + reader.read_line(&mut binja_path)?; + Ok(PathBuf::from(binja_path.trim())) + }) + .unwrap_or_else(|_| { + #[cfg(target_os = "macos")] + return PathBuf::from("/Applications/Binary Ninja.app/Contents/MacOS"); + + #[cfg(target_os = "linux")] + return home.join("binaryninja"); + + #[cfg(windows)] + return PathBuf::from(env::var("PROGRAMFILES").unwrap()).join("Vector35\\BinaryNinja\\"); + }) +} + +fn main() { + // Use BINARYNINJADIR first for custom BN builds/configurations (BN devs/build server), fallback on defaults + let install_path = env::var("BINARYNINJADIR") + .map(PathBuf::from) + .unwrap_or_else(|_| link_path()); + + #[cfg(target_os = "linux")] + println!( + "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-l:libbinaryninjacore.so.1", + install_path.to_str().unwrap(), + install_path.to_str().unwrap(), + ); + + #[cfg(target_os = "macos")] + println!( + "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-lbinaryninjacore", + install_path.to_str().unwrap(), + install_path.to_str().unwrap(), + ); + + #[cfg(target_os = "windows")] + { + println!("cargo:rustc-link-lib=binaryninjacore"); + println!("cargo:rustc-link-search={}", install_path.to_str().unwrap()); + } +} diff --git a/rust/examples/dwarfdump/build.rs b/rust/examples/dwarfdump/build.rs new file mode 100644 index 000000000..7ad93884d --- /dev/null +++ b/rust/examples/dwarfdump/build.rs @@ -0,0 +1,67 @@ +use std::env; +use std::fs::File; +use std::io::BufReader; +use std::path::PathBuf; + +#[cfg(target_os = "macos")] +static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun"); + +#[cfg(target_os = "linux")] +static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun"); + +#[cfg(windows)] +static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun"); + +// Check last run location for path to BinaryNinja; Otherwise check the default install locations +fn link_path() -> PathBuf { + use std::io::prelude::*; + + let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap()); + let lastrun = PathBuf::from(&home).join(LASTRUN_PATH.1); + + File::open(lastrun) + .and_then(|f| { + let mut binja_path = String::new(); + let mut reader = BufReader::new(f); + + reader.read_line(&mut binja_path)?; + Ok(PathBuf::from(binja_path.trim())) + }) + .unwrap_or_else(|_| { + #[cfg(target_os = "macos")] + return PathBuf::from("/Applications/Binary Ninja.app/Contents/MacOS"); + + #[cfg(target_os = "linux")] + return home.join("binaryninja"); + + #[cfg(windows)] + return PathBuf::from(env::var("PROGRAMFILES").unwrap()).join("Vector35\\BinaryNinja\\"); + }) +} + +fn main() { + // Use BINARYNINJADIR first for custom BN builds/configurations (BN devs/build server), fallback on defaults + let install_path = env::var("BINARYNINJADIR") + .map(PathBuf::from) + .unwrap_or_else(|_| link_path()); + + #[cfg(target_os = "linux")] + println!( + "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-l:libbinaryninjacore.so.1", + install_path.to_str().unwrap(), + install_path.to_str().unwrap(), + ); + + #[cfg(target_os = "macos")] + println!( + "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-lbinaryninjacore", + install_path.to_str().unwrap(), + install_path.to_str().unwrap(), + ); + + #[cfg(target_os = "windows")] + { + println!("cargo:rustc-link-lib=binaryninjacore"); + println!("cargo:rustc-link-search={}", install_path.to_str().unwrap()); + } +} diff --git a/rust/examples/flowgraph/build.rs b/rust/examples/flowgraph/build.rs new file mode 100644 index 000000000..7ad93884d --- /dev/null +++ b/rust/examples/flowgraph/build.rs @@ -0,0 +1,67 @@ +use std::env; +use std::fs::File; +use std::io::BufReader; +use std::path::PathBuf; + +#[cfg(target_os = "macos")] +static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun"); + +#[cfg(target_os = "linux")] +static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun"); + +#[cfg(windows)] +static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun"); + +// Check last run location for path to BinaryNinja; Otherwise check the default install locations +fn link_path() -> PathBuf { + use std::io::prelude::*; + + let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap()); + let lastrun = PathBuf::from(&home).join(LASTRUN_PATH.1); + + File::open(lastrun) + .and_then(|f| { + let mut binja_path = String::new(); + let mut reader = BufReader::new(f); + + reader.read_line(&mut binja_path)?; + Ok(PathBuf::from(binja_path.trim())) + }) + .unwrap_or_else(|_| { + #[cfg(target_os = "macos")] + return PathBuf::from("/Applications/Binary Ninja.app/Contents/MacOS"); + + #[cfg(target_os = "linux")] + return home.join("binaryninja"); + + #[cfg(windows)] + return PathBuf::from(env::var("PROGRAMFILES").unwrap()).join("Vector35\\BinaryNinja\\"); + }) +} + +fn main() { + // Use BINARYNINJADIR first for custom BN builds/configurations (BN devs/build server), fallback on defaults + let install_path = env::var("BINARYNINJADIR") + .map(PathBuf::from) + .unwrap_or_else(|_| link_path()); + + #[cfg(target_os = "linux")] + println!( + "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-l:libbinaryninjacore.so.1", + install_path.to_str().unwrap(), + install_path.to_str().unwrap(), + ); + + #[cfg(target_os = "macos")] + println!( + "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-lbinaryninjacore", + install_path.to_str().unwrap(), + install_path.to_str().unwrap(), + ); + + #[cfg(target_os = "windows")] + { + println!("cargo:rustc-link-lib=binaryninjacore"); + println!("cargo:rustc-link-search={}", install_path.to_str().unwrap()); + } +} diff --git a/rust/examples/template/CMakeLists.txt b/rust/examples/template/CMakeLists.txt new file mode 100644 index 000000000..a06a5af66 --- /dev/null +++ b/rust/examples/template/CMakeLists.txt @@ -0,0 +1,89 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(test_headless) + +file(GLOB PLUGIN_SOURCES + ${PROJECT_SOURCE_DIR}/Cargo.toml + ${PROJECT_SOURCE_DIR}/src/*.rs) + +file(GLOB API_SOURCES + ${PROJECT_SOURCE_DIR}/../../../binaryninjacore.h + ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/build.rs + ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/src/* + ${PROJECT_SOURCE_DIR}/../../Cargo.toml + ${PROJECT_SOURCE_DIR}/../../src/*.rs) + +if(CMAKE_BUILD_TYPE MATCHES Debug) + set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/debug) + set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target) +else() + set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/release) + set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target --release) +endif() +set(PLUGIN_PATH ${TARGET_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}test_headless${CMAKE_SHARED_LIBRARY_SUFFIX}) + +add_custom_target(test_headless ALL DEPENDS ${PLUGIN_PATH}) +add_dependencies(test_headless binaryninjaapi) + +find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin) +set(INSTALL_UPDATE_NIGHTLY ${RUSTUP_PATH} install nightly) + +if(APPLE) + if(UNIVERSAL) + if(CMAKE_BUILD_TYPE MATCHES Debug) + set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/debug/${CMAKE_STATIC_LIBRARY_PREFIX}test_headless${CMAKE_SHARED_LIBRARY_SUFFIX}) + set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/debug/${CMAKE_STATIC_LIBRARY_PREFIX}test_headless${CMAKE_SHARED_LIBRARY_SUFFIX}) + else() + set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/release/${CMAKE_STATIC_LIBRARY_PREFIX}test_headless${CMAKE_SHARED_LIBRARY_SUFFIX}) + set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release/${CMAKE_STATIC_LIBRARY_PREFIX}test_headless${CMAKE_SHARED_LIBRARY_SUFFIX}) + endif() + + add_custom_command( + OUTPUT ${PLUGIN_PATH} + COMMAND ${INSTALL_UPDATE_NIGHTLY} + COMMAND ${CMAKE_COMMAND} -E env + MACOSX_DEPLOYMENT_TARGET=10.14 LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} + ${RUSTUP_PATH} run nightly cargo build --target=aarch64-apple-darwin ${CARGO_OPTS} + COMMAND ${CMAKE_COMMAND} -E env + MACOSX_DEPLOYMENT_TARGET=10.14 LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} + ${RUSTUP_PATH} run nightly cargo build --target=x86_64-apple-darwin ${CARGO_OPTS} + COMMAND mkdir -p ${TARGET_DIR} + COMMAND lipo -create ${AARCH64_LIB_PATH} ${X86_64_LIB_PATH} -output ${PLUGIN_PATH} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}) + else() + if(CMAKE_BUILD_TYPE MATCHES Debug) + set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/debug/${CMAKE_STATIC_LIBRARY_PREFIX}test_headless${CMAKE_SHARED_LIBRARY_SUFFIX}) + else() + set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release/${CMAKE_STATIC_LIBRARY_PREFIX}test_headless${CMAKE_SHARED_LIBRARY_SUFFIX}) + endif() + + add_custom_command( + OUTPUT ${PLUGIN_PATH} + COMMAND ${INSTALL_UPDATE_NIGHTLY} + COMMAND ${CMAKE_COMMAND} -E env + MACOSX_DEPLOYMENT_TARGET=10.14 LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} + ${RUSTUP_PATH} run nightly cargo build --target=x86_64-apple-darwin ${CARGO_OPTS} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}) + endif() +elseif(WIN32) + add_custom_command( + OUTPUT ${PLUGIN_PATH} + COMMAND ${INSTALL_UPDATE_NIGHTLY} + COMMAND ${CMAKE_COMMAND} -E env + LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} + ${RUSTUP_PATH} run nightly cargo build ${CARGO_OPTS} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}) +else() + add_custom_command( + OUTPUT ${PLUGIN_PATH} + COMMAND ${INSTALL_UPDATE_NIGHTLY} + COMMAND ${CMAKE_COMMAND} -E env + LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} + ${RUSTUP_PATH} run nightly cargo build ${CARGO_OPTS} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}) +endif() diff --git a/rust/examples/template/Cargo.toml b/rust/examples/template/Cargo.toml new file mode 100644 index 000000000..6a691e3dd --- /dev/null +++ b/rust/examples/template/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "template" +version = "0.1.0" +edition = "2018" + +# Uncomment this if you're writing a plugin (plugins are shared objects loaded by the core): +# [lib] +# crate-type = ["cdylib"] + +# You can point at the BinaryNinja dependency in one of two ways, via path: +# [dependencies] +# binaryninja = {path="../../"} + +# Or directly at the git repo: +[dependencies] +binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"} diff --git a/rust/examples/template/README.md b/rust/examples/template/README.md new file mode 100644 index 000000000..f10f89c8e --- /dev/null +++ b/rust/examples/template/README.md @@ -0,0 +1,17 @@ +[The only official method of providing linker arguments to a crate is through that crate's `build.rs`](https://github.com/rust-lang/cargo/issues/9554), thus this template. + +Please see `Cargo.toml` for further configuration options. + +### Plugins + +Enable +``` +[lib] +crate-type = ["cdylib"] +``` +in Cargo.toml` + +### Standalone executables + +All standalone executables should call both `binaryninja::headless::init()` and `binaryninja::headless::shutdown()`. + diff --git a/rust/examples/template/build.rs b/rust/examples/template/build.rs new file mode 100644 index 000000000..7ad93884d --- /dev/null +++ b/rust/examples/template/build.rs @@ -0,0 +1,67 @@ +use std::env; +use std::fs::File; +use std::io::BufReader; +use std::path::PathBuf; + +#[cfg(target_os = "macos")] +static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun"); + +#[cfg(target_os = "linux")] +static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun"); + +#[cfg(windows)] +static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun"); + +// Check last run location for path to BinaryNinja; Otherwise check the default install locations +fn link_path() -> PathBuf { + use std::io::prelude::*; + + let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap()); + let lastrun = PathBuf::from(&home).join(LASTRUN_PATH.1); + + File::open(lastrun) + .and_then(|f| { + let mut binja_path = String::new(); + let mut reader = BufReader::new(f); + + reader.read_line(&mut binja_path)?; + Ok(PathBuf::from(binja_path.trim())) + }) + .unwrap_or_else(|_| { + #[cfg(target_os = "macos")] + return PathBuf::from("/Applications/Binary Ninja.app/Contents/MacOS"); + + #[cfg(target_os = "linux")] + return home.join("binaryninja"); + + #[cfg(windows)] + return PathBuf::from(env::var("PROGRAMFILES").unwrap()).join("Vector35\\BinaryNinja\\"); + }) +} + +fn main() { + // Use BINARYNINJADIR first for custom BN builds/configurations (BN devs/build server), fallback on defaults + let install_path = env::var("BINARYNINJADIR") + .map(PathBuf::from) + .unwrap_or_else(|_| link_path()); + + #[cfg(target_os = "linux")] + println!( + "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-l:libbinaryninjacore.so.1", + install_path.to_str().unwrap(), + install_path.to_str().unwrap(), + ); + + #[cfg(target_os = "macos")] + println!( + "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-lbinaryninjacore", + install_path.to_str().unwrap(), + install_path.to_str().unwrap(), + ); + + #[cfg(target_os = "windows")] + { + println!("cargo:rustc-link-lib=binaryninjacore"); + println!("cargo:rustc-link-search={}", install_path.to_str().unwrap()); + } +} diff --git a/rust/examples/template/src/main.rs b/rust/examples/template/src/main.rs new file mode 100644 index 000000000..7947c0f78 --- /dev/null +++ b/rust/examples/template/src/main.rs @@ -0,0 +1,46 @@ +use binaryninja::architecture::Architecture; +use binaryninja::binaryview::{BinaryViewBase, BinaryViewExt}; + +// Standalone executables need to provide a main function for rustc +// Plugins should refer to `binaryninja::command::*` for the various registration callbacks. +fn main() { + // This loads all the core architecture, platform, etc plugins + // Standalone executables probably need to call this, but plugins do not + println!("Loading plugins..."); + binaryninja::headless::init(); + + // Your code here... + println!("Loading binary..."); + let bv = binaryninja::open_view("/bin/cat").expect("Couldn't open `/bin/cat`"); + + println!("Filename: `{}`", bv.metadata().filename()); + println!("File size: `{:#x}`", bv.len()); + println!("Function count: {}", bv.functions().len()); + + for func in &bv.functions() { + println!(" `{}`:", func.symbol().full_name()); + for basic_block in &func.basic_blocks() { + // TODO : This is intended to be refactored to be more nice to work with soon(TM) + for addr in basic_block.as_ref() { + print!(" {} ", addr); + match func.arch().instruction_text( + bv.read_buffer(addr, func.arch().max_instr_len()) + .unwrap() + .get_data(), + addr, + ) { + Some((_, tokens)) => { + tokens + .iter() + .for_each(|token| print!("{}", token.text().to_str().unwrap())); + println!("") + } + _ => (), + } + } + } + } + + // Important! Standalone executables need to call shutdown or they will hang forever + binaryninja::headless::shutdown(); +}