initial commit
This commit is contained in:
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "binaryninja-api"]
|
||||
path = binaryninja-api
|
||||
url = https://github.com/Vector35/binaryninja-api
|
||||
63
CMakeLists.txt
Normal file
63
CMakeLists.txt
Normal file
@@ -0,0 +1,63 @@
|
||||
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
|
||||
|
||||
project(rust_api)
|
||||
|
||||
set(CARGO_API_VERSION stable-2022-12-15)
|
||||
|
||||
file(GLOB RUST_API_SOURCES CONFIGURE_DEPENDS
|
||||
../binaryninjacore.h
|
||||
binaryninjacore-sys/build.rs
|
||||
binaryninjacore-sys/Cargo.toml
|
||||
binaryninjacore-sys/src/*
|
||||
Cargo.toml
|
||||
src/*.rs)
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
set(CARGO_CHECK_OPTS --workspace)
|
||||
set(CARGO_DOC_OPTS --no-deps)
|
||||
else()
|
||||
set(CARGO_CHECK_OPTS --workspace --release)
|
||||
set(CARGO_DOC_OPTS --no-deps --release)
|
||||
endif()
|
||||
|
||||
if (BN_BUILD_NUMBER)
|
||||
set(RUST_DOC_FLAGS "--crate-version ${BN_MAJOR_VERSION}.${BN_MINOR_VERSION}.${BN_BUILD_NUMBER}")
|
||||
else()
|
||||
set(RUST_DOC_FLAGS "")
|
||||
endif()
|
||||
|
||||
# TODO : RUSTDOCFLAGS="--theme binja-dark.css --default-theme=binja-dark"
|
||||
|
||||
add_custom_target(rust_api ALL DEPENDS)
|
||||
add_dependencies(rust_api binaryninjaapi)
|
||||
|
||||
add_custom_target(rust_docs ALL DEPENDS ${PROJECT_BINARY_DIR}/target/doc)
|
||||
add_dependencies(rust_docs binaryninjaapi rust_api)
|
||||
|
||||
# Get API source directory so we can find BinaryNinjaCore
|
||||
get_target_property(BN_API_SOURCE_DIR binaryninjaapi SOURCE_DIR)
|
||||
message(STATUS "${BN_API_SOURCE_DIR}")
|
||||
list(APPEND CMAKE_MODULE_PATH "${BN_API_SOURCE_DIR}/cmake")
|
||||
|
||||
find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin)
|
||||
set(RUSTUP_CHECK_COMMAND ${RUSTUP_PATH} run ${CARGO_API_VERSION} cargo check)
|
||||
set(RUSTUP_DOC_COMMAND ${RUSTUP_PATH} run ${CARGO_API_VERSION} cargo doc)
|
||||
|
||||
add_custom_command(
|
||||
TARGET rust_api
|
||||
PRE_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BN_INSTALL_BIN_DIR} ${RUSTUP_CHECK_COMMAND} ${CARGO_CHECK_OPTS}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${RUST_API_SOURCES}
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_BINARY_DIR}/target/doc
|
||||
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BN_INSTALL_BIN_DIR} RUSTDOCFLAGS=${RUST_DOC_FLAGS} ${RUSTUP_DOC_COMMAND} ${CARGO_DOC_OPTS}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${RUST_API_SOURCES}
|
||||
)
|
||||
|
||||
if(BN_API_BUILD_EXAMPLES)
|
||||
add_subdirectory(examples/basic_script)
|
||||
endif()
|
||||
1126
Cargo.lock
generated
Normal file
1126
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
Cargo.toml
Normal file
30
Cargo.toml
Normal file
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "binaryninja"
|
||||
version = "0.1.0"
|
||||
authors = ["Ryan Snyder <ryan@vector35.com>", "Kyle Martin <kyle@vector35.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
noexports = []
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
libc = "0.2"
|
||||
rayon = { version = "1.0", optional = true }
|
||||
binaryninjacore-sys = { path = "binaryninjacore-sys" }
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"examples/basic_script",
|
||||
"examples/decompile",
|
||||
"examples/dwarf/dwarf_export",
|
||||
"examples/dwarf/dwarf_import",
|
||||
"examples/dwarf/dwarfdump",
|
||||
"examples/dwarf/shared",
|
||||
"examples/flowgraph",
|
||||
"examples/minidump",
|
||||
"examples/mlil_visitor",
|
||||
"examples/mlil_lifter",
|
||||
"examples/template"
|
||||
]
|
||||
13
LICENSE
Normal file
13
LICENSE
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright 2021 Vector 35 Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
67
README.md
Normal file
67
README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# BinaryNinja-rs
|
||||
|
||||
<img align="right" src="./under_construction.png" width="175" height="175">
|
||||
|
||||
> :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 runs on Rust version `stable-2022-12-15`
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
:warning: If you're thinking of contributing to the Rust API, we encourage you to join the #rust-api channel in our Slack: https://slack.binary.ninja, especially for large-effort PRs.
|
||||
Add a "Contributing" section to the Rust API readme
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
Having BinaryNinja installed (and your license registered)
|
||||
Clang
|
||||
Rust
|
||||
|
||||
|
||||
## How to use
|
||||
|
||||
See [`examples/template`](examples/template) for more details.
|
||||
|
||||
### To write a plugin:
|
||||
|
||||
`Cargo.toml`:
|
||||
```
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
|
||||
```
|
||||
|
||||
See the `./examples/`. Plugin registration commands are in `binaryninja::command::*`
|
||||
|
||||
|
||||
### To write a standalone executable:
|
||||
|
||||
`Cargo.toml`:
|
||||
```
|
||||
[dependencies]
|
||||
binaryninja = { git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
|
||||
```
|
||||
|
||||
All standalone binaries need to provide a `build.rs`.
|
||||
See [`examples/template`](examples/template) for details.
|
||||
|
||||
## Docs
|
||||
|
||||
Docs can be found at https://dev-rust.binary.ninja/
|
||||
|
||||
---
|
||||
|
||||
#### Attribution
|
||||
|
||||
This project makes use of:
|
||||
- [log] ([log license] - MIT)
|
||||
- [rayon] ([rayon license] - MIT)
|
||||
|
||||
[log]: https://github.com/rust-lang/log
|
||||
[log license]: https://github.com/rust-lang/log/blob/master/LICENSE-MIT
|
||||
[rayon]: https://github.com/rayon-rs/rayon
|
||||
[rayon license]: https://github.com/rayon-rs/rayon/blob/master/LICENSE-MIT
|
||||
1
binaryninja-api
Submodule
1
binaryninja-api
Submodule
Submodule binaryninja-api added at 2d45ae4ea9
337
binaryninjacore-sys/Cargo.lock
generated
Normal file
337
binaryninjacore-sys/Cargo.lock
generated
Normal file
@@ -0,0 +1,337 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binaryninjacore-sys"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.58.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f8523b410d7187a43085e7e064416ea32ded16bd0a4e6fc025e21616d01258f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "853eda514c284c2287f4bf20ae614f8781f40a81d32ecda6e91449304dfe077c"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.112"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42a568c8f2cd051a4d283bd6eb0343ac214c1b0f1ac19f93e1175b2dee38c73d"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
8
binaryninjacore-sys/Cargo.toml
Normal file
8
binaryninjacore-sys/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "binaryninjacore-sys"
|
||||
version = "0.1.0"
|
||||
authors = ["Ryan Snyder <ryan@vector35.com>", "Kyle Martin <kyle@vector35.com>"]
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "^0.66"
|
||||
116
binaryninjacore-sys/build.rs
Normal file
116
binaryninjacore-sys/build.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
extern crate bindgen;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
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=source/binaryninjacore.h");
|
||||
|
||||
//Cargo's output directory
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
|
||||
let link_path = env::var("BINARYNINJADIR")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| link_path());
|
||||
|
||||
// Linux builds of binaryninja ship with libbinaryninjacore.so.1 in the
|
||||
// application folder and no symlink. The linker will attempt to link with
|
||||
// libbinaryninjacore.so. Since this is likely going to fail, we detect this
|
||||
// ahead of time and create an appropriately named symlink inside of OUT_DIR
|
||||
// and add it to the library search path.
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let symlink_target = PathBuf::from(&out_dir).join("libbinaryninjacore.so");
|
||||
if link_path.join("libbinaryninjacore.so.1").exists() && !symlink_target.exists() {
|
||||
use std::os::unix::fs;
|
||||
fs::symlink(
|
||||
link_path.join("libbinaryninjacore.so.1"),
|
||||
PathBuf::from(&out_dir).join("libbinaryninjacore.so"),
|
||||
)
|
||||
.expect("failed to create required symlink");
|
||||
}
|
||||
println!("cargo:rustc-link-search={}", out_dir);
|
||||
}
|
||||
|
||||
println!("cargo:rustc-link-lib=binaryninjacore");
|
||||
println!("cargo:rustc-link-search={}", link_path.to_str().unwrap());
|
||||
|
||||
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();
|
||||
let mut minimum_version = "0".to_string();
|
||||
let file = File::open("source/ui/uitypes.h").expect("Couldn't open uitypes.h");
|
||||
for line in BufReader::new(file).lines() {
|
||||
let line = line.unwrap();
|
||||
if let Some(version) = line.strip_prefix(current_line) {
|
||||
current_version = version.to_owned();
|
||||
} else if let Some(version) = line.strip_prefix(minimum_line) {
|
||||
minimum_version = version.to_owned();
|
||||
}
|
||||
}
|
||||
|
||||
bindgen::builder()
|
||||
.header("source/binaryninjacore.h")
|
||||
.clang_arg("-std=c++17")
|
||||
.clang_arg("-x")
|
||||
.clang_arg("c++")
|
||||
.size_t_is_usize(true)
|
||||
.generate_comments(false)
|
||||
.allowlist_function("BN.*")
|
||||
.allowlist_var("BN_CURRENT_CORE_ABI_VERSION")
|
||||
.allowlist_var("BN_MINIMUM_CORE_ABI_VERSION")
|
||||
.allowlist_var("MAX_RELOCATION_SIZE")
|
||||
.raw_line(format!(
|
||||
"pub const BN_CURRENT_UI_ABI_VERSION: u32 = {};",
|
||||
current_version
|
||||
))
|
||||
.raw_line(format!(
|
||||
"pub const BN_MINIMUM_UI_ABI_VERSION: u32 = {};",
|
||||
minimum_version
|
||||
))
|
||||
.rustified_enum("BN.*")
|
||||
.generate()
|
||||
.expect("Unable to generate bindings")
|
||||
.write_to_file(PathBuf::from(out_dir).join("bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
}
|
||||
7
binaryninjacore-sys/src/lib.rs
Normal file
7
binaryninjacore-sys/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
12
build.rs
Normal file
12
build.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
fn main() {
|
||||
// TODO : Enable the following when https://github.com/rust-lang/rust/issues/43781 stabilizes
|
||||
// #[cfg(doc)]
|
||||
let _ = std::fs::create_dir("target");
|
||||
let _ = std::fs::create_dir("target/doc");
|
||||
let _ = std::fs::copy("../docs/img/favicon.ico", "target/doc/favicon.ico");
|
||||
let _ = std::fs::copy(
|
||||
"under_construction.png",
|
||||
"target/doc/under_construction.png",
|
||||
);
|
||||
let _ = std::fs::copy("../docs/img/logo.png", "target/doc/logo.png");
|
||||
}
|
||||
105
examples/basic_script/CMakeLists.txt
Normal file
105
examples/basic_script/CMakeLists.txt
Normal file
@@ -0,0 +1,105 @@
|
||||
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(OUTPUT_FILE basic_script${CMAKE_EXECUTABLE_SUFFIX})
|
||||
set(OUTPUT_PATH ${CMAKE_BINARY_DIR}/out/bin/${OUTPUT_FILE})
|
||||
|
||||
|
||||
if(NOT BN_API_BUILD_EXAMPLES)
|
||||
# Out-of-tree build
|
||||
find_path(
|
||||
BN_API_PATH
|
||||
NAMES binaryninjaapi.h
|
||||
HINTS ../../.. binaryninjaapi
|
||||
REQUIRED
|
||||
)
|
||||
add_subdirectory(${BN_API_PATH} api)
|
||||
endif()
|
||||
|
||||
add_custom_target(test_headless ALL DEPENDS ${OUTPUT_PATH})
|
||||
add_dependencies(test_headless binaryninjaapi)
|
||||
|
||||
# Get API source directory so we can find BinaryNinjaCore
|
||||
get_target_property(BN_API_SOURCE_DIR binaryninjaapi SOURCE_DIR)
|
||||
message(STATUS "${BN_API_SOURCE_DIR}")
|
||||
list(APPEND CMAKE_MODULE_PATH "${BN_API_SOURCE_DIR}/cmake")
|
||||
|
||||
# BinaryNinjaCore has the user plugins dir define that we want
|
||||
find_package(BinaryNinjaCore REQUIRED)
|
||||
|
||||
find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin)
|
||||
set(RUSTUP_COMMAND ${RUSTUP_PATH} run ${CARGO_API_VERSION} cargo build)
|
||||
|
||||
if(APPLE)
|
||||
if(UNIVERSAL)
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/debug/${OUTPUT_FILE})
|
||||
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/debug/${OUTPUT_FILE})
|
||||
else()
|
||||
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/release/${OUTPUT_FILE})
|
||||
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release/${OUTPUT_FILE})
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${OUTPUT_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_INSTALL_BIN_DIR}
|
||||
${RUSTUP_COMMAND} --target=aarch64-apple-darwin ${CARGO_OPTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_INSTALL_BIN_DIR}
|
||||
${RUSTUP_COMMAND} --target=x86_64-apple-darwin ${CARGO_OPTS}
|
||||
COMMAND lipo -create ${AARCH64_LIB_PATH} ${X86_64_LIB_PATH} -output ${OUTPUT_PATH}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
|
||||
else()
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
set(LIB_PATH ${PROJECT_BINARY_DIR}/target/debug/${OUTPUT_FILE})
|
||||
else()
|
||||
set(LIB_PATH ${PROJECT_BINARY_DIR}/target/release/${OUTPUT_FILE})
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${OUTPUT_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_INSTALL_BIN_DIR}
|
||||
${RUSTUP_COMMAND} ${CARGO_OPTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${LIB_PATH} ${OUTPUT_PATH}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
add_custom_command(
|
||||
OUTPUT ${OUTPUT_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BN_INSTALL_BIN_DIR} ${RUSTUP_COMMAND} ${CARGO_OPTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE} ${OUTPUT_PATH}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
|
||||
else()
|
||||
add_custom_command(
|
||||
OUTPUT ${OUTPUT_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BN_INSTALL_BIN_DIR} ${RUSTUP_COMMAND} ${CARGO_OPTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE} ${OUTPUT_PATH}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
|
||||
endif()
|
||||
8
examples/basic_script/Cargo.toml
Normal file
8
examples/basic_script/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "basic_script"
|
||||
version = "0.1.0"
|
||||
authors = ["KyleMiles <kyle@vector35.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
binaryninja = {path="../../"}
|
||||
68
examples/basic_script/build.rs
Normal file
68
examples/basic_script/build.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
37
examples/basic_script/src/main.rs
Normal file
37
examples/basic_script/src/main.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use binaryninja::architecture::Architecture;
|
||||
use binaryninja::binaryview::{BinaryViewBase, BinaryViewExt};
|
||||
|
||||
fn main() {
|
||||
println!("Loading plugins..."); // This loads all the core architecture, platform, etc plugins
|
||||
let headless_session = binaryninja::headless::Session::new();
|
||||
|
||||
println!("Loading binary...");
|
||||
let bv = headless_session
|
||||
.load("/bin/cat")
|
||||
.expect("Couldn't open `/bin/cat`");
|
||||
|
||||
println!("Filename: `{}`", bv.file().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);
|
||||
if let Some((_, tokens)) = func.arch().instruction_text(
|
||||
bv.read_buffer(addr, func.arch().max_instr_len())
|
||||
.unwrap()
|
||||
.get_data(),
|
||||
addr,
|
||||
) {
|
||||
tokens
|
||||
.iter()
|
||||
.for_each(|token| print!("{}", token.text().as_str()));
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
examples/decompile/Cargo.toml
Normal file
10
examples/decompile/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "decompile"
|
||||
version = "0.1.0"
|
||||
authors = ["Fabian Freyer <mail@fabianfreyer.de>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
binaryninja = {path="../../"}
|
||||
clap = { version = "4.3", features = ["derive"] }
|
||||
|
||||
68
examples/decompile/build.rs
Normal file
68
examples/decompile/build.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
54
examples/decompile/src/main.rs
Normal file
54
examples/decompile/src/main.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
|
||||
use binaryninja::disassembly::{DisassemblyOption, DisassemblySettings};
|
||||
use binaryninja::function::Function;
|
||||
use binaryninja::linearview::{LinearViewCursor, LinearViewObject};
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
/// Use binaryninja to decompile to C.
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(version, long_about = None)]
|
||||
struct Args {
|
||||
/// Path to the file to decompile
|
||||
filename: String,
|
||||
}
|
||||
|
||||
fn decompile_to_c(view: &BinaryView, func: &Function) {
|
||||
let settings = DisassemblySettings::new();
|
||||
settings.set_option(DisassemblyOption::ShowAddress, false);
|
||||
settings.set_option(DisassemblyOption::WaitForIL, true);
|
||||
|
||||
let linearview = LinearViewObject::language_representation(view, &settings);
|
||||
|
||||
let mut cursor = LinearViewCursor::new(&linearview);
|
||||
cursor.seek_to_address(func.highest_address());
|
||||
|
||||
let last = view.get_next_linear_disassembly_lines(&mut cursor.duplicate());
|
||||
let first = view.get_previous_linear_disassembly_lines(&mut cursor);
|
||||
|
||||
let lines = first.into_iter().chain(last.into_iter());
|
||||
|
||||
for line in lines {
|
||||
println!("{}", line.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
eprintln!("Loading plugins...");
|
||||
binaryninja::headless::init();
|
||||
|
||||
eprintln!("Loading binary...");
|
||||
let bv = binaryninja::load(args.filename).expect("Couldn't open file");
|
||||
|
||||
eprintln!("Filename: `{}`", bv.file().filename());
|
||||
eprintln!("File size: `{:#x}`", bv.len());
|
||||
eprintln!("Function count: {}", bv.functions().len());
|
||||
|
||||
for func in &bv.functions() {
|
||||
decompile_to_c(bv.as_ref(), func.as_ref());
|
||||
}
|
||||
|
||||
binaryninja::headless::shutdown();
|
||||
}
|
||||
93
examples/dwarf/dwarf_export/CMakeLists.txt
Normal file
93
examples/dwarf/dwarf_export/CMakeLists.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
||||
|
||||
project(dwarf_export)
|
||||
|
||||
file(GLOB PLUGIN_SOURCES CONFIGURE_DEPENDS
|
||||
${PROJECT_SOURCE_DIR}/Cargo.toml
|
||||
${PROJECT_SOURCE_DIR}/src/*.rs
|
||||
${PROJECT_SOURCE_DIR}/../shared/Cargo.toml
|
||||
${PROJECT_SOURCE_DIR}/../shared/src/*.rs)
|
||||
|
||||
file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS
|
||||
${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)
|
||||
set(OUTPUT_PDB_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}dwarf_export.pdb)
|
||||
endif()
|
||||
|
||||
set(OUTPUT_FILE ${CMAKE_STATIC_LIBRARY_PREFIX}dwarf_export${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
set(PLUGIN_PATH ${TARGET_DIR}/${OUTPUT_FILE})
|
||||
|
||||
add_custom_target(dwarf_export ALL DEPENDS ${PLUGIN_PATH})
|
||||
add_dependencies(dwarf_export binaryninjaapi)
|
||||
|
||||
find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin)
|
||||
if(CARGO_API_VERSION)
|
||||
set(RUSTUP_COMMAND ${RUSTUP_PATH} run ${CARGO_API_VERSION} cargo build)
|
||||
else()
|
||||
set(RUSTUP_COMMAND ${RUSTUP_PATH} run ${CARGO_STABLE_VERSION} cargo build)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
if(UNIVERSAL)
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/debug/${OUTPUT_FILE})
|
||||
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/debug/${OUTPUT_FILE})
|
||||
else()
|
||||
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/release/${OUTPUT_FILE})
|
||||
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release/${OUTPUT_FILE})
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PLUGIN_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_CORE_OUTPUT_DIR}
|
||||
${RUSTUP_COMMAND} --target=aarch64-apple-darwin ${CARGO_OPTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_CORE_OUTPUT_DIR}
|
||||
${RUSTUP_COMMAND} --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}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${PLUGIN_PATH} ${BN_CORE_PLUGIN_DIR}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
|
||||
else()
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
set(LIB_PATH ${PROJECT_BINARY_DIR}/target/debug/${OUTPUT_FILE})
|
||||
else()
|
||||
set(LIB_PATH ${PROJECT_BINARY_DIR}/target/release/${OUTPUT_FILE})
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PLUGIN_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E env MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} ${RUSTUP_COMMAND} ${CARGO_OPTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${PLUGIN_PATH} ${BN_CORE_PLUGIN_DIR}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
add_custom_command(
|
||||
OUTPUT ${PLUGIN_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} ${RUSTUP_COMMAND} ${CARGO_OPTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${PLUGIN_PATH} ${BN_CORE_PLUGIN_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_PDB_NAME} ${BN_CORE_PLUGIN_DIR}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
|
||||
else()
|
||||
add_custom_command(
|
||||
OUTPUT ${PLUGIN_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} ${RUSTUP_COMMAND} ${CARGO_OPTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${PLUGIN_PATH} ${BN_CORE_PLUGIN_DIR}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
|
||||
endif()
|
||||
13
examples/dwarf/dwarf_export/Cargo.toml
Normal file
13
examples/dwarf/dwarf_export/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "dwarf_export"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
binaryninja = {path="../../../"}
|
||||
gimli = "^0.27"
|
||||
log = "^0.4"
|
||||
object = { version = "0.30.3", features = ["write"] }
|
||||
1
examples/dwarf/dwarf_export/README.md
Normal file
1
examples/dwarf/dwarf_export/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# DWARF Export
|
||||
68
examples/dwarf/dwarf_export/build.rs
Normal file
68
examples/dwarf/dwarf_export/build.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
44
examples/dwarf/dwarf_export/src/edit_distance.rs
Normal file
44
examples/dwarf/dwarf_export/src/edit_distance.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
pub(crate) fn distance(a: &str, b: &str) -> usize {
|
||||
if a == b {
|
||||
return 0;
|
||||
}
|
||||
match (a.chars().count(), b.chars().count()) {
|
||||
(0, b) => return b,
|
||||
(a, 0) => return a,
|
||||
// (a_len, b_len) if a_len < b_len => return distance(b, a),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let mut result = 0;
|
||||
let mut cache: Vec<usize> = (1..a.chars().count() + 1).collect();
|
||||
|
||||
for (index_b, char_b) in b.chars().enumerate() {
|
||||
result = index_b;
|
||||
let mut distance_a = index_b;
|
||||
|
||||
for (index_a, char_a) in a.chars().enumerate() {
|
||||
let distance_b = if char_a == char_b {
|
||||
distance_a
|
||||
} else {
|
||||
distance_a + 1
|
||||
};
|
||||
|
||||
distance_a = cache[index_a];
|
||||
|
||||
result = if distance_a > result {
|
||||
if distance_b > result {
|
||||
result + 1
|
||||
} else {
|
||||
distance_b
|
||||
}
|
||||
} else if distance_b > distance_a {
|
||||
distance_a + 1
|
||||
} else {
|
||||
distance_b
|
||||
};
|
||||
|
||||
cache[index_a] = result;
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
795
examples/dwarf/dwarf_export/src/lib.rs
Normal file
795
examples/dwarf/dwarf_export/src/lib.rs
Normal file
@@ -0,0 +1,795 @@
|
||||
mod edit_distance;
|
||||
|
||||
use gimli::{
|
||||
constants,
|
||||
write::{
|
||||
Address, AttributeValue, DwarfUnit, EndianVec, Expression, Range, RangeList, Sections,
|
||||
UnitEntryId,
|
||||
},
|
||||
};
|
||||
use object::{write, Architecture, BinaryFormat, SectionKind};
|
||||
use std::fs;
|
||||
|
||||
use binaryninja::{
|
||||
binaryview::{BinaryView, BinaryViewBase, BinaryViewExt},
|
||||
command::{register, Command},
|
||||
interaction,
|
||||
interaction::{FormResponses, FormResponses::Index},
|
||||
logger::init,
|
||||
rc::Ref,
|
||||
string::BnString,
|
||||
symbol::SymbolType,
|
||||
types::{Conf, MemberAccess, StructureType, Type, TypeClass},
|
||||
};
|
||||
use log::{error, info, LevelFilter};
|
||||
|
||||
fn export_type(
|
||||
name: String,
|
||||
t: &Type,
|
||||
bv: &BinaryView,
|
||||
defined_types: &mut Vec<(Ref<Type>, UnitEntryId)>,
|
||||
dwarf: &mut DwarfUnit,
|
||||
) -> Option<UnitEntryId> {
|
||||
if let Some((_, die)) = defined_types
|
||||
.iter()
|
||||
.find(|(defined_type, _)| defined_type.as_ref() == t)
|
||||
{
|
||||
return Some(*die);
|
||||
}
|
||||
|
||||
let root = dwarf.unit.root();
|
||||
match t.type_class() {
|
||||
TypeClass::VoidTypeClass => {
|
||||
let void_die_uid = dwarf.unit.add(root, constants::DW_TAG_unspecified_type);
|
||||
defined_types.push((t.to_owned(), void_die_uid));
|
||||
|
||||
dwarf.unit.get_mut(void_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String("void".as_bytes().to_vec()),
|
||||
);
|
||||
Some(void_die_uid)
|
||||
}
|
||||
TypeClass::BoolTypeClass => {
|
||||
let bool_die_uid = dwarf.unit.add(root, constants::DW_TAG_base_type);
|
||||
defined_types.push((t.to_owned(), bool_die_uid));
|
||||
|
||||
dwarf.unit.get_mut(bool_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(name.as_bytes().to_vec()),
|
||||
);
|
||||
dwarf.unit.get_mut(bool_die_uid).set(
|
||||
gimli::DW_AT_byte_size,
|
||||
AttributeValue::Data1(t.width() as u8),
|
||||
);
|
||||
dwarf.unit.get_mut(bool_die_uid).set(
|
||||
gimli::DW_AT_encoding,
|
||||
AttributeValue::Encoding(constants::DW_ATE_float),
|
||||
);
|
||||
Some(bool_die_uid)
|
||||
}
|
||||
TypeClass::IntegerTypeClass => {
|
||||
let int_die_uid = dwarf.unit.add(root, constants::DW_TAG_base_type);
|
||||
defined_types.push((t.to_owned(), int_die_uid));
|
||||
|
||||
dwarf.unit.get_mut(int_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(name.as_bytes().to_vec()),
|
||||
);
|
||||
dwarf.unit.get_mut(int_die_uid).set(
|
||||
gimli::DW_AT_byte_size,
|
||||
AttributeValue::Data1(t.width() as u8),
|
||||
);
|
||||
dwarf.unit.get_mut(int_die_uid).set(
|
||||
gimli::DW_AT_encoding,
|
||||
if t.is_signed().contents {
|
||||
AttributeValue::Encoding(constants::DW_ATE_signed)
|
||||
} else {
|
||||
AttributeValue::Encoding(constants::DW_ATE_unsigned)
|
||||
},
|
||||
);
|
||||
Some(int_die_uid)
|
||||
}
|
||||
TypeClass::FloatTypeClass => {
|
||||
let float_die_uid = dwarf.unit.add(root, constants::DW_TAG_base_type);
|
||||
defined_types.push((t.to_owned(), float_die_uid));
|
||||
|
||||
dwarf.unit.get_mut(float_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(name.as_bytes().to_vec()),
|
||||
);
|
||||
dwarf.unit.get_mut(float_die_uid).set(
|
||||
gimli::DW_AT_byte_size,
|
||||
AttributeValue::Data1(t.width() as u8),
|
||||
);
|
||||
dwarf.unit.get_mut(float_die_uid).set(
|
||||
gimli::DW_AT_encoding,
|
||||
AttributeValue::Encoding(constants::DW_ATE_float),
|
||||
);
|
||||
Some(float_die_uid)
|
||||
}
|
||||
TypeClass::StructureTypeClass => {
|
||||
let structure_die_uid = match t.get_structure().unwrap().structure_type() {
|
||||
StructureType::ClassStructureType => {
|
||||
dwarf.unit.add(root, constants::DW_TAG_class_type)
|
||||
}
|
||||
StructureType::StructStructureType => {
|
||||
dwarf.unit.add(root, constants::DW_TAG_structure_type)
|
||||
}
|
||||
StructureType::UnionStructureType => {
|
||||
dwarf.unit.add(root, constants::DW_TAG_union_type)
|
||||
}
|
||||
};
|
||||
defined_types.push((t.to_owned(), structure_die_uid));
|
||||
|
||||
dwarf.unit.get_mut(structure_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(name.as_bytes().to_vec()),
|
||||
);
|
||||
dwarf.unit.get_mut(structure_die_uid).set(
|
||||
gimli::DW_AT_byte_size,
|
||||
AttributeValue::Data2(t.width() as u16),
|
||||
);
|
||||
|
||||
for struct_member in t.get_structure().unwrap().members().unwrap() {
|
||||
let struct_member_die_uid =
|
||||
dwarf.unit.add(structure_die_uid, constants::DW_TAG_member);
|
||||
dwarf.unit.get_mut(struct_member_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(struct_member.name.as_bytes().to_vec()),
|
||||
);
|
||||
match struct_member.access {
|
||||
MemberAccess::PrivateAccess => {
|
||||
dwarf.unit.get_mut(struct_member_die_uid).set(
|
||||
gimli::DW_AT_accessibility,
|
||||
AttributeValue::Accessibility(gimli::DW_ACCESS_private),
|
||||
);
|
||||
}
|
||||
MemberAccess::ProtectedAccess => {
|
||||
dwarf.unit.get_mut(struct_member_die_uid).set(
|
||||
gimli::DW_AT_accessibility,
|
||||
AttributeValue::Accessibility(gimli::DW_ACCESS_protected),
|
||||
);
|
||||
}
|
||||
MemberAccess::PublicAccess => {
|
||||
dwarf.unit.get_mut(struct_member_die_uid).set(
|
||||
gimli::DW_AT_accessibility,
|
||||
AttributeValue::Accessibility(gimli::DW_ACCESS_public),
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
dwarf.unit.get_mut(struct_member_die_uid).set(
|
||||
gimli::DW_AT_data_member_location,
|
||||
AttributeValue::Data8(struct_member.offset),
|
||||
);
|
||||
|
||||
if let Some(target_die_uid) = export_type(
|
||||
format!("{}", struct_member.ty.contents),
|
||||
struct_member.ty.contents.as_ref(),
|
||||
bv,
|
||||
defined_types,
|
||||
dwarf,
|
||||
) {
|
||||
dwarf
|
||||
.unit
|
||||
.get_mut(struct_member_die_uid)
|
||||
.set(gimli::DW_AT_type, AttributeValue::UnitRef(target_die_uid));
|
||||
}
|
||||
}
|
||||
|
||||
Some(structure_die_uid)
|
||||
}
|
||||
TypeClass::EnumerationTypeClass => {
|
||||
let enum_die_uid = dwarf.unit.add(root, constants::DW_TAG_enumeration_type);
|
||||
defined_types.push((t.to_owned(), enum_die_uid));
|
||||
|
||||
dwarf.unit.get_mut(enum_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(name.as_bytes().to_vec()),
|
||||
);
|
||||
dwarf.unit.get_mut(enum_die_uid).set(
|
||||
gimli::DW_AT_byte_size,
|
||||
AttributeValue::Data1(t.width() as u8),
|
||||
);
|
||||
|
||||
for enum_field in t.get_enumeration().unwrap().members() {
|
||||
let enum_field_die_uid = dwarf.unit.add(enum_die_uid, constants::DW_TAG_enumerator);
|
||||
dwarf.unit.get_mut(enum_field_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(enum_field.name.as_bytes().to_vec()),
|
||||
);
|
||||
dwarf.unit.get_mut(enum_field_die_uid).set(
|
||||
gimli::DW_AT_const_value,
|
||||
AttributeValue::Data4(enum_field.value as u32),
|
||||
);
|
||||
}
|
||||
|
||||
Some(enum_die_uid)
|
||||
}
|
||||
TypeClass::PointerTypeClass => {
|
||||
let pointer_die_uid = dwarf.unit.add(root, constants::DW_TAG_pointer_type);
|
||||
defined_types.push((t.to_owned(), pointer_die_uid));
|
||||
|
||||
dwarf.unit.get_mut(pointer_die_uid).set(
|
||||
gimli::DW_AT_byte_size,
|
||||
AttributeValue::Data1(t.width() as u8),
|
||||
);
|
||||
if let Ok(Conf {
|
||||
contents: target_type,
|
||||
..
|
||||
}) = t.target()
|
||||
{
|
||||
// TODO : Passing through the name here might be wrong
|
||||
if let Some(target_die_uid) =
|
||||
export_type(name, &target_type, bv, defined_types, dwarf)
|
||||
{
|
||||
dwarf
|
||||
.unit
|
||||
.get_mut(pointer_die_uid)
|
||||
.set(gimli::DW_AT_type, AttributeValue::UnitRef(target_die_uid));
|
||||
}
|
||||
}
|
||||
Some(pointer_die_uid)
|
||||
}
|
||||
TypeClass::ArrayTypeClass => {
|
||||
let array_die_uid = dwarf.unit.add(root, constants::DW_TAG_array_type);
|
||||
defined_types.push((t.to_owned(), array_die_uid));
|
||||
|
||||
// Name
|
||||
dwarf.unit.get_mut(array_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(name.as_bytes().to_vec()),
|
||||
);
|
||||
|
||||
// Element type
|
||||
if let Ok(Conf {
|
||||
contents: element_type,
|
||||
..
|
||||
}) = t.element_type()
|
||||
{
|
||||
// TODO : Passing through the name here might be wrong
|
||||
if let Some(target_die_uid) =
|
||||
export_type(name, &element_type, bv, defined_types, dwarf)
|
||||
{
|
||||
dwarf
|
||||
.unit
|
||||
.get_mut(array_die_uid)
|
||||
.set(gimli::DW_AT_type, AttributeValue::UnitRef(target_die_uid));
|
||||
}
|
||||
}
|
||||
|
||||
// For some reason subrange types have a 'type' field that is just "some type" that'll work to index this array
|
||||
// We're hardcoding this to a uint64_t. This could be unsound.
|
||||
let array_accessor_type = export_type(
|
||||
"uint64_t".to_string(),
|
||||
&Type::named_int(8, false, "uint64_t"),
|
||||
bv,
|
||||
defined_types,
|
||||
dwarf,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Array length and multidimensional arrays
|
||||
let mut current_t = t.to_owned();
|
||||
while let Ok(Conf {
|
||||
contents: element_type,
|
||||
..
|
||||
}) = current_t.element_type()
|
||||
{
|
||||
let array_dimension_die_uid = dwarf
|
||||
.unit
|
||||
.add(array_die_uid, constants::DW_TAG_subrange_type);
|
||||
|
||||
dwarf.unit.get_mut(array_dimension_die_uid).set(
|
||||
gimli::DW_AT_type,
|
||||
AttributeValue::UnitRef(array_accessor_type),
|
||||
);
|
||||
|
||||
dwarf.unit.get_mut(array_dimension_die_uid).set(
|
||||
gimli::DW_AT_upper_bound,
|
||||
AttributeValue::Data8((current_t.width() / element_type.width()) - 1),
|
||||
);
|
||||
|
||||
if element_type.type_class() != TypeClass::ArrayTypeClass {
|
||||
break;
|
||||
} else {
|
||||
current_t = element_type;
|
||||
}
|
||||
}
|
||||
|
||||
Some(array_die_uid)
|
||||
}
|
||||
TypeClass::FunctionTypeClass => {
|
||||
Some(dwarf.unit.add(root, constants::DW_TAG_unspecified_type))
|
||||
}
|
||||
TypeClass::VarArgsTypeClass => {
|
||||
Some(dwarf.unit.add(root, constants::DW_TAG_unspecified_type))
|
||||
}
|
||||
TypeClass::ValueTypeClass => Some(dwarf.unit.add(root, constants::DW_TAG_unspecified_type)),
|
||||
TypeClass::NamedTypeReferenceClass => {
|
||||
let ntr = t.get_named_type_reference().unwrap();
|
||||
if let Some(target_type) = ntr.target(bv) {
|
||||
if target_type.type_class() == TypeClass::StructureTypeClass {
|
||||
export_type(
|
||||
ntr.name().to_string(),
|
||||
&target_type,
|
||||
bv,
|
||||
defined_types,
|
||||
dwarf,
|
||||
)
|
||||
} else {
|
||||
let typedef_die_uid = dwarf.unit.add(root, constants::DW_TAG_typedef);
|
||||
defined_types.push((t.to_owned(), typedef_die_uid));
|
||||
|
||||
dwarf.unit.get_mut(typedef_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(ntr.name().to_string().as_bytes().to_vec()),
|
||||
);
|
||||
|
||||
if let Some(target_die_uid) = export_type(
|
||||
ntr.name().to_string(),
|
||||
&target_type,
|
||||
bv,
|
||||
defined_types,
|
||||
dwarf,
|
||||
) {
|
||||
dwarf
|
||||
.unit
|
||||
.get_mut(typedef_die_uid)
|
||||
.set(gimli::DW_AT_type, AttributeValue::UnitRef(target_die_uid));
|
||||
}
|
||||
Some(typedef_die_uid)
|
||||
}
|
||||
} else {
|
||||
error!("Could not get target of typedef `{}`", ntr.name());
|
||||
None
|
||||
}
|
||||
}
|
||||
TypeClass::WideCharTypeClass => {
|
||||
let wide_char_die_uid = dwarf.unit.add(root, constants::DW_TAG_base_type);
|
||||
defined_types.push((t.to_owned(), wide_char_die_uid));
|
||||
|
||||
dwarf.unit.get_mut(wide_char_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(name.as_bytes().to_vec()),
|
||||
);
|
||||
dwarf.unit.get_mut(wide_char_die_uid).set(
|
||||
gimli::DW_AT_byte_size,
|
||||
AttributeValue::Data1(t.width() as u8),
|
||||
);
|
||||
dwarf.unit.get_mut(wide_char_die_uid).set(
|
||||
gimli::DW_AT_encoding,
|
||||
if t.is_signed().contents {
|
||||
AttributeValue::Encoding(constants::DW_ATE_signed_char)
|
||||
} else {
|
||||
AttributeValue::Encoding(constants::DW_ATE_unsigned_char)
|
||||
},
|
||||
);
|
||||
Some(wide_char_die_uid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn export_types(
|
||||
bv: &BinaryView,
|
||||
dwarf: &mut DwarfUnit,
|
||||
defined_types: &mut Vec<(Ref<Type>, UnitEntryId)>,
|
||||
) {
|
||||
for t in &bv.types() {
|
||||
export_type(
|
||||
t.name().to_string(),
|
||||
&t.type_object(),
|
||||
bv,
|
||||
defined_types,
|
||||
dwarf,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn export_functions(
|
||||
bv: &BinaryView,
|
||||
dwarf: &mut DwarfUnit,
|
||||
defined_types: &mut Vec<(Ref<Type>, UnitEntryId)>,
|
||||
) {
|
||||
let entry_point = bv.entry_point_function();
|
||||
|
||||
for function in &bv.functions() {
|
||||
// Create function DIE as child of the compilation unit DIE
|
||||
let root = dwarf.unit.root();
|
||||
let function_die_uid = dwarf.unit.add(root, constants::DW_TAG_subprogram);
|
||||
// let function_die = dwarf.unit.get_mut(function_die_uid);
|
||||
|
||||
// Set subprogram DIE attributes
|
||||
dwarf.unit.get_mut(function_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(function.symbol().short_name().as_bytes().to_vec()),
|
||||
);
|
||||
|
||||
// TODO : (DW_AT_main_subprogram VS DW_TAG_entry_point)
|
||||
// TODO : This attribute seems maybe usually unused?
|
||||
if let Ok(entry_point_function) = &entry_point {
|
||||
if entry_point_function.as_ref() == function.as_ref() {
|
||||
dwarf
|
||||
.unit
|
||||
.get_mut(function_die_uid)
|
||||
.set(gimli::DW_AT_main_subprogram, AttributeValue::Flag(true));
|
||||
dwarf.unit.get_mut(function_die_uid).set(
|
||||
gimli::DW_AT_low_pc,
|
||||
AttributeValue::Address(Address::Constant(function.start())), // TODO: Relocations
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let address_ranges = function.address_ranges();
|
||||
if address_ranges.len() == 1 {
|
||||
let address_range = address_ranges.get(0);
|
||||
dwarf.unit.get_mut(function_die_uid).set(
|
||||
gimli::DW_AT_low_pc,
|
||||
AttributeValue::Address(Address::Constant(address_range.start())), // TODO: Relocations
|
||||
);
|
||||
dwarf.unit.get_mut(function_die_uid).set(
|
||||
gimli::DW_AT_high_pc,
|
||||
AttributeValue::Address(Address::Constant(address_range.end())),
|
||||
);
|
||||
} else {
|
||||
let range_list = RangeList(
|
||||
address_ranges
|
||||
.into_iter()
|
||||
.map(|range| Range::StartLength {
|
||||
begin: Address::Constant(range.start()), // TODO: Relocations?
|
||||
length: range.end() - range.start(),
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
let range_list_id = dwarf.unit.ranges.add(range_list);
|
||||
dwarf.unit.get_mut(function_die_uid).set(
|
||||
gimli::DW_AT_ranges,
|
||||
AttributeValue::RangeListRef(range_list_id),
|
||||
);
|
||||
}
|
||||
|
||||
// DWARFv4 2.18: " If no DW_AT_entry_pc attribute is present, then the entry address is assumed to be the same as the value of the DW_AT_low_pc attribute"
|
||||
if address_ranges.get(0).start() != function.start() {
|
||||
dwarf.unit.get_mut(function_die_uid).set(
|
||||
gimli::DW_AT_entry_pc,
|
||||
AttributeValue::Address(Address::Constant(function.start())),
|
||||
);
|
||||
}
|
||||
|
||||
if function.return_type().contents.type_class() != TypeClass::VoidTypeClass {
|
||||
if let Some(return_type_die_uid) = export_type(
|
||||
format!("{}", function.return_type().contents),
|
||||
function.return_type().contents.as_ref(),
|
||||
bv,
|
||||
defined_types,
|
||||
dwarf,
|
||||
) {
|
||||
dwarf.unit.get_mut(function_die_uid).set(
|
||||
gimli::DW_AT_type,
|
||||
AttributeValue::UnitRef(return_type_die_uid),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for parameter in function.function_type().parameters().unwrap() {
|
||||
let param_die_uid = dwarf
|
||||
.unit
|
||||
.add(function_die_uid, constants::DW_TAG_formal_parameter);
|
||||
|
||||
dwarf.unit.get_mut(param_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(parameter.name.as_bytes().to_vec()),
|
||||
);
|
||||
|
||||
if let Some(target_die_uid) = export_type(
|
||||
format!("{}", parameter.t.contents),
|
||||
¶meter.t.contents,
|
||||
bv,
|
||||
defined_types,
|
||||
dwarf,
|
||||
) {
|
||||
dwarf
|
||||
.unit
|
||||
.get_mut(param_die_uid)
|
||||
.set(gimli::DW_AT_type, AttributeValue::UnitRef(target_die_uid));
|
||||
}
|
||||
}
|
||||
|
||||
if function.function_type().has_variable_arguments().contents {
|
||||
dwarf
|
||||
.unit
|
||||
.add(function_die_uid, constants::DW_TAG_unspecified_parameters);
|
||||
}
|
||||
|
||||
if function.symbol().external() {
|
||||
dwarf
|
||||
.unit
|
||||
.get_mut(function_die_uid)
|
||||
.set(gimli::DW_AT_external, AttributeValue::Flag(true));
|
||||
}
|
||||
|
||||
// TODO : calling convention attr
|
||||
// TODO : local vars
|
||||
}
|
||||
}
|
||||
|
||||
fn export_data_vars(
|
||||
bv: &BinaryView,
|
||||
dwarf: &mut DwarfUnit,
|
||||
defined_types: &mut Vec<(Ref<Type>, UnitEntryId)>,
|
||||
) {
|
||||
let root = dwarf.unit.root();
|
||||
|
||||
for data_variable in &bv.data_variables() {
|
||||
if let Some(symbol) = data_variable.symbol(bv) {
|
||||
if symbol.sym_type() == SymbolType::External {
|
||||
continue;
|
||||
} else if symbol.sym_type() == SymbolType::Function {
|
||||
continue;
|
||||
} else if symbol.sym_type() == SymbolType::ImportedFunction {
|
||||
continue;
|
||||
} else if symbol.sym_type() == SymbolType::LibraryFunction {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let var_die_uid = dwarf.unit.add(root, constants::DW_TAG_variable);
|
||||
|
||||
if let Some(symbol) = data_variable.symbol(bv) {
|
||||
dwarf.unit.get_mut(var_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(symbol.full_name().as_bytes().to_vec()),
|
||||
);
|
||||
|
||||
if symbol.external() {
|
||||
dwarf
|
||||
.unit
|
||||
.get_mut(var_die_uid)
|
||||
.set(gimli::DW_AT_external, AttributeValue::Flag(true));
|
||||
}
|
||||
} else {
|
||||
dwarf.unit.get_mut(var_die_uid).set(
|
||||
gimli::DW_AT_name,
|
||||
AttributeValue::String(
|
||||
format!("data_{:x}", data_variable.address)
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let mut variable_location = Expression::new();
|
||||
variable_location.op_addr(Address::Constant(data_variable.address));
|
||||
dwarf.unit.get_mut(var_die_uid).set(
|
||||
gimli::DW_AT_location,
|
||||
AttributeValue::Exprloc(variable_location),
|
||||
);
|
||||
|
||||
if let Some(target_die_uid) = export_type(
|
||||
format!("{}", data_variable.t.contents),
|
||||
data_variable.t.contents.as_ref(),
|
||||
bv,
|
||||
defined_types,
|
||||
dwarf,
|
||||
) {
|
||||
dwarf
|
||||
.unit
|
||||
.get_mut(var_die_uid)
|
||||
.set(gimli::DW_AT_type, AttributeValue::UnitRef(target_die_uid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn present_form(bv_arch: &str) -> Vec<FormResponses> {
|
||||
// TODO : Verify inputs (like save location) so that we can fail early
|
||||
// TODO : Add Language field
|
||||
// TODO : Choose to export types/functions/etc
|
||||
let archs = [
|
||||
"Unknown",
|
||||
"Aarch64",
|
||||
"Aarch64_Ilp32",
|
||||
"Arm",
|
||||
"Avr",
|
||||
"Bpf",
|
||||
"I386",
|
||||
"X86_64",
|
||||
"X86_64_X32",
|
||||
"Hexagon",
|
||||
"LoongArch64",
|
||||
"Mips",
|
||||
"Mips64",
|
||||
"Msp430",
|
||||
"PowerPc",
|
||||
"PowerPc64",
|
||||
"Riscv32",
|
||||
"Riscv64",
|
||||
"S390x",
|
||||
"Sbf",
|
||||
"Sparc64",
|
||||
"Wasm32",
|
||||
"Xtensa",
|
||||
];
|
||||
interaction::FormInputBuilder::new()
|
||||
.save_file_field(
|
||||
"Save Location",
|
||||
Some("Debug Files (*.dwo *.debug);;All Files (*)"),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.choice_field(
|
||||
"Architecture",
|
||||
&archs,
|
||||
archs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.min_by(|&(_, arch_name_1), &(_, arch_name_2)| {
|
||||
edit_distance::distance(bv_arch, arch_name_1)
|
||||
.cmp(&edit_distance::distance(bv_arch, arch_name_2))
|
||||
})
|
||||
.map(|(index, _)| index),
|
||||
)
|
||||
// Add actual / better support for formats other than elf?
|
||||
// .choice_field(
|
||||
// "Container Format",
|
||||
// &["Coff", "Elf", "MachO", "Pe", "Wasm", "Xcoff"],
|
||||
// None,
|
||||
// )
|
||||
.get_form_input("Export as DWARF")
|
||||
}
|
||||
|
||||
fn write_dwarf<T: gimli::Endianity>(
|
||||
responses: Vec<FormResponses>,
|
||||
endian: T,
|
||||
dwarf: &mut DwarfUnit,
|
||||
) {
|
||||
if responses.len() < 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
let arch = match responses[1] {
|
||||
Index(0) => Architecture::Unknown,
|
||||
Index(1) => Architecture::Aarch64,
|
||||
Index(2) => Architecture::Aarch64_Ilp32,
|
||||
Index(3) => Architecture::Arm,
|
||||
Index(4) => Architecture::Avr,
|
||||
Index(5) => Architecture::Bpf,
|
||||
Index(6) => Architecture::I386,
|
||||
Index(7) => Architecture::X86_64,
|
||||
Index(8) => Architecture::X86_64_X32,
|
||||
Index(9) => Architecture::Hexagon,
|
||||
Index(10) => Architecture::LoongArch64,
|
||||
Index(11) => Architecture::Mips,
|
||||
Index(12) => Architecture::Mips64,
|
||||
Index(13) => Architecture::Msp430,
|
||||
Index(14) => Architecture::PowerPc,
|
||||
Index(15) => Architecture::PowerPc64,
|
||||
Index(16) => Architecture::Riscv32,
|
||||
Index(17) => Architecture::Riscv64,
|
||||
Index(18) => Architecture::S390x,
|
||||
Index(19) => Architecture::Sbf,
|
||||
Index(20) => Architecture::Sparc64,
|
||||
Index(21) => Architecture::Wasm32,
|
||||
Index(22) => Architecture::Xtensa,
|
||||
_ => Architecture::Unknown,
|
||||
};
|
||||
|
||||
// let format = match responses[2] {
|
||||
// Index(0) => BinaryFormat::Coff,
|
||||
// Index(1) => BinaryFormat::Elf,
|
||||
// Index(2) => BinaryFormat::MachO,
|
||||
// Index(3) => BinaryFormat::Pe,
|
||||
// Index(4) => BinaryFormat::Wasm,
|
||||
// Index(5) => BinaryFormat::Xcoff,
|
||||
// _ => BinaryFormat::Elf,
|
||||
// };
|
||||
|
||||
// TODO : Look in to other options (mangling, flags, etc (see Object::new))
|
||||
let mut out_object = write::Object::new(
|
||||
BinaryFormat::Elf,
|
||||
arch,
|
||||
if endian.is_little_endian() {
|
||||
object::Endianness::Little
|
||||
} else {
|
||||
object::Endianness::Big
|
||||
},
|
||||
);
|
||||
|
||||
// Finally, write the DWARF data to the sections.
|
||||
let mut sections = Sections::new(EndianVec::new(endian));
|
||||
dwarf.write(&mut sections).unwrap();
|
||||
|
||||
sections
|
||||
.for_each(|input_id, input_data| {
|
||||
// Create section in output object
|
||||
let output_id = out_object.add_section(
|
||||
vec![], // Only machos have segment names? see object::write::Object::segment_name
|
||||
input_id.name().as_bytes().to_vec(),
|
||||
SectionKind::Debug, // TODO: Might be wrong
|
||||
);
|
||||
|
||||
// Write data to section in output object
|
||||
let out_section = out_object.section_mut(output_id);
|
||||
if out_section.is_bss() {
|
||||
panic!("Please report this as a bug: output section is bss");
|
||||
} else {
|
||||
out_section.set_data(input_data.clone().into_vec(), 1);
|
||||
}
|
||||
// out_section.flags = in_section.flags(); // TODO
|
||||
|
||||
Ok::<(), ()>(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
if let interaction::FormResponses::String(filename) = &responses[0] {
|
||||
if let Ok(out_data) = out_object.write() {
|
||||
if let Err(err) = fs::write(filename, out_data) {
|
||||
error!("Failed to write DWARF file: {}", err);
|
||||
} else {
|
||||
info!("Successfully saved as DWARF to `{}`", filename);
|
||||
}
|
||||
} else {
|
||||
error!("Failed to write DWARF with requested settings");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn export_dwarf(bv: &BinaryView) {
|
||||
let arch_name = if let Some(arch) = bv.default_arch() {
|
||||
arch.name()
|
||||
} else {
|
||||
BnString::new("Unknown")
|
||||
};
|
||||
let responses = present_form(&arch_name);
|
||||
|
||||
let encoding = gimli::Encoding {
|
||||
format: gimli::Format::Dwarf32,
|
||||
version: 4,
|
||||
address_size: bv.address_size() as u8,
|
||||
};
|
||||
|
||||
// Create a container for a single compilation unit.
|
||||
// TODO : Add attributes to the compilation unit DIE?
|
||||
let mut dwarf = DwarfUnit::new(encoding);
|
||||
dwarf.unit.get_mut(dwarf.unit.root()).set(
|
||||
gimli::DW_AT_producer,
|
||||
AttributeValue::String("Binary Ninja DWARF Export Plugin".as_bytes().to_vec()),
|
||||
);
|
||||
|
||||
// Everything has types, so we need to track what is already defined globally as to not duplicate type entries
|
||||
let mut defined_types: Vec<(Ref<Type>, UnitEntryId)> = vec![];
|
||||
export_types(bv, &mut dwarf, &mut defined_types);
|
||||
export_functions(bv, &mut dwarf, &mut defined_types);
|
||||
export_data_vars(bv, &mut dwarf, &mut defined_types);
|
||||
// TODO: Export all symbols instead of just data vars?
|
||||
// TODO: Sections? Segments?
|
||||
|
||||
if bv.default_endianness() == binaryninja::Endianness::LittleEndian {
|
||||
write_dwarf(responses, gimli::LittleEndian, &mut dwarf);
|
||||
} else {
|
||||
write_dwarf(responses, gimli::BigEndian, &mut dwarf);
|
||||
};
|
||||
}
|
||||
|
||||
struct MyCommand;
|
||||
impl Command for MyCommand {
|
||||
fn action(&self, view: &BinaryView) {
|
||||
export_dwarf(view)
|
||||
}
|
||||
|
||||
fn valid(&self, _view: &BinaryView) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn CorePluginInit() -> bool {
|
||||
init(LevelFilter::Debug).expect("Unable to initialize logger");
|
||||
|
||||
register(
|
||||
"Export as DWARF",
|
||||
"Export current analysis state and annotations as DWARF for import into other tools",
|
||||
MyCommand {},
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
93
examples/dwarf/dwarf_import/CMakeLists.txt
Normal file
93
examples/dwarf/dwarf_import/CMakeLists.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
||||
|
||||
project(dwarf_import)
|
||||
|
||||
file(GLOB PLUGIN_SOURCES CONFIGURE_DEPENDS
|
||||
${PROJECT_SOURCE_DIR}/Cargo.toml
|
||||
${PROJECT_SOURCE_DIR}/src/*.rs
|
||||
${PROJECT_SOURCE_DIR}/../shared/Cargo.toml
|
||||
${PROJECT_SOURCE_DIR}/../shared/src/*.rs)
|
||||
|
||||
file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS
|
||||
${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)
|
||||
set(OUTPUT_PDB_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}dwarf_import.pdb)
|
||||
endif()
|
||||
|
||||
set(OUTPUT_FILE ${CMAKE_STATIC_LIBRARY_PREFIX}dwarf_import${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
set(PLUGIN_PATH ${TARGET_DIR}/${OUTPUT_FILE})
|
||||
|
||||
add_custom_target(dwarf_import ALL DEPENDS ${PLUGIN_PATH})
|
||||
add_dependencies(dwarf_import binaryninjaapi)
|
||||
|
||||
find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin)
|
||||
if(CARGO_API_VERSION)
|
||||
set(RUSTUP_COMMAND ${RUSTUP_PATH} run ${CARGO_API_VERSION} cargo build)
|
||||
else()
|
||||
set(RUSTUP_COMMAND ${RUSTUP_PATH} run ${CARGO_STABLE_VERSION} cargo build)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
if(UNIVERSAL)
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/debug/${OUTPUT_FILE})
|
||||
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/debug/${OUTPUT_FILE})
|
||||
else()
|
||||
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/release/${OUTPUT_FILE})
|
||||
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release/${OUTPUT_FILE})
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PLUGIN_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_CORE_OUTPUT_DIR}
|
||||
${RUSTUP_COMMAND} --target=aarch64-apple-darwin ${CARGO_OPTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_CORE_OUTPUT_DIR}
|
||||
${RUSTUP_COMMAND} --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}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${PLUGIN_PATH} ${BN_CORE_PLUGIN_DIR}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
|
||||
else()
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
set(LIB_PATH ${PROJECT_BINARY_DIR}/target/debug/${OUTPUT_FILE})
|
||||
else()
|
||||
set(LIB_PATH ${PROJECT_BINARY_DIR}/target/release/${OUTPUT_FILE})
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PLUGIN_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E env MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} ${RUSTUP_COMMAND} ${CARGO_OPTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${PLUGIN_PATH} ${BN_CORE_PLUGIN_DIR}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
add_custom_command(
|
||||
OUTPUT ${PLUGIN_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} ${RUSTUP_COMMAND} ${CARGO_OPTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${PLUGIN_PATH} ${BN_CORE_PLUGIN_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_PDB_NAME} ${BN_CORE_PLUGIN_DIR}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
|
||||
else()
|
||||
add_custom_command(
|
||||
OUTPUT ${PLUGIN_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BN_CORE_OUTPUT_DIR} ${RUSTUP_COMMAND} ${CARGO_OPTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${PLUGIN_PATH} ${BN_CORE_PLUGIN_DIR}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
|
||||
endif()
|
||||
14
examples/dwarf/dwarf_import/Cargo.toml
Normal file
14
examples/dwarf/dwarf_import/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "dwarf_import"
|
||||
version = "0.1.0"
|
||||
authors = ["KyleMiles <kyle@vector35.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
dwarfreader = { path = "../shared/" }
|
||||
binaryninja = { path = "../../../" }
|
||||
gimli = "0.27"
|
||||
log = "0.4.17"
|
||||
390
examples/dwarf/dwarf_import/src/die_handlers.rs
Normal file
390
examples/dwarf/dwarf_import/src/die_handlers.rs
Normal file
@@ -0,0 +1,390 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID};
|
||||
use crate::helpers::*;
|
||||
use crate::types::get_type;
|
||||
|
||||
use binaryninja::{
|
||||
rc::*,
|
||||
types::{EnumerationBuilder, FunctionParameter, ReferenceType, Type, TypeBuilder},
|
||||
};
|
||||
|
||||
use gimli::{constants, AttributeValue::Encoding, DebuggingInformationEntry, Reader, Unit};
|
||||
|
||||
pub(crate) fn handle_base_type<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
) -> Option<Ref<Type>> {
|
||||
// All base types have:
|
||||
// DW_AT_encoding (our concept of type_class)
|
||||
// DW_AT_byte_size and/or DW_AT_bit_size
|
||||
// *DW_AT_name
|
||||
// *DW_AT_endianity (assumed default for arch)
|
||||
// *DW_AT_data_bit_offset (assumed 0)
|
||||
// *Some indication of signedness?
|
||||
// * = Optional
|
||||
|
||||
let name = debug_info_builder_context.get_name(unit, entry)?;
|
||||
let size = get_size_as_usize(entry)?;
|
||||
match entry.attr_value(constants::DW_AT_encoding) {
|
||||
Ok(Some(Encoding(encoding))) => {
|
||||
match encoding {
|
||||
constants::DW_ATE_address => None,
|
||||
constants::DW_ATE_boolean => Some(Type::bool()),
|
||||
constants::DW_ATE_complex_float => None,
|
||||
constants::DW_ATE_float => Some(Type::named_float(size, name)),
|
||||
constants::DW_ATE_signed => Some(Type::named_int(size, true, name)),
|
||||
constants::DW_ATE_signed_char => Some(Type::named_int(size, true, name)),
|
||||
constants::DW_ATE_unsigned => Some(Type::named_int(size, false, name)),
|
||||
constants::DW_ATE_unsigned_char => Some(Type::named_int(size, false, name)),
|
||||
constants::DW_ATE_imaginary_float => None,
|
||||
constants::DW_ATE_packed_decimal => None,
|
||||
constants::DW_ATE_numeric_string => None,
|
||||
constants::DW_ATE_edited => None,
|
||||
constants::DW_ATE_signed_fixed => None,
|
||||
constants::DW_ATE_unsigned_fixed => None,
|
||||
constants::DW_ATE_decimal_float => Some(Type::named_float(size, name)),
|
||||
constants::DW_ATE_UTF => Some(Type::named_int(size, false, name)), // TODO : Verify
|
||||
constants::DW_ATE_UCS => None,
|
||||
constants::DW_ATE_ASCII => None, // Some sort of array?
|
||||
constants::DW_ATE_lo_user => None,
|
||||
constants::DW_ATE_hi_user => None,
|
||||
_ => None, // Anything else is invalid at time of writing (gimli v0.23.0)
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_enum<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
) -> Option<Ref<Type>> {
|
||||
// All base types have:
|
||||
// DW_AT_byte_size
|
||||
// *DW_AT_name
|
||||
// *DW_AT_enum_class
|
||||
// *DW_AT_type
|
||||
// ?DW_AT_abstract_origin
|
||||
// ?DW_AT_accessibility
|
||||
// ?DW_AT_allocated
|
||||
// ?DW_AT_associated
|
||||
// ?DW_AT_bit_size
|
||||
// ?DW_AT_bit_stride
|
||||
// ?DW_AT_byte_stride
|
||||
// ?DW_AT_data_location
|
||||
// ?DW_AT_declaration
|
||||
// ?DW_AT_description
|
||||
// ?DW_AT_sibling
|
||||
// ?DW_AT_signature
|
||||
// ?DW_AT_specification
|
||||
// ?DW_AT_start_scope
|
||||
// ?DW_AT_visibility
|
||||
// * = Optional
|
||||
|
||||
// Children of enumeration_types are enumerators which contain:
|
||||
// DW_AT_name
|
||||
// DW_AT_const_value
|
||||
// *DW_AT_description
|
||||
|
||||
let enumeration_builder = EnumerationBuilder::new();
|
||||
|
||||
let mut tree = unit.entries_tree(Some(entry.offset())).unwrap();
|
||||
let mut children = tree.root().unwrap().children();
|
||||
while let Ok(Some(child)) = children.next() {
|
||||
if child.entry().tag() == constants::DW_TAG_enumerator {
|
||||
let name = debug_info_builder_context.get_name(unit, child.entry())?;
|
||||
let value = get_attr_as_u64(
|
||||
&child
|
||||
.entry()
|
||||
.attr(constants::DW_AT_const_value)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
enumeration_builder.insert(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
Some(Type::enumeration(
|
||||
&enumeration_builder.finalize(),
|
||||
get_size_as_usize(entry).unwrap_or(8),
|
||||
false,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn handle_typedef(
|
||||
debug_info_builder: &mut DebugInfoBuilder,
|
||||
entry_type: Option<TypeUID>,
|
||||
typedef_name: String,
|
||||
) -> (Option<Ref<Type>>, bool) {
|
||||
// All base types have:
|
||||
// DW_AT_name
|
||||
// *DW_AT_type
|
||||
// * = Optional
|
||||
|
||||
// This will fail in the case where we have a typedef to a type that doesn't exist (failed to parse, incomplete, etc)
|
||||
if let Some(entry_type_offset) = entry_type {
|
||||
if let Some((name, t)) = debug_info_builder.get_type(entry_type_offset) {
|
||||
if typedef_name == name {
|
||||
return (Some(t), false);
|
||||
} else if typedef_name != name {
|
||||
return (Some(t), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5.3: "typedef represents a declaration of the type that is not also a definition"
|
||||
(None, false)
|
||||
}
|
||||
|
||||
pub(crate) fn handle_pointer<R: Reader<Offset = usize>>(
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
debug_info_builder: &mut DebugInfoBuilder,
|
||||
entry_type: Option<TypeUID>,
|
||||
reference_type: ReferenceType,
|
||||
) -> Option<Ref<Type>> {
|
||||
// All pointer types have:
|
||||
// DW_AT_type
|
||||
// *DW_AT_byte_size
|
||||
// ?DW_AT_name
|
||||
// ?DW_AT_address
|
||||
// ?DW_AT_allocated
|
||||
// ?DW_AT_associated
|
||||
// ?DW_AT_data_location
|
||||
// * = Optional
|
||||
|
||||
if let Some(pointer_size) = get_size_as_usize(entry) {
|
||||
if let Some(entry_type_offset) = entry_type {
|
||||
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
|
||||
Some(Type::pointer_of_width(
|
||||
parent_type.as_ref(),
|
||||
pointer_size,
|
||||
false,
|
||||
false,
|
||||
Some(reference_type),
|
||||
))
|
||||
} else {
|
||||
Some(Type::pointer_of_width(
|
||||
Type::void().as_ref(),
|
||||
pointer_size,
|
||||
false,
|
||||
false,
|
||||
Some(reference_type),
|
||||
))
|
||||
}
|
||||
} else if let Some(entry_type_offset) = entry_type {
|
||||
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
|
||||
Some(Type::pointer_of_width(
|
||||
parent_type.as_ref(),
|
||||
debug_info_builder_context.default_address_size(),
|
||||
false,
|
||||
false,
|
||||
Some(reference_type),
|
||||
))
|
||||
} else {
|
||||
Some(Type::pointer_of_width(
|
||||
Type::void().as_ref(),
|
||||
debug_info_builder_context.default_address_size(),
|
||||
false,
|
||||
false,
|
||||
Some(reference_type),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_array<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder: &mut DebugInfoBuilder,
|
||||
entry_type: Option<TypeUID>,
|
||||
) -> Option<Ref<Type>> {
|
||||
// All array types have:
|
||||
// DW_AT_type
|
||||
// *DW_AT_name
|
||||
// *DW_AT_ordering
|
||||
// *DW_AT_byte_stride or DW_AT_bit_stride
|
||||
// *DW_AT_byte_size or DW_AT_bit_size
|
||||
// *DW_AT_allocated
|
||||
// *DW_AT_associated and
|
||||
// *DW_AT_data_location
|
||||
// * = Optional
|
||||
// For multidimensional arrays, DW_TAG_subrange_type or DW_TAG_enumeration_type
|
||||
|
||||
if let Some(entry_type_offset) = entry_type {
|
||||
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
|
||||
|
||||
let mut tree = unit.entries_tree(Some(entry.offset())).unwrap();
|
||||
let mut children = tree.root().unwrap().children();
|
||||
|
||||
// TODO : This is currently applying the size in reverse order
|
||||
let mut result_type: Option<Ref<Type>> = None;
|
||||
while let Ok(Some(child)) = children.next() {
|
||||
if let Some(inner_type) = result_type {
|
||||
result_type = Some(Type::array(
|
||||
inner_type.as_ref(),
|
||||
get_subrange_size(child.entry()),
|
||||
));
|
||||
} else {
|
||||
result_type = Some(Type::array(
|
||||
parent_type.as_ref(),
|
||||
get_subrange_size(child.entry()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
result_type.map_or(Some(Type::array(parent_type.as_ref(), 0)), Some)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_function<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
debug_info_builder: &mut DebugInfoBuilder,
|
||||
entry_type: Option<TypeUID>,
|
||||
) -> Option<Ref<Type>> {
|
||||
// All subroutine types have:
|
||||
// *DW_AT_name
|
||||
// *DW_AT_type (if not provided, void)
|
||||
// *DW_AT_prototyped
|
||||
// ?DW_AT_abstract_origin
|
||||
// ?DW_AT_accessibility
|
||||
// ?DW_AT_address_class
|
||||
// ?DW_AT_allocated
|
||||
// ?DW_AT_associated
|
||||
// ?DW_AT_data_location
|
||||
// ?DW_AT_declaration
|
||||
// ?DW_AT_description
|
||||
// ?DW_AT_sibling
|
||||
// ?DW_AT_start_scope
|
||||
// ?DW_AT_visibility
|
||||
// * = Optional
|
||||
|
||||
// May have children, including DW_TAG_formal_parameters, which all have:
|
||||
// *DW_AT_type
|
||||
// * = Optional
|
||||
// or is otherwise DW_TAG_unspecified_parameters
|
||||
|
||||
let return_type = match entry_type {
|
||||
Some(entry_type_offset) => {
|
||||
debug_info_builder
|
||||
.get_type(entry_type_offset)
|
||||
.expect("Subroutine return type was not processed")
|
||||
.1
|
||||
}
|
||||
None => Type::void(),
|
||||
};
|
||||
|
||||
// Alias function type in the case that it contains itself
|
||||
if let Some(name) = debug_info_builder_context.get_name(unit, entry) {
|
||||
debug_info_builder.add_type(
|
||||
get_uid(unit, entry),
|
||||
name.clone(),
|
||||
Type::named_type_from_type(
|
||||
name,
|
||||
&Type::function::<String, &binaryninja::types::Type>(
|
||||
return_type.as_ref(),
|
||||
&[],
|
||||
false,
|
||||
),
|
||||
),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
let mut parameters: Vec<FunctionParameter<String>> = vec![];
|
||||
let mut variable_arguments = false;
|
||||
|
||||
// Get all the children and populate
|
||||
let mut tree = unit.entries_tree(Some(entry.offset())).unwrap();
|
||||
let mut children = tree.root().unwrap().children();
|
||||
while let Ok(Some(child)) = children.next() {
|
||||
if child.entry().tag() == constants::DW_TAG_formal_parameter {
|
||||
if let (Some(child_uid), Some(name)) = {
|
||||
(
|
||||
get_type(
|
||||
unit,
|
||||
child.entry(),
|
||||
debug_info_builder_context,
|
||||
debug_info_builder,
|
||||
),
|
||||
debug_info_builder_context.get_name(unit, child.entry()),
|
||||
)
|
||||
} {
|
||||
let child_type = debug_info_builder.get_type(child_uid).unwrap().1;
|
||||
parameters.push(FunctionParameter::new(child_type, name, None));
|
||||
}
|
||||
} else if child.entry().tag() == constants::DW_TAG_unspecified_parameters {
|
||||
variable_arguments = true;
|
||||
}
|
||||
}
|
||||
|
||||
if debug_info_builder_context.get_name(unit, entry).is_some() {
|
||||
debug_info_builder.remove_type(get_uid(unit, entry));
|
||||
}
|
||||
|
||||
Some(Type::function(
|
||||
return_type.as_ref(),
|
||||
¶meters,
|
||||
variable_arguments,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn handle_const(
|
||||
debug_info_builder: &mut DebugInfoBuilder,
|
||||
entry_type: Option<TypeUID>,
|
||||
) -> Option<Ref<Type>> {
|
||||
// All const types have:
|
||||
// ?DW_AT_allocated
|
||||
// ?DW_AT_associated
|
||||
// ?DW_AT_data_location
|
||||
// ?DW_AT_name
|
||||
// ?DW_AT_sibling
|
||||
// ?DW_AT_type
|
||||
|
||||
if let Some(entry_type_offset) = entry_type {
|
||||
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
|
||||
Some((*parent_type).to_builder().set_const(true).finalize())
|
||||
} else {
|
||||
Some(TypeBuilder::void().set_const(true).finalize())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_volatile(
|
||||
debug_info_builder: &mut DebugInfoBuilder,
|
||||
entry_type: Option<TypeUID>,
|
||||
) -> Option<Ref<Type>> {
|
||||
// All const types have:
|
||||
// ?DW_AT_allocated
|
||||
// ?DW_AT_associated
|
||||
// ?DW_AT_data_location
|
||||
// ?DW_AT_name
|
||||
// ?DW_AT_sibling
|
||||
// ?DW_AT_type
|
||||
|
||||
if let Some(entry_type_offset) = entry_type {
|
||||
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
|
||||
Some((*parent_type).to_builder().set_volatile(true).finalize())
|
||||
} else {
|
||||
Some(TypeBuilder::void().set_volatile(true).finalize())
|
||||
}
|
||||
}
|
||||
407
examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs
Normal file
407
examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs
Normal file
@@ -0,0 +1,407 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::helpers::{get_uid, resolve_specification, DieReference};
|
||||
|
||||
use binaryninja::{
|
||||
binaryview::{BinaryView, BinaryViewBase, BinaryViewExt},
|
||||
debuginfo::{DebugFunctionInfo, DebugInfo},
|
||||
platform::Platform,
|
||||
rc::*,
|
||||
symbol::SymbolType,
|
||||
templatesimplifier::simplify_str_to_fqn,
|
||||
types::{Conf, FunctionParameter, Type},
|
||||
};
|
||||
|
||||
use gimli::{DebuggingInformationEntry, Dwarf, Reader, Unit};
|
||||
|
||||
use log::{error, warn};
|
||||
use std::{
|
||||
collections::{hash_map::Values, HashMap},
|
||||
hash::Hash,
|
||||
};
|
||||
|
||||
pub(crate) type TypeUID = usize;
|
||||
|
||||
/////////////////////////
|
||||
// FunctionInfoBuilder
|
||||
|
||||
// TODO : Function local variables
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub(crate) struct FunctionInfoBuilder {
|
||||
pub(crate) full_name: Option<String>,
|
||||
pub(crate) raw_name: Option<String>,
|
||||
pub(crate) return_type: Option<TypeUID>,
|
||||
pub(crate) address: Option<u64>,
|
||||
pub(crate) parameters: Vec<Option<(String, TypeUID)>>,
|
||||
pub(crate) platform: Option<Ref<Platform>>,
|
||||
}
|
||||
|
||||
impl FunctionInfoBuilder {
|
||||
pub(crate) fn update(
|
||||
&mut self,
|
||||
full_name: Option<String>,
|
||||
raw_name: Option<String>,
|
||||
return_type: Option<TypeUID>,
|
||||
address: Option<u64>,
|
||||
parameters: Vec<Option<(String, TypeUID)>>,
|
||||
) {
|
||||
if full_name.is_some() {
|
||||
self.full_name = full_name;
|
||||
}
|
||||
|
||||
if raw_name.is_some() {
|
||||
self.raw_name = raw_name;
|
||||
}
|
||||
|
||||
if return_type.is_some() {
|
||||
self.return_type = return_type;
|
||||
}
|
||||
|
||||
if address.is_some() {
|
||||
self.address = address;
|
||||
}
|
||||
|
||||
for (i, new_parameter) in parameters.into_iter().enumerate() {
|
||||
match self.parameters.get(i) {
|
||||
Some(None) => self.parameters[i] = new_parameter,
|
||||
Some(Some(_)) => (),
|
||||
// Some(Some((name, _))) if name.as_bytes().is_empty() => {
|
||||
// self.parameters[i] = new_parameter
|
||||
// }
|
||||
// Some(Some((_, uid))) if *uid == 0 => self.parameters[i] = new_parameter, // TODO : This is a placebo....void types aren't actually UID 0
|
||||
_ => self.parameters.push(new_parameter),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
// DebugInfoBuilder
|
||||
|
||||
// TODO : Don't make this pub...fix the value thing
|
||||
pub(crate) struct DebugType {
|
||||
name: String,
|
||||
t: Ref<Type>,
|
||||
commit: bool,
|
||||
}
|
||||
|
||||
pub(crate) struct DebugInfoBuilderContext<R: Reader<Offset = usize>> {
|
||||
dwarf: Dwarf<R>,
|
||||
units: Vec<Unit<R>>,
|
||||
names: HashMap<TypeUID, String>,
|
||||
default_address_size: usize,
|
||||
pub(crate) total_die_count: usize,
|
||||
}
|
||||
|
||||
impl<R: Reader<Offset = usize>> DebugInfoBuilderContext<R> {
|
||||
pub(crate) fn new(view: &BinaryView, dwarf: Dwarf<R>) -> Option<Self> {
|
||||
let mut units = vec![];
|
||||
let mut iter = dwarf.units();
|
||||
while let Ok(Some(header)) = iter.next() {
|
||||
if let Ok(unit) = dwarf.unit(header) {
|
||||
units.push(unit);
|
||||
} else {
|
||||
error!("Unable to read DWARF information. File may be malformed or corrupted. Not applying debug info.");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(Self {
|
||||
dwarf,
|
||||
units,
|
||||
names: HashMap::new(),
|
||||
default_address_size: view.address_size(),
|
||||
total_die_count: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn dwarf(&self) -> &Dwarf<R> {
|
||||
&self.dwarf
|
||||
}
|
||||
|
||||
pub(crate) fn units(&self) -> &[Unit<R>] {
|
||||
&self.units
|
||||
}
|
||||
|
||||
pub(crate) fn default_address_size(&self) -> usize {
|
||||
self.default_address_size
|
||||
}
|
||||
|
||||
pub(crate) fn set_name(&mut self, die_uid: TypeUID, name: String) {
|
||||
assert!(self.names.insert(die_uid, name).is_none());
|
||||
}
|
||||
|
||||
pub(crate) fn get_name(
|
||||
&self,
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
) -> Option<String> {
|
||||
match resolve_specification(unit, entry, self) {
|
||||
DieReference::UnitAndOffset((entry_unit, entry_offset)) => self
|
||||
.names
|
||||
.get(&get_uid(
|
||||
entry_unit,
|
||||
&entry_unit.entry(entry_offset).unwrap(),
|
||||
))
|
||||
.cloned(),
|
||||
DieReference::Err => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DWARF info is stored and displayed in a tree, but is really a graph
|
||||
// The purpose of this builder is to help resolve those graph edges by mapping partial function
|
||||
// info and types to one DIE's UID (T) before adding the completed info to BN's debug info
|
||||
pub(crate) struct DebugInfoBuilder {
|
||||
functions: Vec<FunctionInfoBuilder>,
|
||||
types: HashMap<TypeUID, DebugType>,
|
||||
data_variables: HashMap<u64, (Option<String>, TypeUID)>,
|
||||
}
|
||||
|
||||
impl DebugInfoBuilder {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
functions: vec![],
|
||||
types: HashMap::new(),
|
||||
data_variables: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn insert_function(
|
||||
&mut self,
|
||||
full_name: Option<String>,
|
||||
raw_name: Option<String>,
|
||||
return_type: Option<TypeUID>,
|
||||
address: Option<u64>,
|
||||
parameters: Vec<Option<(String, TypeUID)>>,
|
||||
) {
|
||||
// Raw names should be the primary key, but if they don't exist, use the full name
|
||||
// TODO : Consider further falling back on address/architecture
|
||||
if let Some(function) = self
|
||||
.functions
|
||||
.iter_mut()
|
||||
.find(|func| func.raw_name.is_some() && func.raw_name == raw_name)
|
||||
{
|
||||
function.update(full_name, raw_name, return_type, address, parameters);
|
||||
} else if let Some(function) = self.functions.iter_mut().find(|func| {
|
||||
(func.raw_name.is_none() || raw_name.is_none())
|
||||
&& func.full_name.is_some()
|
||||
&& func.full_name == full_name
|
||||
}) {
|
||||
function.update(full_name, raw_name, return_type, address, parameters);
|
||||
} else {
|
||||
self.functions.push(FunctionInfoBuilder {
|
||||
full_name,
|
||||
raw_name,
|
||||
return_type,
|
||||
address,
|
||||
parameters,
|
||||
platform: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn functions(&self) -> &[FunctionInfoBuilder] {
|
||||
&self.functions
|
||||
}
|
||||
|
||||
pub(crate) fn types(&self) -> Values<'_, TypeUID, DebugType> {
|
||||
self.types.values()
|
||||
}
|
||||
|
||||
pub(crate) fn add_type(
|
||||
&mut self,
|
||||
type_uid: TypeUID,
|
||||
name: String,
|
||||
t: Ref<Type>,
|
||||
commit: bool,
|
||||
) {
|
||||
if let Some(DebugType {
|
||||
name: existing_name,
|
||||
t: existing_type,
|
||||
commit: _,
|
||||
}) = self.types.insert(
|
||||
type_uid,
|
||||
DebugType {
|
||||
name: name.clone(),
|
||||
t: t.clone(),
|
||||
commit,
|
||||
},
|
||||
) {
|
||||
if existing_type != t && commit {
|
||||
error!("DWARF info contains duplicate type definition. Overwriting type `{}` (named `{:?}`) with `{}` (named `{:?}`)",
|
||||
existing_type,
|
||||
existing_name,
|
||||
t,
|
||||
name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn remove_type(&mut self, type_uid: TypeUID) {
|
||||
self.types.remove(&type_uid);
|
||||
}
|
||||
|
||||
// TODO : Non-copy?
|
||||
pub(crate) fn get_type(&self, type_uid: TypeUID) -> Option<(String, Ref<Type>)> {
|
||||
self.types
|
||||
.get(&type_uid)
|
||||
.map(|type_ref_ref| (type_ref_ref.name.clone(), type_ref_ref.t.clone()))
|
||||
}
|
||||
|
||||
pub(crate) fn contains_type(&self, type_uid: TypeUID) -> bool {
|
||||
self.types.get(&type_uid).is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn add_data_variable(
|
||||
&mut self,
|
||||
address: u64,
|
||||
name: Option<String>,
|
||||
type_uid: TypeUID,
|
||||
) {
|
||||
if let Some((_existing_name, existing_type_uid)) =
|
||||
self.data_variables.insert(address, (name, type_uid))
|
||||
{
|
||||
let existing_type = self.get_type(existing_type_uid).unwrap().1;
|
||||
let new_type = self.get_type(type_uid).unwrap().1;
|
||||
|
||||
if existing_type_uid != type_uid || existing_type != new_type {
|
||||
error!("DWARF info contains duplicate data variable definition. Overwriting data variable at 0x{:08x} (`{}`) with `{}`",
|
||||
address,
|
||||
self.get_type(existing_type_uid).unwrap().1,
|
||||
self.get_type(type_uid).unwrap().1
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn commit_types(&self, debug_info: &mut DebugInfo) {
|
||||
for debug_type in self.types() {
|
||||
if debug_type.commit {
|
||||
debug_info.add_type(debug_type.name.clone(), debug_type.t.as_ref(), &[]);
|
||||
// TODO : Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : Consume data?
|
||||
fn commit_data_variables(&self, debug_info: &mut DebugInfo) {
|
||||
for (&address, (name, type_uid)) in &self.data_variables {
|
||||
assert!(debug_info.add_data_variable(
|
||||
address,
|
||||
&self.get_type(*type_uid).unwrap().1,
|
||||
name.clone(),
|
||||
&[] // TODO : Components
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn get_function_type(&self, function: &FunctionInfoBuilder) -> Ref<Type> {
|
||||
let return_type = match function.return_type {
|
||||
Some(return_type_id) => Conf::new(self.get_type(return_type_id).unwrap().1.clone(), 0),
|
||||
_ => Conf::new(binaryninja::types::Type::void(), 0),
|
||||
};
|
||||
|
||||
let parameters: Vec<FunctionParameter<String>> = function
|
||||
.parameters
|
||||
.iter()
|
||||
.filter_map(|parameter| match parameter {
|
||||
Some((name, 0)) => Some(FunctionParameter::new(Type::void(), name.clone(), None)),
|
||||
Some((name, uid)) => Some(FunctionParameter::new(
|
||||
self.get_type(*uid).unwrap().1,
|
||||
name.clone(),
|
||||
None,
|
||||
)),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
// TODO : Handle
|
||||
let variable_parameters = false;
|
||||
|
||||
binaryninja::types::Type::function(&return_type, ¶meters, variable_parameters)
|
||||
}
|
||||
|
||||
fn commit_functions(&self, debug_info: &mut DebugInfo) {
|
||||
for function in self.functions() {
|
||||
// let calling_convention: Option<Ref<CallingConvention<CoreArchitecture>>> = None;
|
||||
|
||||
debug_info.add_function(DebugFunctionInfo::new(
|
||||
function.full_name.clone(),
|
||||
function.full_name.clone(), // TODO : This should eventually be changed, but the "full_name" should probably be the unsimplified version, and the "short_name" should be the simplified version...currently the symbols view shows the full version, so changing it here too makes it look bad in the UI
|
||||
function.raw_name.clone(),
|
||||
Some(self.get_function_type(function)),
|
||||
function.address,
|
||||
function.platform.clone(),
|
||||
vec![], // TODO : Components
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn post_process(&mut self, bv: &BinaryView, _debug_info: &mut DebugInfo) -> &Self {
|
||||
// TODO : We don't need post-processing if we process correctly the first time....
|
||||
// When originally resolving names, we need to check:
|
||||
// If there's already a name from binja that's "more correct" than what we found (has more namespaces)
|
||||
// If there's no name for the DIE, but there's a linkage name that's resolved in binja to a usable name
|
||||
// This is no longer true, because DWARF doesn't provide platform information for functions, so we at least need to post-process thumb functions
|
||||
|
||||
for func in &mut self.functions {
|
||||
// If the function's raw name already exists in the binary...
|
||||
if let Some(raw_name) = &func.raw_name {
|
||||
if let Ok(symbol) = bv.symbol_by_raw_name(raw_name) {
|
||||
// Link mangled names without addresses to existing symbols in the binary
|
||||
if func.address.is_none() && func.raw_name.is_some() {
|
||||
// DWARF doesn't contain GOT info, so remove any entries there...they will be wrong (relying on Binja's mechanisms for the GOT is good )
|
||||
if symbol.sym_type() != SymbolType::ImportAddress {
|
||||
func.address = Some(symbol.address());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(full_name) = &func.full_name {
|
||||
let func_full_name = full_name;
|
||||
let symbol_full_name = symbol.full_name();
|
||||
|
||||
// If our name has fewer namespaces than the existing name, assume we lost the namespace info
|
||||
if simplify_str_to_fqn(func_full_name, true).len()
|
||||
< simplify_str_to_fqn(symbol_full_name.clone(), true).len()
|
||||
{
|
||||
func.full_name =
|
||||
Some(symbol_full_name.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(address) = func.address {
|
||||
let existing_functions = bv.functions_at(address);
|
||||
if existing_functions.len() > 1 {
|
||||
warn!("Multiple existing functions at address {address:08x}. One or more functions at this address may have the wrong platform information. Please report this binary.");
|
||||
} else if existing_functions.len() == 1 {
|
||||
func.platform = Some(existing_functions.get(0).platform());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn commit_info(&self, debug_info: &mut DebugInfo) {
|
||||
self.commit_types(debug_info);
|
||||
self.commit_data_variables(debug_info);
|
||||
self.commit_functions(debug_info);
|
||||
}
|
||||
}
|
||||
78
examples/dwarf/dwarf_import/src/functions.rs
Normal file
78
examples/dwarf/dwarf_import/src/functions.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID};
|
||||
use crate::helpers::*;
|
||||
use crate::types::get_type;
|
||||
|
||||
use gimli::{constants, DebuggingInformationEntry, Reader, Unit};
|
||||
|
||||
fn get_parameters<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
debug_info_builder: &mut DebugInfoBuilder,
|
||||
) -> Vec<Option<(String, TypeUID)>> {
|
||||
if !entry.has_children() {
|
||||
vec![]
|
||||
} else {
|
||||
// We make a new tree from the current entry to iterate over its children
|
||||
let mut sub_die_tree = unit.entries_tree(Some(entry.offset())).unwrap();
|
||||
let root = sub_die_tree.root().unwrap();
|
||||
|
||||
let mut result = vec![];
|
||||
let mut children = root.children();
|
||||
while let Some(child) = children.next().unwrap() {
|
||||
match child.entry().tag() {
|
||||
constants::DW_TAG_formal_parameter => {
|
||||
let name = debug_info_builder_context.get_name(unit, child.entry());
|
||||
let type_ = get_type(
|
||||
unit,
|
||||
child.entry(),
|
||||
debug_info_builder_context,
|
||||
debug_info_builder,
|
||||
);
|
||||
if let Some(parameter_name) = name {
|
||||
if let Some(parameter_type) = type_ {
|
||||
result.push(Some((parameter_name, parameter_type)));
|
||||
} else {
|
||||
result.push(Some((parameter_name, 0)))
|
||||
}
|
||||
} else {
|
||||
result.push(None)
|
||||
}
|
||||
}
|
||||
constants::DW_TAG_unspecified_parameters => (),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_function_entry<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
debug_info_builder: &mut DebugInfoBuilder,
|
||||
) {
|
||||
// Collect function properties (if they exist in this DIE)
|
||||
let full_name = debug_info_builder_context.get_name(unit, entry);
|
||||
let raw_name = get_raw_name(unit, entry, debug_info_builder_context);
|
||||
let return_type = get_type(unit, entry, debug_info_builder_context, debug_info_builder);
|
||||
let address = get_start_address(unit, entry, debug_info_builder_context);
|
||||
let parameters = get_parameters(unit, entry, debug_info_builder_context, debug_info_builder);
|
||||
|
||||
debug_info_builder.insert_function(full_name, raw_name, return_type, address, parameters);
|
||||
}
|
||||
293
examples/dwarf/dwarf_import/src/helpers.rs
Normal file
293
examples/dwarf/dwarf_import/src/helpers.rs
Normal file
@@ -0,0 +1,293 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::DebugInfoBuilderContext;
|
||||
|
||||
use gimli::{
|
||||
constants, Attribute, AttributeValue,
|
||||
AttributeValue::{DebugInfoRef, UnitRef},
|
||||
DebuggingInformationEntry, Operation, Reader, Unit, UnitOffset, UnitSectionOffset,
|
||||
};
|
||||
|
||||
use log::warn;
|
||||
|
||||
pub(crate) fn get_uid<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
) -> usize {
|
||||
match entry.offset().to_unit_section_offset(unit) {
|
||||
UnitSectionOffset::DebugInfoOffset(o) => o.0,
|
||||
UnitSectionOffset::DebugTypesOffset(o) => o.0,
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////
|
||||
// DIE attr convenience functions
|
||||
|
||||
pub(crate) enum DieReference<'a, R: Reader<Offset = usize>> {
|
||||
UnitAndOffset((&'a Unit<R>, UnitOffset)),
|
||||
Err,
|
||||
}
|
||||
|
||||
pub(crate) fn get_attr_die<'a, R: Reader<Offset = usize>>(
|
||||
unit: &'a Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &'a DebugInfoBuilderContext<R>,
|
||||
attr: constants::DwAt,
|
||||
) -> Option<DieReference<'a, R>> {
|
||||
match entry.attr_value(attr) {
|
||||
Ok(Some(UnitRef(offset))) => Some(DieReference::UnitAndOffset((unit, offset))),
|
||||
Ok(Some(DebugInfoRef(offset))) => {
|
||||
for source_unit in debug_info_builder_context.units() {
|
||||
if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) {
|
||||
return Some(DieReference::UnitAndOffset((source_unit, new_offset)));
|
||||
}
|
||||
}
|
||||
warn!("Failed to fetch DIE. Debug information may be incomplete.");
|
||||
None
|
||||
}
|
||||
// Ok(Some(DebugInfoRefSup(offset))) TODO - dwarf 5 stuff
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_specification<'a, R: Reader<Offset = usize>>(
|
||||
unit: &'a Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &'a DebugInfoBuilderContext<R>,
|
||||
) -> DieReference<'a, R> {
|
||||
if let Some(die_reference) = get_attr_die(
|
||||
unit,
|
||||
entry,
|
||||
debug_info_builder_context,
|
||||
constants::DW_AT_specification,
|
||||
) {
|
||||
match die_reference {
|
||||
DieReference::UnitAndOffset((entry_unit, entry_offset)) => {
|
||||
if let Ok(entry) = entry_unit.entry(entry_offset) {
|
||||
resolve_specification(entry_unit, &entry, debug_info_builder_context)
|
||||
} else {
|
||||
warn!("Failed to fetch DIE. Debug information may be incomplete.");
|
||||
DieReference::Err
|
||||
}
|
||||
}
|
||||
DieReference::Err => DieReference::Err,
|
||||
}
|
||||
} else if let Some(die_reference) = get_attr_die(
|
||||
unit,
|
||||
entry,
|
||||
debug_info_builder_context,
|
||||
constants::DW_AT_abstract_origin,
|
||||
) {
|
||||
match die_reference {
|
||||
DieReference::UnitAndOffset((entry_unit, entry_offset)) => {
|
||||
if entry_offset == entry.offset() {
|
||||
warn!("DWARF information is invalid (infinite abstract origin reference cycle). Debug information may be incomplete.");
|
||||
DieReference::Err
|
||||
} else if let Ok(new_entry) = entry_unit.entry(entry_offset) {
|
||||
resolve_specification(entry_unit, &new_entry, debug_info_builder_context)
|
||||
} else {
|
||||
warn!("Failed to fetch DIE. Debug information may be incomplete.");
|
||||
DieReference::Err
|
||||
}
|
||||
}
|
||||
DieReference::Err => DieReference::Err,
|
||||
}
|
||||
} else {
|
||||
DieReference::UnitAndOffset((unit, entry.offset()))
|
||||
}
|
||||
}
|
||||
|
||||
// Get name from DIE, or referenced dependencies
|
||||
pub(crate) fn get_name<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
) -> Option<String> {
|
||||
match resolve_specification(unit, entry, debug_info_builder_context) {
|
||||
DieReference::UnitAndOffset((entry_unit, entry_offset)) => {
|
||||
if let Ok(Some(attr_val)) = entry_unit
|
||||
.entry(entry_offset)
|
||||
.unwrap()
|
||||
.attr_value(constants::DW_AT_name)
|
||||
{
|
||||
if let Ok(attr_string) = debug_info_builder_context
|
||||
.dwarf()
|
||||
.attr_string(entry_unit, attr_val)
|
||||
{
|
||||
if let Ok(attr_string) = attr_string.to_string() {
|
||||
return Some(attr_string.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if let Some(raw_name) = get_raw_name(unit, entry, debug_info_builder_context) {
|
||||
// if let Some(arch) = debug_info_builder_context.default_architecture() {
|
||||
// if let Ok((_, names)) = demangle_gnu3(&arch, raw_name, true) {
|
||||
// return Some(names.join("::"));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
None
|
||||
}
|
||||
DieReference::Err => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Get raw name from DIE, or referenced dependencies
|
||||
pub(crate) fn get_raw_name<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
) -> Option<String> {
|
||||
if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_linkage_name) {
|
||||
if let Ok(attr_string) = debug_info_builder_context
|
||||
.dwarf()
|
||||
.attr_string(unit, attr_val)
|
||||
{
|
||||
if let Ok(attr_string) = attr_string.to_string() {
|
||||
return Some(attr_string.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// Get the size of an object as a usize
|
||||
pub(crate) fn get_size_as_usize<R: Reader<Offset = usize>>(
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
) -> Option<usize> {
|
||||
if let Ok(Some(attr)) = entry.attr(constants::DW_AT_byte_size) {
|
||||
get_attr_as_usize(attr)
|
||||
} else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_bit_size) {
|
||||
get_attr_as_usize(attr).map(|attr_value| attr_value / 8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Get the size of an object as a u64
|
||||
pub(crate) fn get_size_as_u64<R: Reader<Offset = usize>>(
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
) -> Option<u64> {
|
||||
if let Ok(Some(attr)) = entry.attr(constants::DW_AT_byte_size) {
|
||||
get_attr_as_u64(&attr)
|
||||
} else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_bit_size) {
|
||||
get_attr_as_u64(&attr).map(|attr_value| attr_value / 8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Get the size of a subrange as a u64
|
||||
pub(crate) fn get_subrange_size<R: Reader<Offset = usize>>(
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
) -> u64 {
|
||||
if let Ok(Some(attr)) = entry.attr(constants::DW_AT_upper_bound) {
|
||||
get_attr_as_u64(&attr).map_or(0, |v| v + 1)
|
||||
} else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_count) {
|
||||
get_attr_as_u64(&attr).unwrap_or(0)
|
||||
} else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_lower_bound) {
|
||||
get_attr_as_u64(&attr).map_or(0, |v| v + 1)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
// Get the start address of a function
|
||||
pub(crate) fn get_start_address<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
) -> Option<u64> {
|
||||
if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_low_pc) {
|
||||
match debug_info_builder_context
|
||||
.dwarf()
|
||||
.attr_address(unit, attr_val)
|
||||
{
|
||||
Ok(Some(val)) => Some(val),
|
||||
_ => None,
|
||||
}
|
||||
} else if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_entry_pc) {
|
||||
match debug_info_builder_context
|
||||
.dwarf()
|
||||
.attr_address(unit, attr_val)
|
||||
{
|
||||
Ok(Some(val)) => Some(val),
|
||||
_ => None,
|
||||
}
|
||||
} else if let Ok(Some(attr_value)) = entry.attr_value(constants::DW_AT_ranges) {
|
||||
if let Ok(Some(ranges_offset)) = debug_info_builder_context
|
||||
.dwarf()
|
||||
.attr_ranges_offset(unit, attr_value)
|
||||
{
|
||||
if let Ok(mut ranges) = debug_info_builder_context
|
||||
.dwarf()
|
||||
.ranges(unit, ranges_offset)
|
||||
{
|
||||
if let Ok(Some(range)) = ranges.next() {
|
||||
return Some(range.begin);
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Get an attribute value as a u64 if it can be coerced
|
||||
pub(crate) fn get_attr_as_u64<R: Reader<Offset = usize>>(attr: &Attribute<R>) -> Option<u64> {
|
||||
if let Some(value) = attr.u8_value() {
|
||||
Some(value.into())
|
||||
} else if let Some(value) = attr.u16_value() {
|
||||
Some(value.into())
|
||||
} else if let Some(value) = attr.udata_value() {
|
||||
Some(value)
|
||||
} else {
|
||||
attr.sdata_value().map(|value| value as u64)
|
||||
}
|
||||
}
|
||||
|
||||
// Get an attribute value as a usize if it can be coerced
|
||||
pub(crate) fn get_attr_as_usize<R: Reader<Offset = usize>>(attr: Attribute<R>) -> Option<usize> {
|
||||
if let Some(value) = attr.u8_value() {
|
||||
Some(value.into())
|
||||
} else if let Some(value) = attr.u16_value() {
|
||||
Some(value.into())
|
||||
} else if let Some(value) = attr.udata_value() {
|
||||
Some(value as usize)
|
||||
} else {
|
||||
attr.sdata_value().map(|value| value as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// Get an attribute value as a usize if it can be coerced
|
||||
// Parses DW_OP_address, DW_OP_const
|
||||
pub(crate) fn get_expr_value<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
attr: Attribute<R>,
|
||||
) -> Option<u64> {
|
||||
if let AttributeValue::Exprloc(mut expression) = attr.value() {
|
||||
match Operation::parse(&mut expression.0, unit.encoding()) {
|
||||
Ok(Operation::PlusConstant { value }) => Some(value),
|
||||
Ok(Operation::UnsignedConstant { value }) => Some(value),
|
||||
Ok(Operation::Address { address: 0 }) => None,
|
||||
Ok(Operation::Address { address }) => Some(address),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
295
examples/dwarf/dwarf_import/src/lib.rs
Normal file
295
examples/dwarf/dwarf_import/src/lib.rs
Normal file
@@ -0,0 +1,295 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
mod die_handlers;
|
||||
mod dwarfdebuginfo;
|
||||
mod functions;
|
||||
mod helpers;
|
||||
mod types;
|
||||
|
||||
use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext};
|
||||
use crate::functions::parse_function_entry;
|
||||
use crate::helpers::{get_attr_die, get_name, get_uid, DieReference};
|
||||
use crate::types::parse_data_variable;
|
||||
|
||||
use binaryninja::{
|
||||
binaryview::{BinaryView, BinaryViewExt},
|
||||
debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser},
|
||||
logger,
|
||||
templatesimplifier::simplify_str_to_str,
|
||||
};
|
||||
use dwarfreader::{
|
||||
create_section_reader, get_endian, is_dwo_dwarf, is_non_dwo_dwarf, is_raw_dwo_dwarf,
|
||||
};
|
||||
|
||||
use gimli::{constants, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, SectionId, Unit};
|
||||
|
||||
use log::{error, warn, LevelFilter};
|
||||
|
||||
fn recover_names<R: Reader<Offset = usize>>(
|
||||
debug_info_builder_context: &mut DebugInfoBuilderContext<R>,
|
||||
progress: &dyn Fn(usize, usize) -> Result<(), ()>,
|
||||
) -> bool {
|
||||
let mut iter = debug_info_builder_context.dwarf().units();
|
||||
while let Ok(Some(header)) = iter.next() {
|
||||
let unit = debug_info_builder_context.dwarf().unit(header).unwrap();
|
||||
let mut namespace_qualifiers: Vec<(isize, String)> = vec![];
|
||||
let mut entries = unit.entries();
|
||||
let mut depth = 0;
|
||||
|
||||
// The first entry in the unit is the header for the unit
|
||||
if let Ok(Some((delta_depth, _))) = entries.next_dfs() {
|
||||
depth += delta_depth;
|
||||
debug_info_builder_context.total_die_count += 1;
|
||||
}
|
||||
|
||||
while let Ok(Some((delta_depth, entry))) = entries.next_dfs() {
|
||||
debug_info_builder_context.total_die_count += 1;
|
||||
|
||||
if (*progress)(0, debug_info_builder_context.total_die_count).is_err() {
|
||||
return false; // Parsing canceled
|
||||
};
|
||||
|
||||
depth += delta_depth;
|
||||
if depth < 0 {
|
||||
error!("DWARF information is seriously malformed. Aborting parsing.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO : Better module/component support
|
||||
namespace_qualifiers.retain(|&(entry_depth, _)| entry_depth < depth);
|
||||
|
||||
match entry.tag() {
|
||||
constants::DW_TAG_namespace => {
|
||||
fn resolve_namespace_name<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
namespace_qualifiers: &mut Vec<(isize, String)>,
|
||||
depth: isize,
|
||||
) {
|
||||
if let Some(namespace_qualifier) =
|
||||
get_name(unit, entry, debug_info_builder_context)
|
||||
{
|
||||
namespace_qualifiers.push((depth, namespace_qualifier));
|
||||
} else if let Some(die_reference) = get_attr_die(
|
||||
unit,
|
||||
entry,
|
||||
debug_info_builder_context,
|
||||
constants::DW_AT_extension,
|
||||
) {
|
||||
match die_reference {
|
||||
DieReference::UnitAndOffset((entry_unit, entry_offset)) => {
|
||||
resolve_namespace_name(
|
||||
entry_unit,
|
||||
&entry_unit.entry(entry_offset).unwrap(),
|
||||
debug_info_builder_context,
|
||||
namespace_qualifiers,
|
||||
depth,
|
||||
)
|
||||
}
|
||||
DieReference::Err => {
|
||||
warn!(
|
||||
"Failed to fetch DIE. Debug information may be incomplete."
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
namespace_qualifiers
|
||||
.push((depth, "anonymous_namespace".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
resolve_namespace_name(
|
||||
&unit,
|
||||
entry,
|
||||
debug_info_builder_context,
|
||||
&mut namespace_qualifiers,
|
||||
depth,
|
||||
);
|
||||
}
|
||||
constants::DW_TAG_class_type
|
||||
| constants::DW_TAG_structure_type
|
||||
| constants::DW_TAG_union_type => {
|
||||
if let Some(name) = get_name(&unit, entry, debug_info_builder_context) {
|
||||
namespace_qualifiers.push((depth, name))
|
||||
} else {
|
||||
namespace_qualifiers.push((
|
||||
depth,
|
||||
match entry.tag() {
|
||||
constants::DW_TAG_class_type => "anonymous_class".to_string(),
|
||||
constants::DW_TAG_structure_type => "anonymous_structure".to_string(),
|
||||
constants::DW_TAG_union_type => "anonymous_union".to_string(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
))
|
||||
}
|
||||
debug_info_builder_context.set_name(
|
||||
get_uid(&unit, entry),
|
||||
simplify_str_to_str(
|
||||
namespace_qualifiers
|
||||
.iter()
|
||||
.map(|(_, namespace)| namespace.to_owned())
|
||||
.collect::<Vec<String>>()
|
||||
.join("::"),
|
||||
)
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
constants::DW_TAG_typedef
|
||||
| constants::DW_TAG_subprogram
|
||||
| constants::DW_TAG_enumeration_type => {
|
||||
if let Some(name) = get_name(&unit, entry, debug_info_builder_context) {
|
||||
debug_info_builder_context.set_name(
|
||||
get_uid(&unit, entry),
|
||||
simplify_str_to_str(
|
||||
namespace_qualifiers
|
||||
.iter()
|
||||
.chain(vec![&(-1, name)].into_iter())
|
||||
.map(|(_, namespace)| {
|
||||
namespace.to_owned()
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join("::"),
|
||||
)
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if let Some(name) = get_name(&unit, entry, debug_info_builder_context) {
|
||||
debug_info_builder_context.set_name(get_uid(&unit, entry), name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn parse_unit<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
debug_info_builder: &mut DebugInfoBuilder,
|
||||
progress: &dyn Fn(usize, usize) -> Result<(), ()>,
|
||||
current_die_number: &mut usize,
|
||||
) {
|
||||
let mut entries = unit.entries();
|
||||
|
||||
// Really all we care about as we iterate the entries in a given unit is how they modify state (our perception of the file)
|
||||
// There's a lot of junk we don't care about in DWARF info, so we choose a couple DIEs and mutate state (add functions (which adds the types it uses) and keep track of what namespace we're in)
|
||||
while let Ok(Some((_, entry))) = entries.next_dfs() {
|
||||
*current_die_number += 1;
|
||||
if (*progress)(
|
||||
*current_die_number,
|
||||
debug_info_builder_context.total_die_count,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
return; // Parsing canceled
|
||||
}
|
||||
|
||||
match entry.tag() {
|
||||
constants::DW_TAG_subprogram => {
|
||||
parse_function_entry(unit, entry, debug_info_builder_context, debug_info_builder)
|
||||
}
|
||||
constants::DW_TAG_variable => {
|
||||
parse_data_variable(unit, entry, debug_info_builder_context, debug_info_builder)
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_dwarf(
|
||||
view: &BinaryView,
|
||||
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
|
||||
) -> DebugInfoBuilder {
|
||||
// Determine if this is a DWO
|
||||
// TODO : Make this more robust...some DWOs follow non-DWO conventions
|
||||
let dwo_file = is_dwo_dwarf(view) || is_raw_dwo_dwarf(view);
|
||||
|
||||
// Figure out if it's the given view or the raw view that has the dwarf info in it
|
||||
let raw_view = &view.raw_view().unwrap();
|
||||
let view = if is_dwo_dwarf(view) || is_non_dwo_dwarf(view) {
|
||||
view
|
||||
} else {
|
||||
raw_view
|
||||
};
|
||||
|
||||
// gimli setup
|
||||
let endian = get_endian(view);
|
||||
let mut section_reader =
|
||||
|section_id: SectionId| -> _ { create_section_reader(section_id, view, endian, dwo_file) };
|
||||
let mut dwarf = Dwarf::load(&mut section_reader).unwrap();
|
||||
if dwo_file {
|
||||
dwarf.file_type = DwarfFileType::Dwo;
|
||||
}
|
||||
|
||||
// Create debug info builder and recover name mapping first
|
||||
// Since DWARF is stored as a tree with arbitrary implicit edges among leaves,
|
||||
// it is not possible to correctly track namespaces while you're parsing "in order" without backtracking,
|
||||
// so we just do it up front
|
||||
let mut debug_info_builder = DebugInfoBuilder::new();
|
||||
if let Some(mut debug_info_builder_context) = DebugInfoBuilderContext::new(view, dwarf) {
|
||||
if !recover_names(&mut debug_info_builder_context, &progress)
|
||||
|| debug_info_builder_context.total_die_count == 0
|
||||
{
|
||||
return debug_info_builder;
|
||||
}
|
||||
|
||||
// Parse all the compilation units
|
||||
let mut current_die_number = 0;
|
||||
for unit in debug_info_builder_context.units() {
|
||||
parse_unit(
|
||||
unit,
|
||||
&debug_info_builder_context,
|
||||
&mut debug_info_builder,
|
||||
&progress,
|
||||
&mut current_die_number,
|
||||
);
|
||||
}
|
||||
}
|
||||
debug_info_builder
|
||||
}
|
||||
|
||||
struct DWARFParser;
|
||||
|
||||
impl CustomDebugInfoParser for DWARFParser {
|
||||
fn is_valid(&self, view: &BinaryView) -> bool {
|
||||
dwarfreader::is_valid(view)
|
||||
}
|
||||
|
||||
fn parse_info(
|
||||
&self,
|
||||
debug_info: &mut DebugInfo,
|
||||
bv: &BinaryView,
|
||||
debug_file: &BinaryView,
|
||||
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
|
||||
) -> bool {
|
||||
parse_dwarf(debug_file, progress)
|
||||
.post_process(bv, debug_info)
|
||||
.commit_info(debug_info);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn CorePluginInit() -> bool {
|
||||
logger::init(LevelFilter::Debug).unwrap();
|
||||
|
||||
DebugInfoParser::register("DWARF", DWARFParser {});
|
||||
true
|
||||
}
|
||||
393
examples/dwarf/dwarf_import/src/types.rs
Normal file
393
examples/dwarf/dwarf_import/src/types.rs
Normal file
@@ -0,0 +1,393 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::die_handlers::*;
|
||||
use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID};
|
||||
use crate::helpers::*;
|
||||
|
||||
use binaryninja::{
|
||||
rc::*,
|
||||
types::{
|
||||
MemberAccess, MemberScope, ReferenceType, StructureBuilder, StructureType, Type, TypeClass,
|
||||
},
|
||||
};
|
||||
|
||||
use gimli::{constants, DebuggingInformationEntry, Reader, Unit};
|
||||
|
||||
use log::warn;
|
||||
|
||||
pub(crate) fn parse_data_variable<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
debug_info_builder: &mut DebugInfoBuilder,
|
||||
) {
|
||||
let full_name = debug_info_builder_context.get_name(unit, entry);
|
||||
let type_uid = get_type(unit, entry, debug_info_builder_context, debug_info_builder);
|
||||
|
||||
let address = if let Ok(Some(attr)) = entry.attr(constants::DW_AT_location) {
|
||||
get_expr_value(unit, attr)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let (Some(address), Some(type_uid)) = (address, type_uid) {
|
||||
debug_info_builder.add_data_variable(address, full_name, type_uid);
|
||||
}
|
||||
}
|
||||
|
||||
fn do_structure_parse<R: Reader<Offset = usize>>(
|
||||
structure_type: StructureType,
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
debug_info_builder: &mut DebugInfoBuilder,
|
||||
) -> Option<usize> {
|
||||
// All struct, union, and class types will have:
|
||||
// *DW_AT_name
|
||||
// *DW_AT_byte_size or *DW_AT_bit_size
|
||||
// *DW_AT_declaration
|
||||
// *DW_AT_signature
|
||||
// *DW_AT_specification
|
||||
// ?DW_AT_abstract_origin
|
||||
// ?DW_AT_accessibility
|
||||
// ?DW_AT_allocated
|
||||
// ?DW_AT_associated
|
||||
// ?DW_AT_data_location
|
||||
// ?DW_AT_description
|
||||
// ?DW_AT_start_scope
|
||||
// ?DW_AT_visibility
|
||||
// * = Optional
|
||||
|
||||
// Structure/Class/Union _Children_ consist of:
|
||||
// Data members:
|
||||
// DW_AT_type
|
||||
// *DW_AT_name
|
||||
// *DW_AT_accessibility (default private for classes, public for everything else)
|
||||
// *DW_AT_mutable
|
||||
// *DW_AT_data_member_location xor *DW_AT_data_bit_offset (otherwise assume zero) <- there are some deprecations for DWARF 4
|
||||
// *DW_AT_byte_size xor DW_AT_bit_size, iff the storage size is different than it usually would be for the given member type
|
||||
// Function members:
|
||||
// *DW_AT_accessibility (default private for classes, public for everything else)
|
||||
// *DW_AT_virtuality (assume false)
|
||||
// If true: DW_AT_vtable_elem_location
|
||||
// *DW_AT_explicit (assume false)
|
||||
// *DW_AT_object_pointer (assume false; for non-static member function; references the formal parameter that has "DW_AT_artificial = true" and represents "self" or "this" (language specified))
|
||||
// *DW_AT_specification
|
||||
// * = Optional
|
||||
|
||||
if let Ok(Some(_)) = entry.attr(constants::DW_AT_declaration) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let full_name = if get_name(unit, entry, debug_info_builder_context).is_some() {
|
||||
debug_info_builder_context.get_name(unit, entry)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Create structure with proper size
|
||||
let size = get_size_as_u64(entry).unwrap_or(0);
|
||||
let structure_builder: StructureBuilder = StructureBuilder::new();
|
||||
structure_builder
|
||||
.set_packed(true)
|
||||
.set_width(size)
|
||||
.set_structure_type(structure_type);
|
||||
|
||||
// This reference type will be used by any children to grab while we're still building this type
|
||||
// it will also be how any other types refer to this struct
|
||||
if let Some(full_name) = &full_name {
|
||||
debug_info_builder.add_type(
|
||||
get_uid(unit, entry),
|
||||
full_name.clone(),
|
||||
Type::named_type_from_type(
|
||||
full_name.clone(),
|
||||
&Type::structure(&structure_builder.finalize()),
|
||||
),
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
// We _need_ to have initial typedefs or else we can enter infinite parsing loops
|
||||
// These get overwritten in the last step with the actual type, however, so this
|
||||
// is either perfectly fine or breaking a bunch of NTRs
|
||||
let full_name = format!("anonymous_structure_{:x}", get_uid(unit, entry));
|
||||
debug_info_builder.add_type(
|
||||
get_uid(unit, entry),
|
||||
full_name.clone(),
|
||||
Type::named_type_from_type(full_name, &Type::structure(&structure_builder.finalize())),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
// Get all the children and populate
|
||||
let mut tree = unit.entries_tree(Some(entry.offset())).unwrap();
|
||||
let mut children = tree.root().unwrap().children();
|
||||
while let Ok(Some(child)) = children.next() {
|
||||
if child.entry().tag() == constants::DW_TAG_member {
|
||||
if let Some(child_type_id) = get_type(
|
||||
unit,
|
||||
child.entry(),
|
||||
debug_info_builder_context,
|
||||
debug_info_builder,
|
||||
) {
|
||||
if let Some((_, child_type)) = debug_info_builder.get_type(child_type_id) {
|
||||
if let Some(child_name) = debug_info_builder_context
|
||||
.get_name(unit, child.entry())
|
||||
.map_or(
|
||||
if child_type.type_class() == TypeClass::StructureTypeClass {
|
||||
Some("".to_string())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
Some,
|
||||
)
|
||||
{
|
||||
// TODO : support DW_AT_data_bit_offset for offset as well
|
||||
if let Ok(Some(raw_struct_offset)) =
|
||||
child.entry().attr(constants::DW_AT_data_member_location)
|
||||
{
|
||||
// TODO : Let this fail; don't unwrap_or_default get_expr_value
|
||||
let struct_offset =
|
||||
get_attr_as_u64(&raw_struct_offset).unwrap_or_else(|| {
|
||||
get_expr_value(unit, raw_struct_offset).unwrap_or_default()
|
||||
});
|
||||
|
||||
structure_builder.insert(
|
||||
child_type.as_ref(),
|
||||
child_name,
|
||||
struct_offset,
|
||||
false,
|
||||
MemberAccess::NoAccess, // TODO : Resolve actual scopes, if possible
|
||||
MemberScope::NoScope,
|
||||
);
|
||||
} else {
|
||||
structure_builder.append(
|
||||
child_type.as_ref(),
|
||||
child_name,
|
||||
MemberAccess::NoAccess,
|
||||
MemberScope::NoScope,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let finalized_structure = Type::structure(&structure_builder.finalize());
|
||||
if let Some(full_name) = full_name {
|
||||
debug_info_builder.add_type(
|
||||
get_uid(unit, entry) + 1, // TODO : This is super broke (uid + 1 is not guaranteed to be unique)
|
||||
full_name,
|
||||
finalized_structure,
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
debug_info_builder.add_type(
|
||||
get_uid(unit, entry),
|
||||
format!("{}", finalized_structure),
|
||||
finalized_structure,
|
||||
false, // Don't commit anonymous unions (because I think it'll break things)
|
||||
);
|
||||
}
|
||||
Some(get_uid(unit, entry))
|
||||
}
|
||||
|
||||
// This function iterates up through the dependency references, adding all the types along the way until there are no more or stopping at the first one already tracked, then returns the UID of the type of the given DIE
|
||||
pub(crate) fn get_type<R: Reader<Offset = usize>>(
|
||||
unit: &Unit<R>,
|
||||
entry: &DebuggingInformationEntry<R>,
|
||||
debug_info_builder_context: &DebugInfoBuilderContext<R>,
|
||||
debug_info_builder: &mut DebugInfoBuilder,
|
||||
) -> Option<TypeUID> {
|
||||
// If this node (and thus all its referenced nodes) has already been processed, just return the offset
|
||||
if debug_info_builder.contains_type(get_uid(unit, entry)) {
|
||||
return Some(get_uid(unit, entry));
|
||||
}
|
||||
|
||||
// Don't parse types that are just declarations and not definitions
|
||||
if let Ok(Some(_)) = entry.attr(constants::DW_AT_declaration) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let entry_type = if let Some(die_reference) = get_attr_die(
|
||||
unit,
|
||||
entry,
|
||||
debug_info_builder_context,
|
||||
constants::DW_AT_type,
|
||||
) {
|
||||
// This needs to recurse first (before the early return below) to ensure all sub-types have been parsed
|
||||
match die_reference {
|
||||
DieReference::UnitAndOffset((entry_unit, entry_offset)) => get_type(
|
||||
entry_unit,
|
||||
&entry_unit.entry(entry_offset).unwrap(),
|
||||
debug_info_builder_context,
|
||||
debug_info_builder,
|
||||
),
|
||||
DieReference::Err => {
|
||||
warn!("Failed to fetch DIE. Debug information may be incomplete.");
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This needs to recurse first (before the early return below) to ensure all sub-types have been parsed
|
||||
match resolve_specification(unit, entry, debug_info_builder_context) {
|
||||
DieReference::UnitAndOffset((entry_unit, entry_offset))
|
||||
if entry_unit.header.offset() != unit.header.offset()
|
||||
&& entry_offset != entry.offset() =>
|
||||
{
|
||||
get_type(
|
||||
entry_unit,
|
||||
&entry_unit.entry(entry_offset).unwrap(),
|
||||
debug_info_builder_context,
|
||||
debug_info_builder,
|
||||
)
|
||||
}
|
||||
DieReference::UnitAndOffset(_) => None,
|
||||
DieReference::Err => {
|
||||
warn!("Failed to fetch DIE. Debug information may be incomplete.");
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If this node (and thus all its referenced nodes) has already been processed, just return the offset
|
||||
// This check is not redundant because this type might have been processes in the recursive calls above
|
||||
if debug_info_builder.contains_type(get_uid(unit, entry)) {
|
||||
return Some(get_uid(unit, entry));
|
||||
}
|
||||
|
||||
// Collect the required information to create a type and add it to the type map. Also, add the dependencies of this type to the type's typeinfo
|
||||
// Create the type, make a TypeInfo for it, and add it to the debug info
|
||||
let (type_def, mut commit): (Option<Ref<Type>>, bool) = match entry.tag() {
|
||||
constants::DW_TAG_base_type => (
|
||||
handle_base_type(unit, entry, debug_info_builder_context),
|
||||
false,
|
||||
),
|
||||
|
||||
constants::DW_TAG_structure_type => {
|
||||
return do_structure_parse(
|
||||
StructureType::StructStructureType,
|
||||
unit,
|
||||
entry,
|
||||
debug_info_builder_context,
|
||||
debug_info_builder,
|
||||
)
|
||||
}
|
||||
constants::DW_TAG_class_type => {
|
||||
return do_structure_parse(
|
||||
StructureType::ClassStructureType,
|
||||
unit,
|
||||
entry,
|
||||
debug_info_builder_context,
|
||||
debug_info_builder,
|
||||
)
|
||||
}
|
||||
constants::DW_TAG_union_type => {
|
||||
return do_structure_parse(
|
||||
StructureType::UnionStructureType,
|
||||
unit,
|
||||
entry,
|
||||
debug_info_builder_context,
|
||||
debug_info_builder,
|
||||
)
|
||||
}
|
||||
|
||||
// Enum
|
||||
constants::DW_TAG_enumeration_type => {
|
||||
(handle_enum(unit, entry, debug_info_builder_context), true)
|
||||
}
|
||||
|
||||
// Basic types
|
||||
constants::DW_TAG_typedef => {
|
||||
if let Some(name) = debug_info_builder_context.get_name(unit, entry) {
|
||||
handle_typedef(debug_info_builder, entry_type, name)
|
||||
} else {
|
||||
(None, false)
|
||||
}
|
||||
}
|
||||
constants::DW_TAG_pointer_type => (
|
||||
handle_pointer(
|
||||
entry,
|
||||
debug_info_builder_context,
|
||||
debug_info_builder,
|
||||
entry_type,
|
||||
ReferenceType::PointerReferenceType,
|
||||
),
|
||||
false,
|
||||
),
|
||||
constants::DW_TAG_reference_type => (
|
||||
handle_pointer(
|
||||
entry,
|
||||
debug_info_builder_context,
|
||||
debug_info_builder,
|
||||
entry_type,
|
||||
ReferenceType::ReferenceReferenceType,
|
||||
),
|
||||
false,
|
||||
),
|
||||
constants::DW_TAG_rvalue_reference_type => (
|
||||
handle_pointer(
|
||||
entry,
|
||||
debug_info_builder_context,
|
||||
debug_info_builder,
|
||||
entry_type,
|
||||
ReferenceType::RValueReferenceType,
|
||||
),
|
||||
false,
|
||||
),
|
||||
constants::DW_TAG_array_type => (
|
||||
handle_array(unit, entry, debug_info_builder, entry_type),
|
||||
false,
|
||||
),
|
||||
|
||||
// Strange Types
|
||||
constants::DW_TAG_unspecified_type => (Some(Type::void()), false),
|
||||
constants::DW_TAG_subroutine_type => (
|
||||
handle_function(
|
||||
unit,
|
||||
entry,
|
||||
debug_info_builder_context,
|
||||
debug_info_builder,
|
||||
entry_type,
|
||||
),
|
||||
false,
|
||||
),
|
||||
|
||||
// Weird types
|
||||
constants::DW_TAG_const_type => (handle_const(debug_info_builder, entry_type), false),
|
||||
constants::DW_TAG_volatile_type => (handle_volatile(debug_info_builder, entry_type), true), // TODO : Maybe false here
|
||||
|
||||
// Pass-through everything else!
|
||||
_ => return entry_type,
|
||||
};
|
||||
|
||||
// Wrap our resultant type in a TypeInfo so that the internal DebugInfo class can manage it
|
||||
if let Some(type_def) = type_def {
|
||||
let name = if get_name(unit, entry, debug_info_builder_context).is_some() {
|
||||
debug_info_builder_context.get_name(unit, entry)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.unwrap_or_else(|| {
|
||||
commit = false;
|
||||
format!("{}", type_def)
|
||||
});
|
||||
|
||||
debug_info_builder.add_type(get_uid(unit, entry), name, type_def, commit);
|
||||
Some(get_uid(unit, entry))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
13
examples/dwarf/dwarfdump/Cargo.toml
Normal file
13
examples/dwarf/dwarfdump/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "dwarfdump"
|
||||
version = "0.1.0"
|
||||
authors = ["Kyle Martin <kyle@vector35.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
dwarfreader = { path = "../shared/" }
|
||||
binaryninja = {path="../../../"}
|
||||
gimli = "0.27"
|
||||
17
examples/dwarf/dwarfdump/readme.md
Normal file
17
examples/dwarf/dwarfdump/readme.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# DWARF Dump Example
|
||||
|
||||
This is actually a fully-developed plugin, rather than a measly example.
|
||||
|
||||
Two features this does not support are: files in big endian, and .dwo files
|
||||
|
||||
## How to use
|
||||
|
||||
Simply `cargo build --release` in this directory, and copy the `.so` from the target directory to your plugin directory
|
||||
|
||||
### Attribution
|
||||
|
||||
This example makes use of:
|
||||
- [gimli] ([gimli license] - MIT)
|
||||
|
||||
[gimli license]: https://github.com/gimli-rs/gimli/blob/master/LICENSE-MIT
|
||||
[gimli]: https://github.com/gimli-rs/gimli
|
||||
308
examples/dwarf/dwarfdump/src/lib.rs
Normal file
308
examples/dwarf/dwarfdump/src/lib.rs
Normal file
@@ -0,0 +1,308 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use binaryninja::{
|
||||
binaryview::{BinaryView, BinaryViewExt},
|
||||
command::{register, Command},
|
||||
disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenContents},
|
||||
flowgraph::{BranchType, EdgeStyle, FlowGraph, FlowGraphNode, FlowGraphOption},
|
||||
string::BnString,
|
||||
};
|
||||
use dwarfreader::is_valid;
|
||||
|
||||
use gimli::{
|
||||
AttributeValue::{Encoding, Flag, UnitRef},
|
||||
// BigEndian,
|
||||
DebuggingInformationEntry,
|
||||
Dwarf,
|
||||
EntriesTreeNode,
|
||||
Reader,
|
||||
ReaderOffset,
|
||||
SectionId,
|
||||
Unit,
|
||||
UnitSectionOffset,
|
||||
};
|
||||
|
||||
static PADDING: [&'static str; 23] = [
|
||||
"",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
];
|
||||
|
||||
// TODO : This is very much not comprehensive: see https://github.com/gimli-rs/gimli/blob/master/examples/dwarfdump.rs
|
||||
fn get_info_string<R: Reader>(
|
||||
view: &BinaryView,
|
||||
dwarf: &Dwarf<R>,
|
||||
unit: &Unit<R>,
|
||||
die_node: &DebuggingInformationEntry<R>,
|
||||
) -> Vec<DisassemblyTextLine> {
|
||||
let mut disassembly_lines: Vec<DisassemblyTextLine> = Vec::with_capacity(10); // This is an estimate so "most" things won't need to resize
|
||||
|
||||
let label_value = match die_node.offset().to_unit_section_offset(unit) {
|
||||
UnitSectionOffset::DebugInfoOffset(o) => o.0,
|
||||
UnitSectionOffset::DebugTypesOffset(o) => o.0,
|
||||
}
|
||||
.into_u64();
|
||||
let label_string = format!("#0x{:08x}", label_value);
|
||||
disassembly_lines.push(DisassemblyTextLine::from(vec![
|
||||
InstructionTextToken::new(
|
||||
BnString::new(label_string),
|
||||
InstructionTextTokenContents::GotoLabel(label_value),
|
||||
),
|
||||
InstructionTextToken::new(BnString::new(":"), InstructionTextTokenContents::Text),
|
||||
]));
|
||||
|
||||
disassembly_lines.push(DisassemblyTextLine::from(vec![InstructionTextToken::new(
|
||||
BnString::new(die_node.tag().static_string().unwrap()),
|
||||
InstructionTextTokenContents::TypeName, // TODO : KeywordToken?
|
||||
)]));
|
||||
|
||||
let mut attrs = die_node.attrs();
|
||||
while let Some(attr) = attrs.next().unwrap() {
|
||||
let mut attr_line: Vec<InstructionTextToken> = Vec::with_capacity(5);
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new(" "),
|
||||
InstructionTextTokenContents::Indentation,
|
||||
));
|
||||
|
||||
let len;
|
||||
if let Some(n) = attr.name().static_string() {
|
||||
len = n.len();
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new(n),
|
||||
InstructionTextTokenContents::FieldName,
|
||||
));
|
||||
} else {
|
||||
// This is rather unlikely, I think
|
||||
len = 1;
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new("?"),
|
||||
InstructionTextTokenContents::FieldName,
|
||||
));
|
||||
}
|
||||
|
||||
// On command line the magic number that looks good is 22, but that's too much whitespace in a basic block, so I chose 18 (22 is the max with the current padding provided)
|
||||
if len < 18 {
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new(PADDING[18 - len]),
|
||||
InstructionTextTokenContents::Text,
|
||||
));
|
||||
}
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new(" = "),
|
||||
InstructionTextTokenContents::Text,
|
||||
));
|
||||
|
||||
if let Ok(Some(addr)) = dwarf.attr_address(unit, attr.value()) {
|
||||
let addr_string = format!("0x{:08x}", addr);
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new(addr_string),
|
||||
InstructionTextTokenContents::Integer(addr),
|
||||
));
|
||||
} else if let Ok(attr_reader) = dwarf.attr_string(unit, attr.value()) {
|
||||
if let Ok(attr_string) = attr_reader.to_string() {
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new(attr_string.as_ref()),
|
||||
InstructionTextTokenContents::String({
|
||||
let (_, id, offset) =
|
||||
dwarf.lookup_offset_id(attr_reader.offset_id()).unwrap();
|
||||
offset.into_u64() + view.section_by_name(id.name()).unwrap().start()
|
||||
}),
|
||||
));
|
||||
} else {
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new("??"),
|
||||
InstructionTextTokenContents::Text,
|
||||
));
|
||||
}
|
||||
} else if let Encoding(type_class) = attr.value() {
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new(type_class.static_string().unwrap()),
|
||||
InstructionTextTokenContents::TypeName,
|
||||
));
|
||||
} else if let UnitRef(offset) = attr.value() {
|
||||
let addr = match offset.to_unit_section_offset(unit) {
|
||||
UnitSectionOffset::DebugInfoOffset(o) => o.0,
|
||||
UnitSectionOffset::DebugTypesOffset(o) => o.0,
|
||||
}
|
||||
.into_u64();
|
||||
let addr_string = format!("#0x{:08x}", addr);
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new(addr_string),
|
||||
InstructionTextTokenContents::GotoLabel(addr),
|
||||
));
|
||||
} else if let Flag(true) = attr.value() {
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new("true"),
|
||||
InstructionTextTokenContents::Integer(1),
|
||||
));
|
||||
} else if let Flag(false) = attr.value() {
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new("false"),
|
||||
InstructionTextTokenContents::Integer(1),
|
||||
));
|
||||
|
||||
// Fall-back cases
|
||||
} else if let Some(value) = attr.u8_value() {
|
||||
let value_string = format!("{}", value);
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new(value_string),
|
||||
InstructionTextTokenContents::Integer(value.into()),
|
||||
));
|
||||
} else if let Some(value) = attr.u16_value() {
|
||||
let value_string = format!("{}", value);
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new(value_string),
|
||||
InstructionTextTokenContents::Integer(value.into()),
|
||||
));
|
||||
} else if let Some(value) = attr.udata_value() {
|
||||
let value_string = format!("{}", value);
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new(value_string),
|
||||
InstructionTextTokenContents::Integer(value.into()),
|
||||
));
|
||||
} else if let Some(value) = attr.sdata_value() {
|
||||
let value_string = format!("{}", value);
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new(value_string),
|
||||
InstructionTextTokenContents::Integer(value as u64),
|
||||
));
|
||||
} else {
|
||||
let attr_string = format!("{:?}", attr.value());
|
||||
attr_line.push(InstructionTextToken::new(
|
||||
BnString::new(attr_string),
|
||||
InstructionTextTokenContents::Text,
|
||||
));
|
||||
}
|
||||
disassembly_lines.push(DisassemblyTextLine::from(attr_line));
|
||||
}
|
||||
|
||||
disassembly_lines
|
||||
}
|
||||
|
||||
fn process_tree<R: Reader>(
|
||||
view: &BinaryView,
|
||||
dwarf: &Dwarf<R>,
|
||||
unit: &Unit<R>,
|
||||
graph: &FlowGraph,
|
||||
graph_parent: &FlowGraphNode,
|
||||
die_node: EntriesTreeNode<R>,
|
||||
) {
|
||||
// Namespaces only - really interesting to look at!
|
||||
// if (die_node.entry().tag() == constants::DW_TAG_namespace)
|
||||
// || (die_node.entry().tag() == constants::DW_TAG_class_type)
|
||||
// || (die_node.entry().tag() == constants::DW_TAG_compile_unit)
|
||||
// || (die_node.entry().tag() == constants::DW_TAG_subprogram)
|
||||
// {
|
||||
let new_node = FlowGraphNode::new(graph);
|
||||
|
||||
let attr_string = get_info_string(view, dwarf, unit, die_node.entry());
|
||||
new_node.set_disassembly_lines(&attr_string);
|
||||
|
||||
graph.append(&new_node);
|
||||
graph_parent.add_outgoing_edge(
|
||||
BranchType::UnconditionalBranch,
|
||||
&new_node,
|
||||
&EdgeStyle::default(),
|
||||
);
|
||||
|
||||
let mut children = die_node.children();
|
||||
while let Some(child) = children.next().unwrap() {
|
||||
process_tree(view, dwarf, unit, graph, &new_node, child);
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
fn dump_dwarf(bv: &BinaryView) {
|
||||
let view = if bv.section_by_name(".debug_info").is_ok() {
|
||||
bv.to_owned()
|
||||
} else {
|
||||
bv.parent_view().unwrap()
|
||||
};
|
||||
|
||||
let graph = FlowGraph::new();
|
||||
graph.set_option(FlowGraphOption::FlowGraphUsesBlockHighlights, true);
|
||||
graph.set_option(FlowGraphOption::FlowGraphUsesInstructionHighlights, true);
|
||||
|
||||
let graph_root = FlowGraphNode::new(&graph);
|
||||
graph_root.set_lines(vec!["Graph Root"]);
|
||||
graph.append(&graph_root);
|
||||
|
||||
let endian = dwarfreader::get_endian(bv);
|
||||
let section_reader = |section_id: SectionId| -> _ {
|
||||
dwarfreader::create_section_reader(section_id, bv, endian, false)
|
||||
};
|
||||
let dwarf = Dwarf::load(§ion_reader).unwrap();
|
||||
|
||||
let mut iter = dwarf.units();
|
||||
while let Some(header) = iter.next().unwrap() {
|
||||
let unit = dwarf.unit(header).unwrap();
|
||||
let mut entries = unit.entries();
|
||||
let mut depth = 0;
|
||||
|
||||
if let Some((delta_depth, entry)) = entries.next_dfs().unwrap() {
|
||||
depth += delta_depth;
|
||||
assert!(depth >= 0);
|
||||
|
||||
let mut tree = unit.entries_tree(Some(entry.offset())).unwrap();
|
||||
let root = tree.root().unwrap();
|
||||
|
||||
process_tree(&view, &dwarf, &unit, &graph, &graph_root, root);
|
||||
}
|
||||
}
|
||||
|
||||
view.show_graph_report("DWARF", graph);
|
||||
}
|
||||
|
||||
struct DWARFDump;
|
||||
|
||||
impl Command for DWARFDump {
|
||||
fn action(&self, view: &BinaryView) {
|
||||
dump_dwarf(view);
|
||||
}
|
||||
|
||||
fn valid(&self, view: &BinaryView) -> bool {
|
||||
is_valid(view)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn UIPluginInit() -> bool {
|
||||
register(
|
||||
"DWARF Dump",
|
||||
"Show embedded DWARF info as a tree structure for you to navigate",
|
||||
DWARFDump {},
|
||||
);
|
||||
true
|
||||
}
|
||||
9
examples/dwarf/shared/Cargo.toml
Normal file
9
examples/dwarf/shared/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "dwarfreader"
|
||||
version = "0.1.0"
|
||||
authors = ["Kyle Martin <kyle@vector35.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
binaryninja = {path="../../../"}
|
||||
gimli = "0.27"
|
||||
159
examples/dwarf/shared/src/lib.rs
Normal file
159
examples/dwarf/shared/src/lib.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use gimli::{EndianRcSlice, Endianity, Error, RunTimeEndian, SectionId};
|
||||
|
||||
use binaryninja::binaryninjacore_sys::*;
|
||||
|
||||
use binaryninja::{
|
||||
binaryview::{BinaryView, BinaryViewBase, BinaryViewExt},
|
||||
databuffer::DataBuffer,
|
||||
Endianness,
|
||||
};
|
||||
|
||||
use std::{ffi::CString, rc::Rc};
|
||||
|
||||
//////////////////////
|
||||
// Dwarf Validation
|
||||
|
||||
pub fn is_non_dwo_dwarf(view: &BinaryView) -> bool {
|
||||
view.section_by_name(".debug_info").is_ok() || view.section_by_name("__debug_info").is_ok()
|
||||
}
|
||||
|
||||
pub fn is_dwo_dwarf(view: &BinaryView) -> bool {
|
||||
view.section_by_name(".debug_info.dwo").is_ok()
|
||||
}
|
||||
|
||||
pub fn is_raw_non_dwo_dwarf(view: &BinaryView) -> bool {
|
||||
if let Ok(raw_view) = view.raw_view() {
|
||||
raw_view.section_by_name(".debug_info").is_ok()
|
||||
|| view.section_by_name("__debug_info").is_ok()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_raw_dwo_dwarf(view: &BinaryView) -> bool {
|
||||
if let Ok(raw_view) = view.raw_view() {
|
||||
raw_view.section_by_name(".debug_info.dwo").is_ok()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_valid(view: &BinaryView) -> bool {
|
||||
is_non_dwo_dwarf(view)
|
||||
|| is_raw_non_dwo_dwarf(view)
|
||||
|| is_dwo_dwarf(view)
|
||||
|| is_raw_dwo_dwarf(view)
|
||||
}
|
||||
|
||||
pub fn get_endian(view: &BinaryView) -> RunTimeEndian {
|
||||
match view.default_endianness() {
|
||||
Endianness::LittleEndian => RunTimeEndian::Little,
|
||||
Endianness::BigEndian => RunTimeEndian::Big,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_section_reader<'a, Endian: 'a + Endianity>(
|
||||
section_id: SectionId,
|
||||
view: &'a BinaryView,
|
||||
endian: Endian,
|
||||
dwo_file: bool,
|
||||
) -> Result<EndianRcSlice<Endian>, Error> {
|
||||
let section_name = if dwo_file && section_id.dwo_name().is_some() {
|
||||
section_id.dwo_name().unwrap()
|
||||
} else {
|
||||
section_id.name()
|
||||
};
|
||||
|
||||
if let Ok(section) = view.section_by_name(section_name) {
|
||||
// TODO : This is kinda broke....should add rust wrappers for some of this
|
||||
if let Some(symbol) = view
|
||||
.symbols()
|
||||
.iter()
|
||||
.find(|symbol| symbol.full_name().as_str() == "__elf_section_headers")
|
||||
{
|
||||
if let Some(data_var) = view
|
||||
.data_variables()
|
||||
.iter()
|
||||
.find(|var| var.address == symbol.address())
|
||||
{
|
||||
// TODO : This should eventually be wrapped by some DataView sorta thingy thing, like how python does it
|
||||
let data_type = data_var.type_with_confidence().contents;
|
||||
let data = view.read_vec(data_var.address, data_type.width() as usize);
|
||||
let element_type = data_type.element_type().unwrap().contents;
|
||||
|
||||
if let Some(current_section_header) = data
|
||||
.chunks(element_type.width() as usize)
|
||||
.find(|section_header| {
|
||||
endian.read_u64(§ion_header[24..32]) == section.start()
|
||||
})
|
||||
{
|
||||
if (endian.read_u64(¤t_section_header[8..16]) & 2048) != 0 {
|
||||
// Get section, trim header, decompress, return
|
||||
let offset = section.start() + 24;
|
||||
let len = section.len() - 24;
|
||||
|
||||
if let Ok(buffer) = view.read_buffer(offset, len) {
|
||||
use std::ptr;
|
||||
let transform_name =
|
||||
CString::new("Zlib").unwrap().into_bytes_with_nul();
|
||||
let transform =
|
||||
unsafe { BNGetTransformByName(transform_name.as_ptr() as *mut _) };
|
||||
|
||||
// Omega broke
|
||||
let raw_buf: *mut BNDataBuffer =
|
||||
unsafe { BNCreateDataBuffer(ptr::null_mut(), 0) };
|
||||
if unsafe {
|
||||
BNDecode(
|
||||
transform,
|
||||
std::mem::transmute(buffer),
|
||||
raw_buf,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
)
|
||||
} {
|
||||
let output_buffer: DataBuffer =
|
||||
unsafe { std::mem::transmute(raw_buf) };
|
||||
|
||||
return Ok(EndianRcSlice::new(
|
||||
output_buffer.get_data().into(),
|
||||
endian,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let offset = section.start();
|
||||
let len = section.len();
|
||||
if len == 0 {
|
||||
Ok(EndianRcSlice::new(Rc::from([]), endian))
|
||||
} else {
|
||||
Ok(EndianRcSlice::new(
|
||||
Rc::from(view.read_vec(offset, len).as_slice()),
|
||||
endian,
|
||||
))
|
||||
}
|
||||
} else if let Ok(section) = view.section_by_name("__".to_string() + §ion_name[1..]) {
|
||||
Ok(EndianRcSlice::new(
|
||||
Rc::from(view.read_vec(section.start(), section.len()).as_slice()),
|
||||
endian,
|
||||
))
|
||||
} else {
|
||||
Ok(EndianRcSlice::new(Rc::from([]), endian))
|
||||
}
|
||||
}
|
||||
353
examples/flowgraph/Cargo.lock
generated
Normal file
353
examples/flowgraph/Cargo.lock
generated
Normal file
@@ -0,0 +1,353 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binaryninja"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"binaryninjacore-sys",
|
||||
"libc",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binaryninjacore-sys"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.58.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f8523b410d7187a43085e7e064416ea32ded16bd0a4e6fc025e21616d01258f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "853eda514c284c2287f4bf20ae614f8781f40a81d32ecda6e91449304dfe077c"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flowgraph"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"binaryninja",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.112"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42a568c8f2cd051a4d283bd6eb0343ac214c1b0f1ac19f93e1175b2dee38c73d"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
11
examples/flowgraph/Cargo.toml
Normal file
11
examples/flowgraph/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "flowgraph"
|
||||
version = "0.1.0"
|
||||
authors = ["Kyle Martin <kyle@vector35.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
binaryninja = {path="../../"}
|
||||
51
examples/flowgraph/src/lib.rs
Normal file
51
examples/flowgraph/src/lib.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use binaryninja::{
|
||||
binaryview::{BinaryView, BinaryViewExt},
|
||||
command::register,
|
||||
disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenContents},
|
||||
flowgraph::{BranchType, EdgePenStyle, EdgeStyle, FlowGraph, FlowGraphNode, ThemeColor},
|
||||
string::BnString,
|
||||
};
|
||||
|
||||
fn test_graph(view: &BinaryView) {
|
||||
let graph = FlowGraph::new();
|
||||
|
||||
let disassembly_lines_a = vec![DisassemblyTextLine::from(vec![
|
||||
InstructionTextToken::new(BnString::new("Li"), InstructionTextTokenContents::Text),
|
||||
InstructionTextToken::new(BnString::new("ne"), InstructionTextTokenContents::Text),
|
||||
InstructionTextToken::new(BnString::new(" 1"), InstructionTextTokenContents::Text),
|
||||
])];
|
||||
|
||||
let node_a = FlowGraphNode::new(&graph);
|
||||
node_a.set_disassembly_lines(&disassembly_lines_a);
|
||||
|
||||
let node_b = FlowGraphNode::new(&graph);
|
||||
let disassembly_lines_b = vec![DisassemblyTextLine::from(&vec!["Li", "ne", " 2"])];
|
||||
node_b.set_disassembly_lines(&disassembly_lines_b);
|
||||
|
||||
let node_c = FlowGraphNode::new(&graph);
|
||||
node_c.set_lines(vec!["Line 3", "Line 4", "Line 5"]);
|
||||
|
||||
graph.append(&node_a);
|
||||
graph.append(&node_b);
|
||||
graph.append(&node_c);
|
||||
|
||||
let edge = EdgeStyle::new(EdgePenStyle::DashDotDotLine, 2, ThemeColor::AddressColor);
|
||||
node_a.add_outgoing_edge(BranchType::UserDefinedBranch, &node_b, &edge);
|
||||
node_a.add_outgoing_edge(
|
||||
BranchType::UnconditionalBranch,
|
||||
&node_c,
|
||||
&EdgeStyle::default(),
|
||||
);
|
||||
|
||||
view.show_graph_report("Rust Graph Title", graph);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn UIPluginInit() -> bool {
|
||||
register(
|
||||
"Rust Graph Test Title",
|
||||
"Rust Graph Test Description",
|
||||
test_graph,
|
||||
);
|
||||
true
|
||||
}
|
||||
2
examples/minidump/.gitignore
vendored
Normal file
2
examples/minidump/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
/Cargo.lock
|
||||
13
examples/minidump/Cargo.toml
Normal file
13
examples/minidump/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "minidump_bn"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
binaryninja = {path="../../"}
|
||||
log = "0.4.17"
|
||||
minidump = "0.15.2"
|
||||
time = "=0.3.16" # Remove this when we update from stable-2022-12-15 - it's pinning a dependency that requires a more recent version of rustc
|
||||
65
examples/minidump/README.md
Normal file
65
examples/minidump/README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Binary Ninja Minidump Loader
|
||||
|
||||
A Minidump memory dump loader plugin for Binary Ninja.
|
||||
|
||||

|
||||
|
||||
This plugin adds a new _Minidump_ binary view type. When a binary with the magic number `MDMP` is opened, this plugin will automatically try to load in the binary as a minidump, and create a new _Minidump_ binary view to view the contents.
|
||||
|
||||
The architecture is determined automatically from the platform information embedded in the minidump.
|
||||
|
||||

|
||||
|
||||
The loaded minidump's memory regions and modules can be navigated via the _Memory Map_ window. In the _Minidump_ binary view, the meanings of "Segments" and "Sections" in the Memory Map window are modified to mean the following:
|
||||
|
||||
- The memory regions in the minidump are loaded as _Segments_. The _Data Offset_ and _Data Length_ fields of each segment are the corresponding addresses in the minidump file where the data for that memory region is located.
|
||||
- The modules in the minidump are loaded as _Sections_, with the name of each section being the path to the module.
|
||||
|
||||

|
||||
|
||||
## Supported Minidump Types
|
||||
|
||||
This plugin currently only supports loading minidump files generated by the Windows [`MiniDumpWriteDump` API](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/nf-minidumpapiset-minidumpwritedump).
|
||||
|
||||
This includes dumps generated from:
|
||||
|
||||
- The [`.dump` command](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/-dump--create-dump-file-) in WinDbg.
|
||||
- The `.dump` command in Binary Ninja's debugger for Windows targets (which uses the same debugging engine as WinDbg).
|
||||
|
||||
For both of the above, it's recommended to generate a full dump:
|
||||
|
||||
```
|
||||
.dump /ma dumpfile.dmp
|
||||
```
|
||||
|
||||
- The [`minidump` command](https://help.x64dbg.com/en/latest/commands/memory-operations/minidump.html) in x64dbg.
|
||||
|
||||
```
|
||||
minidump dumpfile.dmp
|
||||
```
|
||||
|
||||
- Right clicking on a listed process and then clicking "Create dump file" / "Create full dump" from Windows Task Manager, Process Hacker, Sysinternals Process Explorer, etc...
|
||||
|
||||
## Unsupported Features (for now)
|
||||
|
||||
- Loading Minidump files from platforms or APIs other than Windows' `MinidumpWriteDump`, such as those generated by [Google Breakpad](https://chromium.googlesource.com/breakpad/breakpad/).
|
||||
- Loading and applyng debug information from the minidump file. In Windows minidump files, `MinidumpModuleList` streams contain information about the PDB file which contains the debug information for the module; this isn't currently read or applied, however.
|
||||
- Integration with Binary Ninja's built-in debugger. Minidump files can contain information about threads, register values, and stack frames, and it would be nice in the future for minidump files to be loadable back into the debugger in order to resume a debugging session. This isn't currently done, however.
|
||||
|
||||
## Building and Installing
|
||||
|
||||
This plugin currently needs to be built from source, then copied into your user plugin folder.
|
||||
|
||||
```
|
||||
cargo build --release
|
||||
cp target/release/libminidump_bn.so ~/.binaryninja/plugins/
|
||||
```
|
||||
|
||||
The code in this plugin targets the `dev` branch of the [Binary Ninja Rust API](https://github.com/Vector35/binaryninja-api/tree/dev/rust).
|
||||
|
||||
To update the Binary Ninja Rust API dependency:
|
||||
|
||||
```
|
||||
cargo update -p binaryninja
|
||||
cargo build --release
|
||||
```
|
||||
68
examples/minidump/build.rs
Normal file
68
examples/minidump/build.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
BIN
examples/minidump/images/loaded-minidump-screenshot-border.png
Normal file
BIN
examples/minidump/images/loaded-minidump-screenshot-border.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 260 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 119 KiB |
44
examples/minidump/src/command.rs
Normal file
44
examples/minidump/src/command.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use std::str;
|
||||
|
||||
use log::{debug, error, info};
|
||||
use minidump::{Minidump, MinidumpMemoryInfoList};
|
||||
|
||||
use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
|
||||
|
||||
use crate::view::DataBufferWrapper;
|
||||
|
||||
pub fn print_memory_information(bv: &BinaryView) {
|
||||
debug!("Printing memory information");
|
||||
if let Ok(minidump_bv) = bv.parent_view() {
|
||||
if let Ok(read_buffer) = minidump_bv.read_buffer(0, minidump_bv.len()) {
|
||||
let read_buffer = DataBufferWrapper::new(read_buffer);
|
||||
if let Ok(minidump_obj) = Minidump::read(read_buffer) {
|
||||
if let Ok(memory_info_list) = minidump_obj.get_stream::<MinidumpMemoryInfoList>() {
|
||||
let mut memory_info_list_writer = Vec::new();
|
||||
match memory_info_list.print(&mut memory_info_list_writer) {
|
||||
Ok(_) => {
|
||||
if let Ok(memory_info_str) = str::from_utf8(&memory_info_list_writer) {
|
||||
info!("{memory_info_str}");
|
||||
} else {
|
||||
error!("Could not convert the memory information description from minidump into a valid string");
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
error!("Could not get memory information from minidump");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
"Could not parse a valid MinidumpMemoryInfoList stream from the minidump"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
error!("Could not parse a valid minidump file from the parent binary view's data buffer");
|
||||
}
|
||||
} else {
|
||||
error!("Could not read data from parent binary view");
|
||||
}
|
||||
} else {
|
||||
error!("Could not get the parent binary view");
|
||||
}
|
||||
}
|
||||
37
examples/minidump/src/lib.rs
Normal file
37
examples/minidump/src/lib.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use binaryninja::binaryview::BinaryView;
|
||||
use binaryninja::command::{register, Command};
|
||||
use binaryninja::custombinaryview::register_view_type;
|
||||
use log::{debug, LevelFilter};
|
||||
|
||||
mod command;
|
||||
mod view;
|
||||
|
||||
struct PrintMemoryInformationCommand;
|
||||
|
||||
impl Command for PrintMemoryInformationCommand {
|
||||
fn action(&self, binary_view: &BinaryView) {
|
||||
command::print_memory_information(binary_view);
|
||||
}
|
||||
|
||||
fn valid(&self, _binary_view: &BinaryView) -> bool {
|
||||
true // TODO: Of course, the command will not always be valid!
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
pub extern "C" fn CorePluginInit() -> bool {
|
||||
binaryninja::logger::init(LevelFilter::Trace).expect("failed to initialize logging");
|
||||
|
||||
debug!("Registering minidump binary view type");
|
||||
register_view_type("Minidump", "Minidump", view::MinidumpBinaryViewType::new);
|
||||
|
||||
debug!("Registering minidump plugin commands");
|
||||
register(
|
||||
"Minidump\\[DEBUG] Print Minidump Memory Information",
|
||||
"Print a human-readable description of the contents of the MinidumpMemoryInfoList stream in the loaded minidump",
|
||||
PrintMemoryInformationCommand {},
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
426
examples/minidump/src/view.rs
Normal file
426
examples/minidump/src/view.rs
Normal file
@@ -0,0 +1,426 @@
|
||||
use std::collections::HashMap;
|
||||
use std::ops::{Deref, Range};
|
||||
use std::sync::Arc;
|
||||
|
||||
use binaryninja::section::Section;
|
||||
use binaryninja::segment::Segment;
|
||||
use log::{debug, error, info, warn};
|
||||
use minidump::format::MemoryProtection;
|
||||
use minidump::{
|
||||
Minidump, MinidumpMemory64List, MinidumpMemoryInfoList, MinidumpMemoryList, MinidumpModuleList,
|
||||
MinidumpStream, MinidumpSystemInfo, Module,
|
||||
};
|
||||
|
||||
use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
|
||||
use binaryninja::custombinaryview::{
|
||||
BinaryViewType, BinaryViewTypeBase, CustomBinaryView, CustomBinaryViewType, CustomView,
|
||||
CustomViewBuilder,
|
||||
};
|
||||
use binaryninja::databuffer::DataBuffer;
|
||||
use binaryninja::platform::Platform;
|
||||
use binaryninja::Endianness;
|
||||
|
||||
type BinaryViewResult<R> = binaryninja::binaryview::Result<R>;
|
||||
|
||||
/// A wrapper around a `binaryninja::databuffer::DataBuffer`, from which a `[u8]` buffer can be obtained
|
||||
/// to pass to `minidump::Minidump::read`.
|
||||
///
|
||||
/// This code is taken from [`dwarfdump`](https://github.com/Vector35/binaryninja-api/blob/9d8bc846bd213407fb1a7a19af2a96f17501ac3b/rust/examples/dwarfdump/src/lib.rs#L81)
|
||||
/// in the Rust API examples.
|
||||
#[derive(Clone)]
|
||||
pub struct DataBufferWrapper {
|
||||
inner: Arc<DataBuffer>,
|
||||
}
|
||||
|
||||
impl DataBufferWrapper {
|
||||
pub fn new(buf: DataBuffer) -> Self {
|
||||
DataBufferWrapper {
|
||||
inner: Arc::new(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for DataBufferWrapper {
|
||||
type Target = [u8];
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner.get_data()
|
||||
}
|
||||
}
|
||||
|
||||
/// The _Minidump_ binary view type, which the Rust plugin registers with the Binary Ninja core
|
||||
/// (via `binaryninja::custombinaryview::register_view_type`) as a possible binary view
|
||||
/// that can be applied to opened binaries.
|
||||
///
|
||||
/// If this view type is valid for an opened binary (determined by `is_valid_for`),
|
||||
/// the Binary Ninja core then uses this view type to create an actual instance of the _Minidump_
|
||||
/// binary view (via `create_custom_view`).
|
||||
pub struct MinidumpBinaryViewType {
|
||||
view_type: BinaryViewType,
|
||||
}
|
||||
|
||||
impl MinidumpBinaryViewType {
|
||||
pub fn new(view_type: BinaryViewType) -> Self {
|
||||
MinidumpBinaryViewType { view_type }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<BinaryViewType> for MinidumpBinaryViewType {
|
||||
fn as_ref(&self) -> &BinaryViewType {
|
||||
&self.view_type
|
||||
}
|
||||
}
|
||||
|
||||
impl BinaryViewTypeBase for MinidumpBinaryViewType {
|
||||
fn is_deprecated(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn is_valid_for(&self, data: &BinaryView) -> bool {
|
||||
let mut magic_number = Vec::<u8>::new();
|
||||
data.read_into_vec(&mut magic_number, 0, 4);
|
||||
|
||||
magic_number == b"MDMP"
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomBinaryViewType for MinidumpBinaryViewType {
|
||||
fn create_custom_view<'builder>(
|
||||
&self,
|
||||
data: &BinaryView,
|
||||
builder: CustomViewBuilder<'builder, Self>,
|
||||
) -> BinaryViewResult<CustomView<'builder>> {
|
||||
debug!("Creating MinidumpBinaryView from registered MinidumpBinaryViewType");
|
||||
|
||||
let binary_view = builder.create::<MinidumpBinaryView>(data, ());
|
||||
binary_view
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SegmentData {
|
||||
rva_range: Range<u64>,
|
||||
mapped_addr_range: Range<u64>,
|
||||
}
|
||||
|
||||
impl SegmentData {
|
||||
fn from_addresses_and_size(rva: u64, mapped_addr: u64, size: u64) -> Self {
|
||||
SegmentData {
|
||||
rva_range: Range {
|
||||
start: rva,
|
||||
end: rva + size,
|
||||
},
|
||||
mapped_addr_range: Range {
|
||||
start: mapped_addr,
|
||||
end: mapped_addr + size,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SegmentMemoryProtection {
|
||||
readable: bool,
|
||||
writable: bool,
|
||||
executable: bool,
|
||||
}
|
||||
|
||||
/// An instance of the actual _Minidump_ custom binary view.
|
||||
/// This contains the main logic to load the memory segments inside a minidump file into the binary view.
|
||||
pub struct MinidumpBinaryView {
|
||||
/// The handle to the "real" BinaryView object, in the Binary Ninja core.
|
||||
inner: binaryninja::rc::Ref<BinaryView>,
|
||||
}
|
||||
|
||||
impl MinidumpBinaryView {
|
||||
fn new(view: &BinaryView) -> Self {
|
||||
MinidumpBinaryView {
|
||||
inner: view.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn init(&self) -> BinaryViewResult<()> {
|
||||
let parent_view = self.parent_view()?;
|
||||
let read_buffer = parent_view.read_buffer(0, parent_view.len())?;
|
||||
let read_buffer = DataBufferWrapper::new(read_buffer);
|
||||
|
||||
if let Ok(minidump_obj) = Minidump::read(read_buffer) {
|
||||
// Architecture, platform information
|
||||
if let Ok(minidump_system_info) = minidump_obj.get_stream::<MinidumpSystemInfo>() {
|
||||
if let Some(platform) = MinidumpBinaryView::translate_minidump_platform(
|
||||
minidump_system_info.cpu,
|
||||
minidump_obj.endian,
|
||||
minidump_system_info.os,
|
||||
) {
|
||||
self.set_default_platform(&platform);
|
||||
} else {
|
||||
error!(
|
||||
"Could not parse valid system information from minidump: could not map system information in MinidumpSystemInfo stream (arch {:?}, endian {:?}, os {:?}) to a known architecture",
|
||||
minidump_system_info.cpu,
|
||||
minidump_obj.endian,
|
||||
minidump_system_info.os,
|
||||
);
|
||||
return Err(());
|
||||
}
|
||||
} else {
|
||||
error!("Could not parse system information from minidump: could not find a valid MinidumpSystemInfo stream");
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Memory segments
|
||||
let mut segment_data = Vec::<SegmentData>::new();
|
||||
|
||||
// Memory segments in a full memory dump (MinidumpMemory64List)
|
||||
// Grab the shared base RVA for all entries in the MinidumpMemory64List,
|
||||
// since the minidump crate doesn't expose this to us
|
||||
if let Ok(raw_stream) = minidump_obj.get_raw_stream(MinidumpMemory64List::STREAM_TYPE) {
|
||||
if let Ok(base_rva_array) = raw_stream[8..16].try_into() {
|
||||
let base_rva = u64::from_le_bytes(base_rva_array);
|
||||
debug!("Found BaseRVA value {:#x}", base_rva);
|
||||
|
||||
if let Ok(minidump_memory_list) =
|
||||
minidump_obj.get_stream::<MinidumpMemory64List>()
|
||||
{
|
||||
let mut current_rva = base_rva;
|
||||
for memory_segment in minidump_memory_list.iter() {
|
||||
debug!(
|
||||
"Found memory segment at RVA {:#x} with virtual address {:#x} and size {:#x}",
|
||||
current_rva,
|
||||
memory_segment.base_address,
|
||||
memory_segment.size,
|
||||
);
|
||||
segment_data.push(SegmentData::from_addresses_and_size(
|
||||
current_rva,
|
||||
memory_segment.base_address,
|
||||
memory_segment.size,
|
||||
));
|
||||
current_rva += memory_segment.size;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("Could not parse BaseRVA value shared by all entries in the MinidumpMemory64List stream")
|
||||
}
|
||||
} else {
|
||||
warn!("Could not read memory from minidump: could not find a valid MinidumpMemory64List stream. This minidump may not be a full memory dump. Trying to find partial dump memory from a MinidumpMemoryList now...");
|
||||
// Memory segments in a regular memory dump (MinidumpMemoryList),
|
||||
// i.e. one that does not include the full process memory data.
|
||||
if let Ok(minidump_memory_list) = minidump_obj.get_stream::<MinidumpMemoryList>() {
|
||||
for memory_segment in minidump_memory_list.by_addr() {
|
||||
debug!(
|
||||
"Found memory segment at RVA {:#x} with virtual address {:#x} and size {:#x}",
|
||||
memory_segment.desc.memory.rva,
|
||||
memory_segment.base_address,
|
||||
memory_segment.size
|
||||
);
|
||||
segment_data.push(SegmentData::from_addresses_and_size(
|
||||
memory_segment.desc.memory.rva as u64,
|
||||
memory_segment.base_address,
|
||||
memory_segment.size,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
error!("Could not read any memory from minidump: could not find a valid MinidumpMemory64List stream or a valid MinidumpMemoryList stream.");
|
||||
}
|
||||
}
|
||||
|
||||
// Memory protection information
|
||||
let mut segment_protection_data = HashMap::new();
|
||||
|
||||
if let Ok(minidump_memory_info_list) =
|
||||
minidump_obj.get_stream::<MinidumpMemoryInfoList>()
|
||||
{
|
||||
for memory_info in minidump_memory_info_list.iter() {
|
||||
if let Some(memory_range) = memory_info.memory_range() {
|
||||
debug!(
|
||||
"Found memory protection info for memory segment ranging from virtual address {:#x} to {:#x}: {:#?}",
|
||||
memory_range.start,
|
||||
memory_range.end,
|
||||
memory_info.protection
|
||||
);
|
||||
segment_protection_data.insert(
|
||||
// The range returned to us by MinidumpMemoryInfoList is an
|
||||
// end-inclusive range_map::Range; we need to add 1 to
|
||||
// the end index to make it into an end-exclusive std::ops::Range.
|
||||
Range {
|
||||
start: memory_range.start,
|
||||
end: memory_range.end + 1,
|
||||
},
|
||||
memory_info.protection,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for segment in segment_data.iter() {
|
||||
if let Some(segment_protection) =
|
||||
segment_protection_data.get(&segment.mapped_addr_range)
|
||||
{
|
||||
let segment_memory_protection =
|
||||
MinidumpBinaryView::translate_memory_protection(*segment_protection);
|
||||
|
||||
info!(
|
||||
"Adding memory segment at virtual address {:#x} to {:#x}, from data range {:#x} to {:#x}, with protections readable {}, writable {}, executable {}",
|
||||
segment.mapped_addr_range.start,
|
||||
segment.mapped_addr_range.end,
|
||||
segment.rva_range.start,
|
||||
segment.rva_range.end,
|
||||
segment_memory_protection.readable,
|
||||
segment_memory_protection.writable,
|
||||
segment_memory_protection.executable,
|
||||
);
|
||||
|
||||
self.add_segment(
|
||||
Segment::builder(segment.mapped_addr_range.clone())
|
||||
.parent_backing(segment.rva_range.clone())
|
||||
.is_auto(true)
|
||||
.readable(segment_memory_protection.readable)
|
||||
.writable(segment_memory_protection.writable)
|
||||
.executable(segment_memory_protection.executable),
|
||||
);
|
||||
} else {
|
||||
error!(
|
||||
"Could not find memory protection information for memory segment from {:#x} to {:#x}", segment.mapped_addr_range.start,
|
||||
segment.mapped_addr_range.end,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Module information
|
||||
// This stretches the concept a bit, but we can add each module as a
|
||||
// separate "section" of the binary.
|
||||
// Sections can be named, and can span multiple segments.
|
||||
if let Ok(minidump_module_list) = minidump_obj.get_stream::<MinidumpModuleList>() {
|
||||
for module_info in minidump_module_list.by_addr() {
|
||||
info!(
|
||||
"Found module with name {} at virtual address {:#x} with size {:#x}",
|
||||
module_info.name,
|
||||
module_info.base_address(),
|
||||
module_info.size(),
|
||||
);
|
||||
let module_address_range = Range {
|
||||
start: module_info.base_address(),
|
||||
end: module_info.base_address() + module_info.size(),
|
||||
};
|
||||
self.add_section(
|
||||
Section::builder(module_info.name.clone(), module_address_range)
|
||||
.is_auto(true),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
warn!("Could not find valid module information in minidump: could not find a valid MinidumpModuleList stream");
|
||||
}
|
||||
} else {
|
||||
error!("Could not parse data as minidump");
|
||||
return Err(());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn translate_minidump_platform(
|
||||
minidump_cpu_arch: minidump::system_info::Cpu,
|
||||
minidump_endian: minidump::Endian,
|
||||
minidump_os: minidump::system_info::Os,
|
||||
) -> Option<binaryninja::rc::Ref<Platform>> {
|
||||
match minidump_os {
|
||||
minidump::system_info::Os::Windows => match minidump_cpu_arch {
|
||||
minidump::system_info::Cpu::Arm64 => Platform::by_name("windows-aarch64"),
|
||||
minidump::system_info::Cpu::Arm => Platform::by_name("windows-armv7"),
|
||||
minidump::system_info::Cpu::X86 => Platform::by_name("windows-x86"),
|
||||
minidump::system_info::Cpu::X86_64 => Platform::by_name("windows-x86_64"),
|
||||
_ => None,
|
||||
},
|
||||
minidump::system_info::Os::MacOs => match minidump_cpu_arch {
|
||||
minidump::system_info::Cpu::Arm64 => Platform::by_name("mac-aarch64"),
|
||||
minidump::system_info::Cpu::Arm => Platform::by_name("mac-armv7"),
|
||||
minidump::system_info::Cpu::X86 => Platform::by_name("mac-x86"),
|
||||
minidump::system_info::Cpu::X86_64 => Platform::by_name("mac-x86_64"),
|
||||
_ => None,
|
||||
},
|
||||
minidump::system_info::Os::Linux => match minidump_cpu_arch {
|
||||
minidump::system_info::Cpu::Arm64 => Platform::by_name("linux-aarch64"),
|
||||
minidump::system_info::Cpu::Arm => Platform::by_name("linux-armv7"),
|
||||
minidump::system_info::Cpu::X86 => Platform::by_name("linux-x86"),
|
||||
minidump::system_info::Cpu::X86_64 => Platform::by_name("linux-x86_64"),
|
||||
minidump::system_info::Cpu::Ppc => match minidump_endian {
|
||||
minidump::Endian::Little => Platform::by_name("linux-ppc32_le"),
|
||||
minidump::Endian::Big => Platform::by_name("linux-ppc32"),
|
||||
},
|
||||
minidump::system_info::Cpu::Ppc64 => match minidump_endian {
|
||||
minidump::Endian::Little => Platform::by_name("linux-ppc64_le"),
|
||||
minidump::Endian::Big => Platform::by_name("linux-ppc64"),
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
minidump::system_info::Os::NaCl => None,
|
||||
minidump::system_info::Os::Android => None,
|
||||
minidump::system_info::Os::Ios => None,
|
||||
minidump::system_info::Os::Ps3 => None,
|
||||
minidump::system_info::Os::Solaris => None,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn translate_memory_protection(
|
||||
minidump_memory_protection: MemoryProtection,
|
||||
) -> SegmentMemoryProtection {
|
||||
let (readable, writable, executable) = match minidump_memory_protection {
|
||||
MemoryProtection::PAGE_NOACCESS => (false, false, false),
|
||||
MemoryProtection::PAGE_READONLY => (true, false, false),
|
||||
MemoryProtection::PAGE_READWRITE => (true, true, false),
|
||||
MemoryProtection::PAGE_WRITECOPY => (true, true, false),
|
||||
MemoryProtection::PAGE_EXECUTE => (false, false, true),
|
||||
MemoryProtection::PAGE_EXECUTE_READ => (true, false, true),
|
||||
MemoryProtection::PAGE_EXECUTE_READWRITE => (true, true, true),
|
||||
MemoryProtection::PAGE_EXECUTE_WRITECOPY => (true, true, true),
|
||||
MemoryProtection::ACCESS_MASK => (false, false, false),
|
||||
MemoryProtection::PAGE_GUARD => (false, false, false),
|
||||
MemoryProtection::PAGE_NOCACHE => (false, false, false),
|
||||
MemoryProtection::PAGE_WRITECOMBINE => (false, false, false),
|
||||
_ => (false, false, false),
|
||||
};
|
||||
SegmentMemoryProtection {
|
||||
readable,
|
||||
writable,
|
||||
executable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<BinaryView> for MinidumpBinaryView {
|
||||
fn as_ref(&self) -> &BinaryView {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl BinaryViewBase for MinidumpBinaryView {
|
||||
// TODO: This should be filled out with the actual address size
|
||||
// from the platform information in the minidump.
|
||||
fn address_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn default_endianness(&self) -> Endianness {
|
||||
// TODO: This should be filled out with the actual endianness
|
||||
// from the platform information in the minidump.
|
||||
Endianness::LittleEndian
|
||||
}
|
||||
|
||||
fn entry_point(&self) -> u64 {
|
||||
// TODO: We should fill this out with a real entry point.
|
||||
// This can be done by getting the main module of the minidump
|
||||
// with MinidumpModuleList::main_module,
|
||||
// then parsing the PE metadata of the main module to find its entry point(s).
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl CustomBinaryView for MinidumpBinaryView {
|
||||
type Args = ();
|
||||
|
||||
fn new(handle: &BinaryView, _args: &Self::Args) -> BinaryViewResult<Self> {
|
||||
Ok(MinidumpBinaryView::new(handle))
|
||||
}
|
||||
|
||||
fn init(&self, _args: Self::Args) -> BinaryViewResult<()> {
|
||||
self.init()
|
||||
}
|
||||
}
|
||||
16
examples/mlil_lifter/Cargo.toml
Normal file
16
examples/mlil_lifter/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "mlil_lifter"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# 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"}
|
||||
68
examples/mlil_lifter/build.rs
Normal file
68
examples/mlil_lifter/build.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
49
examples/mlil_lifter/src/main.rs
Normal file
49
examples/mlil_lifter/src/main.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use std::env;
|
||||
|
||||
use binaryninja::binaryview::BinaryViewExt;
|
||||
|
||||
// Standalone executables need to provide a main function for rustc
|
||||
// Plugins should refer to `binaryninja::command::*` for the various registration callbacks.
|
||||
fn main() {
|
||||
let mut args = env::args();
|
||||
let _ = args.next().unwrap();
|
||||
let Some(filename) = args.next() else {
|
||||
panic!("Expected input filename\n");
|
||||
};
|
||||
|
||||
// This loads all the core architecture, platform, etc plugins
|
||||
// Standalone executables probably need to call this, but plugins do not
|
||||
println!("Loading plugins...");
|
||||
let _headless_session = binaryninja::headless::Session::new();
|
||||
|
||||
// Your code here...
|
||||
println!("Loading binary...");
|
||||
let bv = binaryninja::load(filename).expect("Couldn't open binary file");
|
||||
|
||||
// Go through all functions in the binary
|
||||
for func in bv.functions().iter() {
|
||||
let sym = func.symbol();
|
||||
println!("Function {}:", sym.full_name());
|
||||
|
||||
let Ok(il) = func.medium_level_il() else {
|
||||
println!(" Does not have MLIL\n");
|
||||
continue;
|
||||
};
|
||||
// Get the SSA form for this function
|
||||
let il = il.ssa_form();
|
||||
|
||||
// Loop through all blocks in the function
|
||||
for block in il.basic_blocks().iter() {
|
||||
// Loop though each instruction in the block
|
||||
for instr in block.iter() {
|
||||
// Uplift the instruction into a native rust format
|
||||
let lifted = instr.lift();
|
||||
let address = instr.address();
|
||||
|
||||
// print the lifted instruction
|
||||
println!("{address:08x}: {lifted:x?}");
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
16
examples/mlil_visitor/Cargo.toml
Normal file
16
examples/mlil_visitor/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "mlil_visitor"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# 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"}
|
||||
68
examples/mlil_visitor/build.rs
Normal file
68
examples/mlil_visitor/build.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
266
examples/mlil_visitor/src/main.rs
Normal file
266
examples/mlil_visitor/src/main.rs
Normal file
@@ -0,0 +1,266 @@
|
||||
use std::env;
|
||||
|
||||
use binaryninja::binaryview::BinaryViewExt;
|
||||
use binaryninja::mlil::operation::MediumLevelILOperand;
|
||||
use binaryninja::mlil::{MediumLevelILFunction, MediumLevelILInstruction, MediumLevelILOperation};
|
||||
use binaryninja::types::Variable;
|
||||
|
||||
fn print_indent(indent: usize) {
|
||||
print!("{:<indent$}", "")
|
||||
}
|
||||
|
||||
fn print_operation(operation: &MediumLevelILOperation) {
|
||||
use MediumLevelILOperation::*;
|
||||
match operation {
|
||||
Nop(_) => print!("Nop"),
|
||||
Noret(_) => print!("Noret"),
|
||||
Bp(_) => print!("Bp"),
|
||||
Undef(_) => print!("Undef"),
|
||||
Unimpl(_) => print!("Unimpl"),
|
||||
If(_) => print!("If"),
|
||||
FloatConst(_) => print!("FloatConst"),
|
||||
Const(_) => print!("Const"),
|
||||
ConstPtr(_) => print!("ConstPtr"),
|
||||
Import(_) => print!("Import"),
|
||||
ExternPtr(_) => print!("ExternPtr"),
|
||||
ConstData(_) => print!("ConstData"),
|
||||
Jump(_) => print!("Jump"),
|
||||
RetHint(_) => print!("RetHint"),
|
||||
StoreSsa(_) => print!("StoreSsa"),
|
||||
StoreStructSsa(_) => print!("StoreStructSsa"),
|
||||
StoreStruct(_) => print!("StoreStruct"),
|
||||
Store(_) => print!("Store"),
|
||||
JumpTo(_) => print!("JumpTo"),
|
||||
Goto(_) => print!("Goto"),
|
||||
FreeVarSlot(_) => print!("FreeVarSlot"),
|
||||
SetVarField(_) => print!("SetVarField"),
|
||||
SetVar(_) => print!("SetVar"),
|
||||
FreeVarSlotSsa(_) => print!("FreeVarSlotSsa"),
|
||||
SetVarSsaField(_) => print!("SetVarSsaField"),
|
||||
SetVarAliasedField(_) => print!("SetVarAliasedField"),
|
||||
SetVarAliased(_) => print!("SetVarAliased"),
|
||||
SetVarSsa(_) => print!("SetVarSsa"),
|
||||
VarPhi(_) => print!("VarPhi"),
|
||||
MemPhi(_) => print!("MemPhi"),
|
||||
VarSplit(_) => print!("VarSplit"),
|
||||
SetVarSplit(_) => print!("SetVarSplit"),
|
||||
VarSplitSsa(_) => print!("VarSplitSsa"),
|
||||
SetVarSplitSsa(_) => print!("SetVarSplitSsa"),
|
||||
Add(_) => print!("Add"),
|
||||
Sub(_) => print!("Sub"),
|
||||
And(_) => print!("And"),
|
||||
Or(_) => print!("Or"),
|
||||
Xor(_) => print!("Xor"),
|
||||
Lsl(_) => print!("Lsl"),
|
||||
Lsr(_) => print!("Lsr"),
|
||||
Asr(_) => print!("Asr"),
|
||||
Rol(_) => print!("Rol"),
|
||||
Ror(_) => print!("Ror"),
|
||||
Mul(_) => print!("Mul"),
|
||||
MuluDp(_) => print!("MuluDp"),
|
||||
MulsDp(_) => print!("MulsDp"),
|
||||
Divu(_) => print!("Divu"),
|
||||
DivuDp(_) => print!("DivuDp"),
|
||||
Divs(_) => print!("Divs"),
|
||||
DivsDp(_) => print!("DivsDp"),
|
||||
Modu(_) => print!("Modu"),
|
||||
ModuDp(_) => print!("ModuDp"),
|
||||
Mods(_) => print!("Mods"),
|
||||
ModsDp(_) => print!("ModsDp"),
|
||||
CmpE(_) => print!("CmpE"),
|
||||
CmpNe(_) => print!("CmpNe"),
|
||||
CmpSlt(_) => print!("CmpSlt"),
|
||||
CmpUlt(_) => print!("CmpUlt"),
|
||||
CmpSle(_) => print!("CmpSle"),
|
||||
CmpUle(_) => print!("CmpUle"),
|
||||
CmpSge(_) => print!("CmpSge"),
|
||||
CmpUge(_) => print!("CmpUge"),
|
||||
CmpSgt(_) => print!("CmpSgt"),
|
||||
CmpUgt(_) => print!("CmpUgt"),
|
||||
TestBit(_) => print!("TestBit"),
|
||||
AddOverflow(_) => print!("AddOverflow"),
|
||||
FcmpE(_) => print!("FcmpE"),
|
||||
FcmpNe(_) => print!("FcmpNe"),
|
||||
FcmpLt(_) => print!("FcmpLt"),
|
||||
FcmpLe(_) => print!("FcmpLe"),
|
||||
FcmpGe(_) => print!("FcmpGe"),
|
||||
FcmpGt(_) => print!("FcmpGt"),
|
||||
FcmpO(_) => print!("FcmpO"),
|
||||
FcmpUo(_) => print!("FcmpUo"),
|
||||
Fadd(_) => print!("Fadd"),
|
||||
Fsub(_) => print!("Fsub"),
|
||||
Fmul(_) => print!("Fmul"),
|
||||
Fdiv(_) => print!("Fdiv"),
|
||||
Adc(_) => print!("Adc"),
|
||||
Sbb(_) => print!("Sbb"),
|
||||
Rlc(_) => print!("Rlc"),
|
||||
Rrc(_) => print!("Rrc"),
|
||||
Call(_) => print!("Call"),
|
||||
Tailcall(_) => print!("Tailcall"),
|
||||
Syscall(_) => print!("Syscall"),
|
||||
Intrinsic(_) => print!("Intrinsic"),
|
||||
IntrinsicSsa(_) => print!("IntrinsicSsa"),
|
||||
CallSsa(_) => print!("CallSsa"),
|
||||
TailcallSsa(_) => print!("TailcallSsa"),
|
||||
CallUntypedSsa(_) => print!("CallUntypedSsa"),
|
||||
TailcallUntypedSsa(_) => print!("TailcallUntypedSsa"),
|
||||
SyscallSsa(_) => print!("SyscallSsa"),
|
||||
SyscallUntypedSsa(_) => print!("SyscallUntypedSsa"),
|
||||
CallUntyped(_) => print!("CallUntyped"),
|
||||
TailcallUntyped(_) => print!("TailcallUntyped"),
|
||||
SyscallUntyped(_) => print!("SyscallUntyped"),
|
||||
SeparateParamList(_) => print!("SeparateParamList"),
|
||||
SharedParamSlot(_) => print!("SharedParamSlot"),
|
||||
Neg(_) => print!("Neg"),
|
||||
Not(_) => print!("Not"),
|
||||
Sx(_) => print!("Sx"),
|
||||
Zx(_) => print!("Zx"),
|
||||
LowPart(_) => print!("LowPart"),
|
||||
BoolToInt(_) => print!("BoolToInt"),
|
||||
UnimplMem(_) => print!("UnimplMem"),
|
||||
Fsqrt(_) => print!("Fsqrt"),
|
||||
Fneg(_) => print!("Fneg"),
|
||||
Fabs(_) => print!("Fabs"),
|
||||
FloatToInt(_) => print!("FloatToInt"),
|
||||
IntToFloat(_) => print!("IntToFloat"),
|
||||
FloatConv(_) => print!("FloatConv"),
|
||||
RoundToInt(_) => print!("RoundToInt"),
|
||||
Floor(_) => print!("Floor"),
|
||||
Ceil(_) => print!("Ceil"),
|
||||
Ftrunc(_) => print!("Ftrunc"),
|
||||
Load(_) => print!("Load"),
|
||||
LoadStruct(_) => print!("LoadStruct"),
|
||||
LoadStructSsa(_) => print!("LoadStructSsa"),
|
||||
LoadSsa(_) => print!("LoadSsa"),
|
||||
Ret(_) => print!("Ret"),
|
||||
Var(_) => print!("Var"),
|
||||
AddressOf(_) => print!("AddressOf"),
|
||||
VarField(_) => print!("VarField"),
|
||||
AddressOfField(_) => print!("AddressOfField"),
|
||||
VarSsa(_) => print!("VarSsa"),
|
||||
VarAliased(_) => print!("VarAliased"),
|
||||
VarSsaField(_) => print!("VarSsaField"),
|
||||
VarAliasedField(_) => print!("VarAliasedField"),
|
||||
Trap(_) => print!("Trap"),
|
||||
}
|
||||
}
|
||||
|
||||
fn print_variable(func: &MediumLevelILFunction, var: &Variable) {
|
||||
print!("{}", func.get_function().get_variable_name(var));
|
||||
}
|
||||
|
||||
fn print_il_expr(instr: &MediumLevelILInstruction, mut indent: usize) {
|
||||
print_indent(indent);
|
||||
print_operation(instr.operation());
|
||||
println!("");
|
||||
|
||||
indent += 1;
|
||||
|
||||
use MediumLevelILOperand::*;
|
||||
for (_name, operand) in instr.operands() {
|
||||
match operand {
|
||||
Int(int) => {
|
||||
print_indent(indent);
|
||||
println!("int 0x{:x}", int);
|
||||
}
|
||||
Float(float) => {
|
||||
print_indent(indent);
|
||||
println!("int {:e}", float);
|
||||
}
|
||||
Expr(expr) => print_il_expr(&expr, indent),
|
||||
Var(var) => {
|
||||
print_indent(indent);
|
||||
print!("var ");
|
||||
print_variable(instr.function(), &var);
|
||||
println!();
|
||||
}
|
||||
VarSsa(var) => {
|
||||
print_indent(indent);
|
||||
print!("ssa var ");
|
||||
print_variable(instr.function(), &var.variable);
|
||||
println!("#{}", var.version);
|
||||
}
|
||||
IntList(list) => {
|
||||
print_indent(indent);
|
||||
print!("index list ");
|
||||
for i in list {
|
||||
print!("{i} ");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
VarList(list) => {
|
||||
print_indent(indent);
|
||||
print!("var list ");
|
||||
for i in list {
|
||||
print_variable(instr.function(), &i);
|
||||
print!(" ");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
VarSsaList(list) => {
|
||||
print_indent(indent);
|
||||
print!("ssa var list ");
|
||||
for i in list {
|
||||
print_variable(instr.function(), &i.variable);
|
||||
print!("#{} ", i.version);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
ExprList(list) => {
|
||||
print_indent(indent);
|
||||
println!("expr list");
|
||||
for i in list {
|
||||
print_il_expr(&i, indent + 1);
|
||||
}
|
||||
}
|
||||
TargetMap(list) => {
|
||||
print_indent(indent);
|
||||
print!("target map ");
|
||||
for (i, f) in list {
|
||||
print!("({i}, {f}) ");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Standalone executables need to provide a main function for rustc
|
||||
// Plugins should refer to `binaryninja::command::*` for the various registration callbacks.
|
||||
fn main() {
|
||||
let mut args = env::args();
|
||||
let _ = args.next().unwrap();
|
||||
let Some(filename) = args.next() else {
|
||||
panic!("Expected input filename\n");
|
||||
};
|
||||
|
||||
// This loads all the core architecture, platform, etc plugins
|
||||
// Standalone executables probably need to call this, but plugins do not
|
||||
println!("Loading plugins...");
|
||||
let _headless_session = binaryninja::headless::Session::new();
|
||||
|
||||
// Your code here...
|
||||
println!("Loading binary...");
|
||||
let bv = binaryninja::load(filename).expect("Couldn't open binary file");
|
||||
|
||||
// Go through all functions in the binary
|
||||
for func in bv.functions().iter() {
|
||||
let sym = func.symbol();
|
||||
println!("Function {}:", sym.full_name());
|
||||
|
||||
let Ok(il) = func.medium_level_il() else {
|
||||
println!(" Does not have MLIL\n");
|
||||
continue;
|
||||
};
|
||||
|
||||
// Loop through all blocks in the function
|
||||
for block in il.basic_blocks().iter() {
|
||||
// Loop though each instruction in the block
|
||||
for instr in block.iter() {
|
||||
// Generically parse the IL tree and display the parts
|
||||
print_il_expr(&instr, 2);
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
355
examples/template/Cargo.lock
generated
Normal file
355
examples/template/Cargo.lock
generated
Normal file
@@ -0,0 +1,355 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binaryninja"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/Vector35/binaryninja-api.git?branch=dev#83d7a7800ac4a970d618ad125cdbb18e3b29b53e"
|
||||
dependencies = [
|
||||
"binaryninjacore-sys",
|
||||
"libc",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binaryninjacore-sys"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/Vector35/binaryninja-api.git?branch=dev#83d7a7800ac4a970d618ad125cdbb18e3b29b53e"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.58.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f8523b410d7187a43085e7e064416ea32ded16bd0a4e6fc025e21616d01258f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa66045b9cb23c2e9c1520732030608b02ee07e5cfaa5a521ec15ded7fa24c90"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "template"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"binaryninja",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
16
examples/template/Cargo.toml
Normal file
16
examples/template/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "template"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# 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"}
|
||||
19
examples/template/README.md
Normal file
19
examples/template/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Template
|
||||
|
||||
[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()` (see [`src/main.rs`](src/main.rs)).
|
||||
Standalone executables will fail to link if you do not provide a `build.rs`. The one provided here should work.
|
||||
68
examples/template/build.rs
Normal file
68
examples/template/build.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
42
examples/template/src/main.rs
Normal file
42
examples/template/src/main.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
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...");
|
||||
let headless_session = binaryninja::headless::Session::new();
|
||||
|
||||
// Your code here...
|
||||
println!("Loading binary...");
|
||||
let bv = headless_session
|
||||
.load("/bin/cat")
|
||||
.expect("Couldn't open `/bin/cat`");
|
||||
|
||||
println!("Filename: `{}`", bv.file().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);
|
||||
if let Some((_, tokens)) = func.arch().instruction_text(
|
||||
bv.read_buffer(addr, func.arch().max_instr_len())
|
||||
.unwrap()
|
||||
.get_data(),
|
||||
addr,
|
||||
) {
|
||||
tokens
|
||||
.iter()
|
||||
.for_each(|token| print!("{}", token.text().as_str()));
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
source
Submodule
1
source
Submodule
Submodule source added at 3dd22f4099
2884
src/architecture.rs
Normal file
2884
src/architecture.rs
Normal file
File diff suppressed because it is too large
Load Diff
135
src/backgroundtask.rs
Normal file
135
src/backgroundtask.rs
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Background tasks provide plugins the ability to run tasks in the background so they don't hand the UI
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use std::result;
|
||||
|
||||
use crate::rc::*;
|
||||
use crate::string::*;
|
||||
|
||||
pub type Result<R> = result::Result<R, ()>;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct BackgroundTask {
|
||||
pub(crate) handle: *mut BNBackgroundTask,
|
||||
}
|
||||
|
||||
impl BackgroundTask {
|
||||
pub(crate) unsafe fn from_raw(handle: *mut BNBackgroundTask) -> Self {
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub fn new<S: BnStrCompatible>(initial_text: S, can_cancel: bool) -> Result<Ref<Self>> {
|
||||
let text = initial_text.into_bytes_with_nul();
|
||||
|
||||
let handle = unsafe { BNBeginBackgroundTask(text.as_ref().as_ptr() as *mut _, can_cancel) };
|
||||
|
||||
if handle.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
unsafe { Ok(Ref::new(Self { handle })) }
|
||||
}
|
||||
|
||||
pub fn can_cancel(&self) -> bool {
|
||||
unsafe { BNCanCancelBackgroundTask(self.handle) }
|
||||
}
|
||||
|
||||
pub fn is_cancelled(&self) -> bool {
|
||||
unsafe { BNIsBackgroundTaskCancelled(self.handle) }
|
||||
}
|
||||
|
||||
pub fn is_finished(&self) -> bool {
|
||||
unsafe { BNIsBackgroundTaskFinished(self.handle) }
|
||||
}
|
||||
|
||||
pub fn get_progress_text(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNGetBackgroundTaskProgressText(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn cancel(&self) {
|
||||
unsafe { BNCancelBackgroundTask(self.handle) }
|
||||
}
|
||||
|
||||
pub fn finish(&self) {
|
||||
unsafe { BNFinishBackgroundTask(self.handle) }
|
||||
}
|
||||
|
||||
pub fn set_progress_text<S: BnStrCompatible>(&self, text: S) {
|
||||
let progress_text = text.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
BNSetBackgroundTaskProgressText(self.handle, progress_text.as_ref().as_ptr() as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn running_tasks() -> Array<BackgroundTask> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let handles = BNGetRunningBackgroundTasks(&mut count);
|
||||
|
||||
Array::new(handles, count, ())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for BackgroundTask {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewBackgroundTaskReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeBackgroundTask(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for BackgroundTask {
|
||||
type Raw = *mut BNBackgroundTask;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for BackgroundTask {
|
||||
unsafe fn free(raw: *mut *mut BNBackgroundTask, count: usize, _context: &()) {
|
||||
BNFreeBackgroundTaskList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for BackgroundTask {
|
||||
type Wrapped = Guard<'a, BackgroundTask>;
|
||||
|
||||
unsafe fn wrap_raw(
|
||||
raw: &'a *mut BNBackgroundTask,
|
||||
context: &'a (),
|
||||
) -> Guard<'a, BackgroundTask> {
|
||||
Guard::new(BackgroundTask::from_raw(*raw), context)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for BackgroundTask {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for BackgroundTask {}
|
||||
unsafe impl Sync for BackgroundTask {}
|
||||
318
src/basicblock.rs
Normal file
318
src/basicblock.rs
Normal file
@@ -0,0 +1,318 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use crate::architecture::CoreArchitecture;
|
||||
use crate::function::Function;
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::rc::*;
|
||||
|
||||
enum EdgeDirection {
|
||||
Incoming,
|
||||
Outgoing,
|
||||
}
|
||||
|
||||
pub struct Edge<'a, C: 'a + BlockContext> {
|
||||
branch: super::BranchType,
|
||||
back_edge: bool,
|
||||
source: Guard<'a, BasicBlock<C>>,
|
||||
target: Guard<'a, BasicBlock<C>>,
|
||||
}
|
||||
|
||||
impl<'a, C: 'a + BlockContext> Edge<'a, C> {
|
||||
pub fn branch_type(&self) -> super::BranchType {
|
||||
self.branch
|
||||
}
|
||||
|
||||
pub fn back_edge(&self) -> bool {
|
||||
self.back_edge
|
||||
}
|
||||
|
||||
pub fn source(&self) -> &BasicBlock<C> {
|
||||
&self.source
|
||||
}
|
||||
|
||||
pub fn target(&self) -> &BasicBlock<C> {
|
||||
&self.target
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: 'a + fmt::Debug + BlockContext> fmt::Debug for Edge<'a, C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:?} ({}) {:?} -> {:?}",
|
||||
self.branch, self.back_edge, &*self.source, &*self.target
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EdgeContext<'a, C: 'a + BlockContext> {
|
||||
dir: EdgeDirection,
|
||||
orig_block: &'a BasicBlock<C>,
|
||||
}
|
||||
|
||||
impl<'a, C: 'a + BlockContext> CoreArrayProvider for Edge<'a, C> {
|
||||
type Raw = BNBasicBlockEdge;
|
||||
type Context = EdgeContext<'a, C>;
|
||||
}
|
||||
|
||||
unsafe impl<'a, C: 'a + BlockContext> CoreOwnedArrayProvider for Edge<'a, C> {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeBasicBlockEdgeList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, C: 'a + BlockContext> CoreArrayWrapper<'a> for Edge<'a, C> {
|
||||
type Wrapped = Edge<'a, C>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Edge<'a, C> {
|
||||
let edge_target = Guard::new(
|
||||
BasicBlock::from_raw(raw.target, context.orig_block.context.clone()),
|
||||
raw,
|
||||
);
|
||||
let orig_block = Guard::new(
|
||||
BasicBlock::from_raw(
|
||||
context.orig_block.handle,
|
||||
context.orig_block.context.clone(),
|
||||
),
|
||||
raw,
|
||||
);
|
||||
|
||||
let (source, target) = match context.dir {
|
||||
EdgeDirection::Incoming => (edge_target, orig_block),
|
||||
EdgeDirection::Outgoing => (orig_block, edge_target),
|
||||
};
|
||||
|
||||
Edge {
|
||||
branch: raw.type_,
|
||||
back_edge: raw.backEdge,
|
||||
source,
|
||||
target,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BlockContext: Clone + Sync + Send + Sized {
|
||||
type Instruction;
|
||||
type Iter: Iterator<Item = Self::Instruction>;
|
||||
|
||||
fn start(&self, block: &BasicBlock<Self>) -> Self::Instruction;
|
||||
fn iter(&self, block: &BasicBlock<Self>) -> Self::Iter;
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct BasicBlock<C: BlockContext> {
|
||||
pub(crate) handle: *mut BNBasicBlock,
|
||||
context: C,
|
||||
}
|
||||
|
||||
unsafe impl<C: BlockContext> Send for BasicBlock<C> {}
|
||||
unsafe impl<C: BlockContext> Sync for BasicBlock<C> {}
|
||||
|
||||
impl<C: BlockContext> BasicBlock<C> {
|
||||
pub(crate) unsafe fn from_raw(handle: *mut BNBasicBlock, context: C) -> Self {
|
||||
Self { handle, context }
|
||||
}
|
||||
|
||||
// TODO native bb vs il bbs
|
||||
pub fn function(&self) -> Ref<Function> {
|
||||
unsafe {
|
||||
let func = BNGetBasicBlockFunction(self.handle);
|
||||
Function::from_raw(func)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arch(&self) -> CoreArchitecture {
|
||||
unsafe {
|
||||
let arch = BNGetBasicBlockArchitecture(self.handle);
|
||||
CoreArchitecture::from_raw(arch)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> C::Iter {
|
||||
self.context.iter(self)
|
||||
}
|
||||
|
||||
pub fn raw_start(&self) -> u64 {
|
||||
unsafe { BNGetBasicBlockStart(self.handle) }
|
||||
}
|
||||
|
||||
pub fn raw_end(&self) -> u64 {
|
||||
unsafe { BNGetBasicBlockEnd(self.handle) }
|
||||
}
|
||||
|
||||
pub fn raw_length(&self) -> u64 {
|
||||
unsafe { BNGetBasicBlockLength(self.handle) }
|
||||
}
|
||||
|
||||
pub fn incoming_edges(&self) -> Array<Edge<C>> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let edges = BNGetBasicBlockIncomingEdges(self.handle, &mut count);
|
||||
|
||||
Array::new(
|
||||
edges,
|
||||
count,
|
||||
EdgeContext {
|
||||
dir: EdgeDirection::Incoming,
|
||||
orig_block: self,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn outgoing_edges(&self) -> Array<Edge<C>> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let edges = BNGetBasicBlockOutgoingEdges(self.handle, &mut count);
|
||||
|
||||
Array::new(
|
||||
edges,
|
||||
count,
|
||||
EdgeContext {
|
||||
dir: EdgeDirection::Outgoing,
|
||||
orig_block: self,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// is this valid for il blocks?
|
||||
pub fn has_undetermined_outgoing_edges(&self) -> bool {
|
||||
unsafe { BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) }
|
||||
}
|
||||
|
||||
pub fn can_exit(&self) -> bool {
|
||||
unsafe { BNBasicBlockCanExit(self.handle) }
|
||||
}
|
||||
|
||||
pub fn index(&self) -> usize {
|
||||
unsafe { BNGetBasicBlockIndex(self.handle) }
|
||||
}
|
||||
|
||||
pub fn immediate_dominator(&self) -> Option<Ref<Self>> {
|
||||
unsafe {
|
||||
let block = BNGetBasicBlockImmediateDominator(self.handle, false);
|
||||
|
||||
if block.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Ref::new(BasicBlock::from_raw(block, self.context.clone())))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dominators(&self) -> Array<BasicBlock<C>> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let blocks = BNGetBasicBlockDominators(self.handle, &mut count, false);
|
||||
|
||||
Array::new(blocks, count, self.context.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn strict_dominators(&self) -> Array<BasicBlock<C>> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let blocks = BNGetBasicBlockStrictDominators(self.handle, &mut count, false);
|
||||
|
||||
Array::new(blocks, count, self.context.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dominator_tree_children(&self) -> Array<BasicBlock<C>> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let blocks = BNGetBasicBlockDominatorTreeChildren(self.handle, &mut count, false);
|
||||
|
||||
Array::new(blocks, count, self.context.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dominance_frontier(&self) -> Array<BasicBlock<C>> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let blocks = BNGetBasicBlockDominanceFrontier(self.handle, &mut count, false);
|
||||
|
||||
Array::new(blocks, count, self.context.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO iterated dominance frontier
|
||||
}
|
||||
|
||||
impl<'a, C: BlockContext> IntoIterator for &'a BasicBlock<C> {
|
||||
type Item = C::Instruction;
|
||||
type IntoIter = C::Iter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: fmt::Debug + BlockContext> fmt::Debug for BasicBlock<C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"<bb handle {:p} context {:?} contents: {} -> {}>",
|
||||
self.handle,
|
||||
&self.context,
|
||||
self.raw_start(),
|
||||
self.raw_end()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: BlockContext> ToOwned for BasicBlock<C> {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<C: BlockContext> RefCountable for BasicBlock<C> {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewBasicBlockReference(handle.handle),
|
||||
context: handle.context.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeBasicBlock(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: BlockContext> CoreArrayProvider for BasicBlock<C> {
|
||||
type Raw = *mut BNBasicBlock;
|
||||
type Context = C;
|
||||
}
|
||||
|
||||
unsafe impl<C: BlockContext> CoreOwnedArrayProvider for BasicBlock<C> {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeBasicBlockList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, C: 'a + BlockContext> CoreArrayWrapper<'a> for BasicBlock<C> {
|
||||
type Wrapped = Guard<'a, BasicBlock<C>>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
|
||||
Guard::new(BasicBlock::from_raw(*raw, context.clone()), context)
|
||||
}
|
||||
}
|
||||
101
src/binaryreader.rs
Normal file
101
src/binaryreader.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2022-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! A convenience class for reading binary data
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::binaryview::BinaryView;
|
||||
use crate::Endianness;
|
||||
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
|
||||
pub struct BinaryReader {
|
||||
handle: *mut BNBinaryReader,
|
||||
}
|
||||
|
||||
impl BinaryReader {
|
||||
pub fn new(view: &BinaryView, endian: Endianness) -> Self {
|
||||
let handle = unsafe { BNCreateBinaryReader(view.handle) };
|
||||
unsafe {
|
||||
BNSetBinaryReaderEndianness(handle, endian);
|
||||
}
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub fn endian(&self) -> Endianness {
|
||||
unsafe { BNGetBinaryReaderEndianness(self.handle) }
|
||||
}
|
||||
|
||||
pub fn set_endian(&self, endian: Endianness) {
|
||||
unsafe { BNSetBinaryReaderEndianness(self.handle, endian) }
|
||||
}
|
||||
|
||||
pub fn offset(&self) -> u64 {
|
||||
unsafe { BNGetReaderPosition(self.handle) }
|
||||
}
|
||||
|
||||
pub fn eof(&self) -> bool {
|
||||
unsafe { BNIsEndOfFile(self.handle) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for BinaryReader {
|
||||
/// Seek to the specified position.
|
||||
///
|
||||
/// # Errors
|
||||
/// Seeking relative to [SeekFrom::End] is unsupported and will return an error.
|
||||
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
|
||||
unsafe {
|
||||
match pos {
|
||||
SeekFrom::Current(offset) => BNSeekBinaryReaderRelative(self.handle, offset),
|
||||
SeekFrom::Start(offset) => BNSeekBinaryReader(self.handle, offset),
|
||||
_ => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Unsupported,
|
||||
"Cannot seek end of BinaryReader",
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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(
|
||||
std::io::ErrorKind::Other,
|
||||
"Read out of bounds",
|
||||
))
|
||||
} else {
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BinaryReader {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeBinaryReader(self.handle) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for BinaryReader {}
|
||||
unsafe impl Send for BinaryReader {}
|
||||
1454
src/binaryview.rs
Normal file
1454
src/binaryview.rs
Normal file
File diff suppressed because it is too large
Load Diff
99
src/binarywriter.rs
Normal file
99
src/binarywriter.rs
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright 2022-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! A convenience class for writing binary data
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::binaryview::BinaryView;
|
||||
use crate::Endianness;
|
||||
|
||||
use std::io::{Seek, SeekFrom, Write};
|
||||
|
||||
pub struct BinaryWriter {
|
||||
handle: *mut BNBinaryWriter,
|
||||
}
|
||||
|
||||
impl BinaryWriter {
|
||||
pub fn new(view: &BinaryView, endian: Endianness) -> Self {
|
||||
let handle = unsafe { BNCreateBinaryWriter(view.handle) };
|
||||
unsafe {
|
||||
BNSetBinaryWriterEndianness(handle, endian);
|
||||
}
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub fn endian(&self) -> Endianness {
|
||||
unsafe { BNGetBinaryWriterEndianness(self.handle) }
|
||||
}
|
||||
|
||||
pub fn set_endian(&self, endian: Endianness) {
|
||||
unsafe { BNSetBinaryWriterEndianness(self.handle, endian) }
|
||||
}
|
||||
|
||||
pub fn offset(&self) -> u64 {
|
||||
unsafe { BNGetWriterPosition(self.handle) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for BinaryWriter {
|
||||
/// Seek to the specified position.
|
||||
///
|
||||
/// # Errors
|
||||
/// Seeking relative to [SeekFrom::End] is unsupported and will return an error.
|
||||
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
|
||||
unsafe {
|
||||
match pos {
|
||||
SeekFrom::Current(offset) => BNSeekBinaryWriterRelative(self.handle, offset),
|
||||
SeekFrom::Start(offset) => BNSeekBinaryWriter(self.handle, offset),
|
||||
_ => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Unsupported,
|
||||
"Cannot seek end of BinaryWriter",
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(self.offset())
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for BinaryWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
let len = buf.len();
|
||||
let result = unsafe { BNWriteData(self.handle, buf.as_ptr() as *mut _, len) };
|
||||
if !result {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"write out of bounds",
|
||||
))
|
||||
} else {
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BinaryWriter {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeBinaryWriter(self.handle) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for BinaryWriter {}
|
||||
unsafe impl Send for BinaryWriter {}
|
||||
872
src/callingconvention.rs
Normal file
872
src/callingconvention.rs
Normal file
@@ -0,0 +1,872 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Contains and provides information about different systems' calling conventions to analysis.
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::architecture::{Architecture, ArchitectureExt, CoreArchitecture, Register};
|
||||
use crate::rc::{
|
||||
CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Guard, Ref, RefCountable,
|
||||
};
|
||||
use crate::string::*;
|
||||
|
||||
// TODO
|
||||
// force valid registers once Arch has _from_id methods
|
||||
// CallingConvention impl
|
||||
// dataflow callbacks
|
||||
|
||||
pub trait CallingConventionBase: Sync {
|
||||
type Arch: Architecture;
|
||||
|
||||
fn caller_saved_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>;
|
||||
fn callee_saved_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>;
|
||||
fn int_arg_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>;
|
||||
fn float_arg_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>;
|
||||
|
||||
fn arg_registers_shared_index(&self) -> bool;
|
||||
fn reserved_stack_space_for_arg_registers(&self) -> bool;
|
||||
fn stack_adjusted_on_return(&self) -> bool;
|
||||
fn is_eligible_for_heuristics(&self) -> bool;
|
||||
|
||||
fn return_int_reg(&self) -> Option<<Self::Arch as Architecture>::Register>;
|
||||
fn return_hi_int_reg(&self) -> Option<<Self::Arch as Architecture>::Register>;
|
||||
fn return_float_reg(&self) -> Option<<Self::Arch as Architecture>::Register>;
|
||||
|
||||
fn global_pointer_reg(&self) -> Option<<Self::Arch as Architecture>::Register>;
|
||||
|
||||
fn implicitly_defined_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>;
|
||||
fn are_argument_registers_used_for_var_args(&self) -> bool;
|
||||
}
|
||||
|
||||
pub fn register_calling_convention<A, N, C>(arch: &A, name: N, cc: C) -> Ref<CallingConvention<A>>
|
||||
where
|
||||
A: Architecture,
|
||||
N: BnStrCompatible,
|
||||
C: 'static + CallingConventionBase<Arch = A>,
|
||||
{
|
||||
struct CustomCallingConventionContext<C>
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
raw_handle: *mut BNCallingConvention,
|
||||
cc: C,
|
||||
}
|
||||
|
||||
extern "C" fn cb_free<C>(ctxt: *mut c_void)
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::free", unsafe {
|
||||
let _ctxt = Box::from_raw(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
})
|
||||
}
|
||||
|
||||
fn alloc_register_list<I: Iterator<Item = u32> + ExactSizeIterator>(
|
||||
items: I,
|
||||
count: &mut usize,
|
||||
) -> *mut u32 {
|
||||
let len = items.len();
|
||||
*count = len;
|
||||
|
||||
if len == 0 {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
let mut res = Vec::with_capacity(len + 1);
|
||||
|
||||
res.push(len as u32);
|
||||
|
||||
for i in items {
|
||||
res.push(i);
|
||||
}
|
||||
|
||||
assert!(res.len() == len + 1);
|
||||
|
||||
let raw = res.as_mut_ptr();
|
||||
mem::forget(res);
|
||||
|
||||
unsafe { raw.offset(1) }
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn cb_free_register_list(_ctxt: *mut c_void, regs: *mut u32) {
|
||||
ffi_wrap!("CallingConvention::free_register_list", unsafe {
|
||||
if regs.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let actual_start = regs.offset(-1);
|
||||
let len = *actual_start + 1;
|
||||
let _regs = Vec::from_raw_parts(actual_start, len as usize, len as usize);
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_caller_saved<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::caller_saved_registers", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
let regs = ctxt.cc.caller_saved_registers();
|
||||
|
||||
alloc_register_list(regs.iter().map(|r| r.id()), &mut *count)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_callee_saved<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::_callee_saved_registers", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
let regs = ctxt.cc.callee_saved_registers();
|
||||
|
||||
alloc_register_list(regs.iter().map(|r| r.id()), &mut *count)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_int_args<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::int_arg_registers", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
let regs = ctxt.cc.int_arg_registers();
|
||||
|
||||
alloc_register_list(regs.iter().map(|r| r.id()), &mut *count)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_float_args<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::float_arg_registers", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
let regs = ctxt.cc.float_arg_registers();
|
||||
|
||||
alloc_register_list(regs.iter().map(|r| r.id()), &mut *count)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_arg_shared_index<C>(ctxt: *mut c_void) -> bool
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::arg_registers_shared_index", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
|
||||
ctxt.cc.arg_registers_shared_index()
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_stack_reserved_arg_regs<C>(ctxt: *mut c_void) -> bool
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!(
|
||||
"CallingConvention::reserved_stack_space_for_arg_registers",
|
||||
unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
|
||||
ctxt.cc.reserved_stack_space_for_arg_registers()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
extern "C" fn cb_stack_adjusted_on_return<C>(ctxt: *mut c_void) -> bool
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::stack_adjusted_on_return", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
|
||||
ctxt.cc.stack_adjusted_on_return()
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_is_eligible_for_heuristics<C>(ctxt: *mut c_void) -> bool
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::is_eligible_for_heuristics", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
|
||||
ctxt.cc.is_eligible_for_heuristics()
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_return_int_reg<C>(ctxt: *mut c_void) -> u32
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::return_int_reg", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
|
||||
match ctxt.cc.return_int_reg() {
|
||||
Some(r) => r.id(),
|
||||
_ => 0xffff_ffff,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_return_hi_int_reg<C>(ctxt: *mut c_void) -> u32
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::return_hi_int_reg", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
|
||||
match ctxt.cc.return_hi_int_reg() {
|
||||
Some(r) => r.id(),
|
||||
_ => 0xffff_ffff,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_return_float_reg<C>(ctxt: *mut c_void) -> u32
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::return_float_reg", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
|
||||
match ctxt.cc.return_float_reg() {
|
||||
Some(r) => r.id(),
|
||||
_ => 0xffff_ffff,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_global_pointer_reg<C>(ctxt: *mut c_void) -> u32
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::global_pointer_reg", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
|
||||
match ctxt.cc.global_pointer_reg() {
|
||||
Some(r) => r.id(),
|
||||
_ => 0xffff_ffff,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_implicitly_defined_registers<C>(
|
||||
ctxt: *mut c_void,
|
||||
count: *mut usize,
|
||||
) -> *mut u32
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::implicitly_defined_registers", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
let regs = ctxt.cc.implicitly_defined_registers();
|
||||
|
||||
alloc_register_list(regs.iter().map(|r| r.id()), &mut *count)
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::extra_unused_type_parameters)] // TODO : This is bad; need to finish this stub
|
||||
extern "C" fn cb_incoming_reg_value<C>(
|
||||
_ctxt: *mut c_void,
|
||||
_reg: u32,
|
||||
_func: *mut BNFunction,
|
||||
val: *mut BNRegisterValue,
|
||||
) where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::incoming_reg_value", unsafe {
|
||||
//let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
let val = &mut *val;
|
||||
|
||||
val.state = BNRegisterValueType::EntryValue;
|
||||
val.value = _reg as i64;
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::extra_unused_type_parameters)] // TODO : This is bad; need to finish this stub
|
||||
extern "C" fn cb_incoming_flag_value<C>(
|
||||
_ctxt: *mut c_void,
|
||||
_flag: u32,
|
||||
_func: *mut BNFunction,
|
||||
val: *mut BNRegisterValue,
|
||||
) where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::incoming_flag_value", unsafe {
|
||||
//let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
let val = &mut *val;
|
||||
|
||||
val.state = BNRegisterValueType::EntryValue;
|
||||
val.value = _flag as i64;
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_incoming_var_for_param<C>(
|
||||
ctxt: *mut c_void,
|
||||
var: *const BNVariable,
|
||||
_func: *mut BNFunction,
|
||||
param: *mut BNVariable,
|
||||
) where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::incoming_var_for_param", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
ptr::write(
|
||||
param,
|
||||
BNGetDefaultIncomingVariableForParameterVariable(ctxt.raw_handle, var),
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_incoming_param_for_var<C>(
|
||||
ctxt: *mut c_void,
|
||||
var: *const BNVariable,
|
||||
_func: *mut BNFunction,
|
||||
param: *mut BNVariable,
|
||||
) where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::incoming_param_for_var", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
ptr::write(
|
||||
param,
|
||||
BNGetDefaultParameterVariableForIncomingVariable(ctxt.raw_handle, var),
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_are_argument_registers_used_for_var_args<C>(ctxt: *mut c_void) -> bool
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!(
|
||||
"CallingConvention::are_argument_registers_used_for_var_args",
|
||||
unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
|
||||
ctxt.cc.are_argument_registers_used_for_var_args()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let name = name.into_bytes_with_nul();
|
||||
let raw = Box::into_raw(Box::new(CustomCallingConventionContext {
|
||||
raw_handle: ptr::null_mut(),
|
||||
cc,
|
||||
}));
|
||||
let mut cc = BNCustomCallingConvention {
|
||||
context: raw as *mut _,
|
||||
|
||||
freeObject: Some(cb_free::<C>),
|
||||
|
||||
getCallerSavedRegisters: Some(cb_caller_saved::<C>),
|
||||
getCalleeSavedRegisters: Some(cb_callee_saved::<C>),
|
||||
getIntegerArgumentRegisters: Some(cb_int_args::<C>),
|
||||
getFloatArgumentRegisters: Some(cb_float_args::<C>),
|
||||
freeRegisterList: Some(cb_free_register_list),
|
||||
|
||||
areArgumentRegistersSharedIndex: Some(cb_arg_shared_index::<C>),
|
||||
isStackReservedForArgumentRegisters: Some(cb_stack_reserved_arg_regs::<C>),
|
||||
isStackAdjustedOnReturn: Some(cb_stack_adjusted_on_return::<C>),
|
||||
isEligibleForHeuristics: Some(cb_is_eligible_for_heuristics::<C>),
|
||||
|
||||
getIntegerReturnValueRegister: Some(cb_return_int_reg::<C>),
|
||||
getHighIntegerReturnValueRegister: Some(cb_return_hi_int_reg::<C>),
|
||||
getFloatReturnValueRegister: Some(cb_return_float_reg::<C>),
|
||||
getGlobalPointerRegister: Some(cb_global_pointer_reg::<C>),
|
||||
|
||||
getImplicitlyDefinedRegisters: Some(cb_implicitly_defined_registers::<C>),
|
||||
getIncomingRegisterValue: Some(cb_incoming_reg_value::<C>),
|
||||
getIncomingFlagValue: Some(cb_incoming_flag_value::<C>),
|
||||
getIncomingVariableForParameterVariable: Some(cb_incoming_var_for_param::<C>),
|
||||
getParameterVariableForIncomingVariable: Some(cb_incoming_param_for_var::<C>),
|
||||
|
||||
areArgumentRegistersUsedForVarArgs: Some(cb_are_argument_registers_used_for_var_args::<C>),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let cc_name = name.as_ref().as_ptr() as *mut _;
|
||||
let result = BNCreateCallingConvention(arch.as_ref().0, cc_name, &mut cc);
|
||||
|
||||
assert!(!result.is_null());
|
||||
|
||||
(*raw).raw_handle = result;
|
||||
|
||||
BNRegisterCallingConvention(arch.as_ref().0, result);
|
||||
|
||||
Ref::new(CallingConvention {
|
||||
handle: result,
|
||||
arch_handle: arch.handle(),
|
||||
_arch: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CallingConvention<A: Architecture> {
|
||||
pub(crate) handle: *mut BNCallingConvention,
|
||||
pub(crate) arch_handle: A::Handle,
|
||||
_arch: PhantomData<*mut A>,
|
||||
}
|
||||
|
||||
unsafe impl<A: Architecture> Send for CallingConvention<A> {}
|
||||
unsafe impl<A: Architecture> Sync for CallingConvention<A> {}
|
||||
|
||||
impl<A: Architecture> CallingConvention<A> {
|
||||
pub(crate) unsafe fn ref_from_raw(
|
||||
handle: *mut BNCallingConvention,
|
||||
arch: A::Handle,
|
||||
) -> Ref<Self> {
|
||||
Ref::new(CallingConvention {
|
||||
handle,
|
||||
arch_handle: arch,
|
||||
_arch: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn name(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNGetCallingConventionName(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn variables_for_parameters<S: Clone + BnStrCompatible>(
|
||||
&self,
|
||||
params: &[FunctionParameter<S>],
|
||||
permitted_registers: Option<&[A::Register]>,
|
||||
) -> Vec<Variable> {
|
||||
let mut bn_params: Vec<BNFunctionParameter> = vec![];
|
||||
let mut name_strings = vec![];
|
||||
|
||||
for parameter in params.iter() {
|
||||
name_strings.push(parameter.name.clone().into_bytes_with_nul());
|
||||
}
|
||||
for (parameter, raw_name) in params.iter().zip(name_strings.iter_mut()) {
|
||||
let location = match ¶meter.location {
|
||||
Some(location) => location.raw(),
|
||||
None => unsafe { mem::zeroed() },
|
||||
};
|
||||
bn_params.push(BNFunctionParameter {
|
||||
name: raw_name.as_ref().as_ptr() as *mut _,
|
||||
type_: parameter.t.contents.handle,
|
||||
typeConfidence: parameter.t.confidence,
|
||||
defaultLocation: parameter.location.is_none(),
|
||||
location,
|
||||
});
|
||||
}
|
||||
|
||||
let mut count: usize = 0;
|
||||
let vars: *mut BNVariable = if let Some(permitted_args) = permitted_registers {
|
||||
let mut permitted_regs = vec![];
|
||||
for r in permitted_args {
|
||||
permitted_regs.push(r.id());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
BNGetVariablesForParameters(
|
||||
self.handle,
|
||||
bn_params.as_ptr(),
|
||||
bn_params.len(),
|
||||
permitted_regs.as_ptr(),
|
||||
permitted_regs.len(),
|
||||
&mut count,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
BNGetVariablesForParametersDefaultPermittedArgs(
|
||||
self.handle,
|
||||
bn_params.as_ptr(),
|
||||
bn_params.len(),
|
||||
&mut count,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Gotta keep this around so the pointers are valid during the call
|
||||
drop(name_strings);
|
||||
|
||||
let vars_slice = unsafe { slice::from_raw_parts(vars, count) };
|
||||
let mut result = vec![];
|
||||
for var in vars_slice {
|
||||
result.push(unsafe { Variable::from_raw(*var) });
|
||||
}
|
||||
|
||||
unsafe { BNFreeVariableList(vars) };
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Architecture> Eq for CallingConvention<A> {}
|
||||
impl<A: Architecture> PartialEq for CallingConvention<A> {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
self.handle == rhs.handle
|
||||
}
|
||||
}
|
||||
|
||||
use crate::types::{FunctionParameter, Variable};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
impl<A: Architecture> Hash for CallingConvention<A> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.handle.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Architecture> CallingConventionBase for CallingConvention<A> {
|
||||
type Arch = A;
|
||||
|
||||
fn caller_saved_registers(&self) -> Vec<A::Register> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let regs = BNGetCallerSavedRegisters(self.handle, &mut count);
|
||||
let arch = self.arch_handle.borrow();
|
||||
|
||||
let res = slice::from_raw_parts(regs, count)
|
||||
.iter()
|
||||
.map(|&r| {
|
||||
arch.register_from_id(r)
|
||||
.expect("bad reg id from CallingConvention")
|
||||
})
|
||||
.collect();
|
||||
|
||||
BNFreeRegisterList(regs);
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn callee_saved_registers(&self) -> Vec<A::Register> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let regs = BNGetCalleeSavedRegisters(self.handle, &mut count);
|
||||
let arch = self.arch_handle.borrow();
|
||||
|
||||
let res = slice::from_raw_parts(regs, count)
|
||||
.iter()
|
||||
.map(|&r| {
|
||||
arch.register_from_id(r)
|
||||
.expect("bad reg id from CallingConvention")
|
||||
})
|
||||
.collect();
|
||||
|
||||
BNFreeRegisterList(regs);
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn int_arg_registers(&self) -> Vec<A::Register> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn float_arg_registers(&self) -> Vec<A::Register> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn arg_registers_shared_index(&self) -> bool {
|
||||
unsafe { BNAreArgumentRegistersSharedIndex(self.handle) }
|
||||
}
|
||||
|
||||
fn reserved_stack_space_for_arg_registers(&self) -> bool {
|
||||
unsafe { BNIsStackReservedForArgumentRegisters(self.handle) }
|
||||
}
|
||||
|
||||
fn stack_adjusted_on_return(&self) -> bool {
|
||||
unsafe { BNIsStackAdjustedOnReturn(self.handle) }
|
||||
}
|
||||
|
||||
fn is_eligible_for_heuristics(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn return_int_reg(&self) -> Option<A::Register> {
|
||||
match unsafe { BNGetIntegerReturnValueRegister(self.handle) } {
|
||||
id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn return_hi_int_reg(&self) -> Option<A::Register> {
|
||||
match unsafe { BNGetHighIntegerReturnValueRegister(self.handle) } {
|
||||
id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn return_float_reg(&self) -> Option<A::Register> {
|
||||
match unsafe { BNGetFloatReturnValueRegister(self.handle) } {
|
||||
id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn global_pointer_reg(&self) -> Option<A::Register> {
|
||||
match unsafe { BNGetGlobalPointerRegister(self.handle) } {
|
||||
id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn implicitly_defined_registers(&self) -> Vec<A::Register> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn are_argument_registers_used_for_var_args(&self) -> bool {
|
||||
unsafe { BNAreArgumentRegistersUsedForVarArgs(self.handle) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Architecture> ToOwned for CallingConvention<A> {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<A: Architecture> RefCountable for CallingConvention<A> {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewCallingConventionReference(handle.handle),
|
||||
arch_handle: handle.arch_handle.clone(),
|
||||
_arch: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeCallingConvention(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Architecture> CoreArrayProvider for CallingConvention<A> {
|
||||
type Raw = *mut BNCallingConvention;
|
||||
type Context = A::Handle;
|
||||
}
|
||||
|
||||
unsafe impl<A: Architecture> CoreOwnedArrayProvider for CallingConvention<A> {
|
||||
unsafe fn free(raw: *mut *mut BNCallingConvention, count: usize, _content: &Self::Context) {
|
||||
BNFreeCallingConventionList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, A: Architecture> CoreArrayWrapper<'a> for CallingConvention<A> {
|
||||
type Wrapped = Guard<'a, CallingConvention<A>>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
|
||||
Guard::new(
|
||||
CallingConvention {
|
||||
handle: *raw,
|
||||
arch_handle: context.clone(),
|
||||
_arch: Default::default(),
|
||||
},
|
||||
context,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for CallingConvention<CoreArchitecture> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "<cc: {} arch: {}>", self.name(), self.arch_handle.name())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConventionBuilder<A: Architecture> {
|
||||
caller_saved_registers: Vec<A::Register>,
|
||||
_callee_saved_registers: Vec<A::Register>,
|
||||
int_arg_registers: Vec<A::Register>,
|
||||
float_arg_registers: Vec<A::Register>,
|
||||
|
||||
arg_registers_shared_index: bool,
|
||||
reserved_stack_space_for_arg_registers: bool,
|
||||
stack_adjusted_on_return: bool,
|
||||
is_eligible_for_heuristics: bool,
|
||||
|
||||
return_int_reg: Option<A::Register>,
|
||||
return_hi_int_reg: Option<A::Register>,
|
||||
return_float_reg: Option<A::Register>,
|
||||
|
||||
global_pointer_reg: Option<A::Register>,
|
||||
|
||||
implicitly_defined_registers: Vec<A::Register>,
|
||||
|
||||
are_argument_registers_used_for_var_args: bool,
|
||||
|
||||
arch_handle: A::Handle,
|
||||
_arch: PhantomData<*const A>,
|
||||
}
|
||||
|
||||
unsafe impl<A: Architecture> Send for ConventionBuilder<A> {}
|
||||
unsafe impl<A: Architecture> Sync for ConventionBuilder<A> {}
|
||||
|
||||
macro_rules! bool_arg {
|
||||
($name:ident) => {
|
||||
pub fn $name(mut self, val: bool) -> Self {
|
||||
self.$name = val;
|
||||
self
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! reg_list {
|
||||
($name:ident) => {
|
||||
pub fn $name(mut self, regs: &[&str]) -> Self {
|
||||
{
|
||||
// FIXME NLL
|
||||
let arch = self.arch_handle.borrow();
|
||||
let arch_regs = regs.iter().filter_map(|&r| arch.register_by_name(r));
|
||||
|
||||
self.$name = arch_regs.collect();
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! reg {
|
||||
($name:ident) => {
|
||||
pub fn $name(mut self, reg: &str) -> Self {
|
||||
{
|
||||
// FIXME NLL
|
||||
let arch = self.arch_handle.borrow();
|
||||
self.$name = arch.register_by_name(reg);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<A: Architecture> ConventionBuilder<A> {
|
||||
pub fn new(arch: &A) -> Self {
|
||||
Self {
|
||||
caller_saved_registers: Vec::new(),
|
||||
_callee_saved_registers: Vec::new(),
|
||||
int_arg_registers: Vec::new(),
|
||||
float_arg_registers: Vec::new(),
|
||||
|
||||
arg_registers_shared_index: false,
|
||||
reserved_stack_space_for_arg_registers: false,
|
||||
stack_adjusted_on_return: false,
|
||||
is_eligible_for_heuristics: false,
|
||||
|
||||
return_int_reg: None,
|
||||
return_hi_int_reg: None,
|
||||
return_float_reg: None,
|
||||
|
||||
global_pointer_reg: None,
|
||||
|
||||
implicitly_defined_registers: Vec::new(),
|
||||
|
||||
are_argument_registers_used_for_var_args: false,
|
||||
|
||||
arch_handle: arch.handle(),
|
||||
_arch: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
reg_list!(caller_saved_registers);
|
||||
reg_list!(_callee_saved_registers);
|
||||
reg_list!(int_arg_registers);
|
||||
reg_list!(float_arg_registers);
|
||||
|
||||
bool_arg!(arg_registers_shared_index);
|
||||
bool_arg!(reserved_stack_space_for_arg_registers);
|
||||
bool_arg!(stack_adjusted_on_return);
|
||||
bool_arg!(is_eligible_for_heuristics);
|
||||
|
||||
reg!(return_int_reg);
|
||||
reg!(return_hi_int_reg);
|
||||
reg!(return_float_reg);
|
||||
|
||||
reg!(global_pointer_reg);
|
||||
|
||||
reg_list!(implicitly_defined_registers);
|
||||
|
||||
bool_arg!(are_argument_registers_used_for_var_args);
|
||||
|
||||
pub fn register(self, name: &str) -> Ref<CallingConvention<A>> {
|
||||
let arch = self.arch_handle.clone();
|
||||
|
||||
register_calling_convention(arch.borrow(), name, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Architecture> CallingConventionBase for ConventionBuilder<A> {
|
||||
type Arch = A;
|
||||
|
||||
fn caller_saved_registers(&self) -> Vec<A::Register> {
|
||||
self.caller_saved_registers.clone()
|
||||
}
|
||||
|
||||
fn callee_saved_registers(&self) -> Vec<A::Register> {
|
||||
self.caller_saved_registers.clone()
|
||||
}
|
||||
|
||||
fn int_arg_registers(&self) -> Vec<A::Register> {
|
||||
self.int_arg_registers.clone()
|
||||
}
|
||||
|
||||
fn float_arg_registers(&self) -> Vec<A::Register> {
|
||||
self.float_arg_registers.clone()
|
||||
}
|
||||
|
||||
fn arg_registers_shared_index(&self) -> bool {
|
||||
self.arg_registers_shared_index
|
||||
}
|
||||
|
||||
fn reserved_stack_space_for_arg_registers(&self) -> bool {
|
||||
self.reserved_stack_space_for_arg_registers
|
||||
}
|
||||
|
||||
fn stack_adjusted_on_return(&self) -> bool {
|
||||
self.stack_adjusted_on_return
|
||||
}
|
||||
|
||||
fn is_eligible_for_heuristics(&self) -> bool {
|
||||
self.is_eligible_for_heuristics
|
||||
}
|
||||
|
||||
fn return_int_reg(&self) -> Option<A::Register> {
|
||||
self.return_int_reg
|
||||
}
|
||||
|
||||
fn return_hi_int_reg(&self) -> Option<A::Register> {
|
||||
self.return_hi_int_reg
|
||||
}
|
||||
|
||||
fn return_float_reg(&self) -> Option<A::Register> {
|
||||
self.return_float_reg
|
||||
}
|
||||
|
||||
fn global_pointer_reg(&self) -> Option<A::Register> {
|
||||
self.global_pointer_reg
|
||||
}
|
||||
|
||||
fn implicitly_defined_registers(&self) -> Vec<A::Register> {
|
||||
self.implicitly_defined_registers.clone()
|
||||
}
|
||||
|
||||
fn are_argument_registers_used_for_var_args(&self) -> bool {
|
||||
self.are_argument_registers_used_for_var_args
|
||||
}
|
||||
}
|
||||
448
src/command.rs
Normal file
448
src/command.rs
Normal file
@@ -0,0 +1,448 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Provides commands for registering plugins and plugin actions.
|
||||
//!
|
||||
//! All plugins need to provide one of the following functions for Binary Ninja to call:
|
||||
//!
|
||||
//! ```rust
|
||||
//! pub extern "C" fn CorePluginInit() -> bool {}
|
||||
//! ```
|
||||
//!
|
||||
//! ```rust
|
||||
//! pub extern "C" fn UIPluginInit() -> bool {}
|
||||
//! ```
|
||||
//!
|
||||
//! Both of these functions can call any of the following registration functions, though `CorePluginInit` is called during Binary Ninja core initialization, and `UIPluginInit` is called during Binary Ninja UI initialization.
|
||||
//!
|
||||
//! The return value of these functions should indicate whether they successfully initialized themselves.
|
||||
|
||||
use binaryninjacore_sys::{
|
||||
BNBinaryView, BNFunction, BNRegisterPluginCommand, BNRegisterPluginCommandForAddress,
|
||||
BNRegisterPluginCommandForFunction, BNRegisterPluginCommandForRange,
|
||||
};
|
||||
|
||||
use std::ops::Range;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::binaryview::BinaryView;
|
||||
use crate::function::Function;
|
||||
use crate::string::BnStrCompatible;
|
||||
|
||||
/// The trait required for generic commands. See [register] for example usage.
|
||||
pub trait Command: 'static + Sync {
|
||||
fn action(&self, view: &BinaryView);
|
||||
fn valid(&self, view: &BinaryView) -> bool;
|
||||
}
|
||||
|
||||
impl<T> Command for T
|
||||
where
|
||||
T: 'static + Sync + Fn(&BinaryView),
|
||||
{
|
||||
fn action(&self, view: &BinaryView) {
|
||||
self(view);
|
||||
}
|
||||
|
||||
fn valid(&self, _view: &BinaryView) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// struct MyCommand;
|
||||
///
|
||||
/// impl Command for MyCommand {
|
||||
/// fn action(&self, view: &BinaryView) {
|
||||
/// // Your code here
|
||||
/// }
|
||||
///
|
||||
/// fn valid(&self, view: &BinaryView) -> bool {
|
||||
/// // Your code here
|
||||
/// true
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[no_mangle]
|
||||
/// pub extern "C" fn CorePluginInit() -> bool {
|
||||
/// register(
|
||||
/// "My Plugin Command",
|
||||
/// "A description of my command",
|
||||
/// MyCommand {},
|
||||
/// );
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
pub fn register<S, C>(name: S, desc: S, command: C)
|
||||
where
|
||||
S: BnStrCompatible,
|
||||
C: Command,
|
||||
{
|
||||
extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView)
|
||||
where
|
||||
C: Command,
|
||||
{
|
||||
ffi_wrap!("Command::action", unsafe {
|
||||
let cmd = &*(ctxt as *const C);
|
||||
|
||||
debug_assert!(!view.is_null());
|
||||
let view = BinaryView { handle: view };
|
||||
|
||||
cmd.action(&view);
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView) -> bool
|
||||
where
|
||||
C: Command,
|
||||
{
|
||||
ffi_wrap!("Command::valid", unsafe {
|
||||
let cmd = &*(ctxt as *const C);
|
||||
|
||||
debug_assert!(!view.is_null());
|
||||
let view = BinaryView { handle: view };
|
||||
|
||||
cmd.valid(&view)
|
||||
})
|
||||
}
|
||||
|
||||
let name = name.into_bytes_with_nul();
|
||||
let desc = desc.into_bytes_with_nul();
|
||||
|
||||
let name_ptr = name.as_ref().as_ptr() as *mut _;
|
||||
let desc_ptr = desc.as_ref().as_ptr() as *mut _;
|
||||
|
||||
let ctxt = Box::into_raw(Box::new(command));
|
||||
|
||||
unsafe {
|
||||
BNRegisterPluginCommand(
|
||||
name_ptr,
|
||||
desc_ptr,
|
||||
Some(cb_action::<C>),
|
||||
Some(cb_valid::<C>),
|
||||
ctxt as *mut _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The trait required for address-associated commands. See [register_for_address] for example usage.
|
||||
pub trait AddressCommand: 'static + Sync {
|
||||
fn action(&self, view: &BinaryView, addr: u64);
|
||||
fn valid(&self, view: &BinaryView, addr: u64) -> bool;
|
||||
}
|
||||
|
||||
impl<T> AddressCommand for T
|
||||
where
|
||||
T: 'static + Sync + Fn(&BinaryView, u64),
|
||||
{
|
||||
fn action(&self, view: &BinaryView, addr: u64) {
|
||||
self(view, addr);
|
||||
}
|
||||
|
||||
fn valid(&self, _view: &BinaryView, _addr: u64) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// struct MyCommand;
|
||||
///
|
||||
/// impl AddressCommand for MyCommand {
|
||||
/// fn action(&self, view: &BinaryView, addr: u64) {
|
||||
/// // Your code here
|
||||
/// }
|
||||
///
|
||||
/// fn valid(&self, view: &BinaryView, addr: u64) -> bool {
|
||||
/// // Your code here
|
||||
/// true
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[no_mangle]
|
||||
/// pub extern "C" fn CorePluginInit() -> bool {
|
||||
/// register_for_address(
|
||||
/// "My Plugin Command",
|
||||
/// "A description of my command",
|
||||
/// MyCommand {},
|
||||
/// );
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
pub fn register_for_address<S, C>(name: S, desc: S, command: C)
|
||||
where
|
||||
S: BnStrCompatible,
|
||||
C: AddressCommand,
|
||||
{
|
||||
extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64)
|
||||
where
|
||||
C: AddressCommand,
|
||||
{
|
||||
ffi_wrap!("AddressCommand::action", unsafe {
|
||||
let cmd = &*(ctxt as *const C);
|
||||
|
||||
debug_assert!(!view.is_null());
|
||||
let view = BinaryView { handle: view };
|
||||
|
||||
cmd.action(&view, addr);
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64) -> bool
|
||||
where
|
||||
C: AddressCommand,
|
||||
{
|
||||
ffi_wrap!("AddressCommand::valid", unsafe {
|
||||
let cmd = &*(ctxt as *const C);
|
||||
|
||||
debug_assert!(!view.is_null());
|
||||
let view = BinaryView { handle: view };
|
||||
|
||||
cmd.valid(&view, addr)
|
||||
})
|
||||
}
|
||||
|
||||
let name = name.into_bytes_with_nul();
|
||||
let desc = desc.into_bytes_with_nul();
|
||||
|
||||
let name_ptr = name.as_ref().as_ptr() as *mut _;
|
||||
let desc_ptr = desc.as_ref().as_ptr() as *mut _;
|
||||
|
||||
let ctxt = Box::into_raw(Box::new(command));
|
||||
|
||||
unsafe {
|
||||
BNRegisterPluginCommandForAddress(
|
||||
name_ptr,
|
||||
desc_ptr,
|
||||
Some(cb_action::<C>),
|
||||
Some(cb_valid::<C>),
|
||||
ctxt as *mut _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The trait required for range-associated commands. See [register_for_range] for example usage.
|
||||
pub trait RangeCommand: 'static + Sync {
|
||||
fn action(&self, view: &BinaryView, range: Range<u64>);
|
||||
fn valid(&self, view: &BinaryView, range: Range<u64>) -> bool;
|
||||
}
|
||||
|
||||
impl<T> RangeCommand for T
|
||||
where
|
||||
T: 'static + Sync + Fn(&BinaryView, Range<u64>),
|
||||
{
|
||||
fn action(&self, view: &BinaryView, range: Range<u64>) {
|
||||
self(view, range);
|
||||
}
|
||||
|
||||
fn valid(&self, _view: &BinaryView, _range: Range<u64>) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// struct MyCommand;
|
||||
///
|
||||
/// impl AddressCommand for MyCommand {
|
||||
/// fn action(&self, view: &BinaryView, range: Range<u64>) {
|
||||
/// // Your code here
|
||||
/// }
|
||||
///
|
||||
/// fn valid(&self, view: &BinaryView, range: Range<u64>) -> bool {
|
||||
/// // Your code here
|
||||
/// true
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[no_mangle]
|
||||
/// pub extern "C" fn CorePluginInit() -> bool {
|
||||
/// register_for_range(
|
||||
/// "My Plugin Command",
|
||||
/// "A description of my command",
|
||||
/// MyCommand {},
|
||||
/// );
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
pub fn register_for_range<S, C>(name: S, desc: S, command: C)
|
||||
where
|
||||
S: BnStrCompatible,
|
||||
C: RangeCommand,
|
||||
{
|
||||
extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64, len: u64)
|
||||
where
|
||||
C: RangeCommand,
|
||||
{
|
||||
ffi_wrap!("RangeCommand::action", unsafe {
|
||||
let cmd = &*(ctxt as *const C);
|
||||
|
||||
debug_assert!(!view.is_null());
|
||||
let view = BinaryView { handle: view };
|
||||
|
||||
cmd.action(&view, addr..addr.wrapping_add(len));
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_valid<C>(
|
||||
ctxt: *mut c_void,
|
||||
view: *mut BNBinaryView,
|
||||
addr: u64,
|
||||
len: u64,
|
||||
) -> bool
|
||||
where
|
||||
C: RangeCommand,
|
||||
{
|
||||
ffi_wrap!("RangeCommand::valid", unsafe {
|
||||
let cmd = &*(ctxt as *const C);
|
||||
|
||||
debug_assert!(!view.is_null());
|
||||
let view = BinaryView { handle: view };
|
||||
|
||||
cmd.valid(&view, addr..addr.wrapping_add(len))
|
||||
})
|
||||
}
|
||||
|
||||
let name = name.into_bytes_with_nul();
|
||||
let desc = desc.into_bytes_with_nul();
|
||||
|
||||
let name_ptr = name.as_ref().as_ptr() as *mut _;
|
||||
let desc_ptr = desc.as_ref().as_ptr() as *mut _;
|
||||
|
||||
let ctxt = Box::into_raw(Box::new(command));
|
||||
|
||||
unsafe {
|
||||
BNRegisterPluginCommandForRange(
|
||||
name_ptr,
|
||||
desc_ptr,
|
||||
Some(cb_action::<C>),
|
||||
Some(cb_valid::<C>),
|
||||
ctxt as *mut _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The trait required for function-associated commands. See [register_for_function] for example usage.
|
||||
pub trait FunctionCommand: 'static + Sync {
|
||||
fn action(&self, view: &BinaryView, func: &Function);
|
||||
fn valid(&self, view: &BinaryView, func: &Function) -> bool;
|
||||
}
|
||||
|
||||
impl<T> FunctionCommand for T
|
||||
where
|
||||
T: 'static + Sync + Fn(&BinaryView, &Function),
|
||||
{
|
||||
fn action(&self, view: &BinaryView, func: &Function) {
|
||||
self(view, func);
|
||||
}
|
||||
|
||||
fn valid(&self, _view: &BinaryView, _func: &Function) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// struct MyCommand;
|
||||
///
|
||||
/// impl AddressCommand for MyCommand {
|
||||
/// fn action(&self, view: &BinaryView, func: &Function) {
|
||||
/// // Your code here
|
||||
/// }
|
||||
///
|
||||
/// fn valid(&self, view: &BinaryView, func: &Function) -> bool {
|
||||
/// // Your code here
|
||||
/// true
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[no_mangle]
|
||||
/// pub extern "C" fn CorePluginInit() -> bool {
|
||||
/// register_for_function(
|
||||
/// "My Plugin Command",
|
||||
/// "A description of my command",
|
||||
/// MyCommand {},
|
||||
/// );
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
pub fn register_for_function<S, C>(name: S, desc: S, command: C)
|
||||
where
|
||||
S: BnStrCompatible,
|
||||
C: FunctionCommand,
|
||||
{
|
||||
extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, func: *mut BNFunction)
|
||||
where
|
||||
C: FunctionCommand,
|
||||
{
|
||||
ffi_wrap!("FunctionCommand::action", unsafe {
|
||||
let cmd = &*(ctxt as *const C);
|
||||
|
||||
debug_assert!(!view.is_null());
|
||||
let view = BinaryView { handle: view };
|
||||
|
||||
debug_assert!(!func.is_null());
|
||||
let func = Function { handle: func };
|
||||
|
||||
cmd.action(&view, &func);
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_valid<C>(
|
||||
ctxt: *mut c_void,
|
||||
view: *mut BNBinaryView,
|
||||
func: *mut BNFunction,
|
||||
) -> bool
|
||||
where
|
||||
C: FunctionCommand,
|
||||
{
|
||||
ffi_wrap!("FunctionCommand::valid", unsafe {
|
||||
let cmd = &*(ctxt as *const C);
|
||||
|
||||
debug_assert!(!view.is_null());
|
||||
let view = BinaryView { handle: view };
|
||||
|
||||
debug_assert!(!func.is_null());
|
||||
let func = Function { handle: func };
|
||||
|
||||
cmd.valid(&view, &func)
|
||||
})
|
||||
}
|
||||
|
||||
let name = name.into_bytes_with_nul();
|
||||
let desc = desc.into_bytes_with_nul();
|
||||
|
||||
let name_ptr = name.as_ref().as_ptr() as *mut _;
|
||||
let desc_ptr = desc.as_ref().as_ptr() as *mut _;
|
||||
|
||||
let ctxt = Box::into_raw(Box::new(command));
|
||||
|
||||
unsafe {
|
||||
BNRegisterPluginCommandForFunction(
|
||||
name_ptr,
|
||||
desc_ptr,
|
||||
Some(cb_action::<C>),
|
||||
Some(cb_valid::<C>),
|
||||
ctxt as *mut _,
|
||||
);
|
||||
}
|
||||
}
|
||||
807
src/custombinaryview.rs
Normal file
807
src/custombinaryview.rs
Normal file
@@ -0,0 +1,807 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! An interface for providing your own [BinaryView]s to Binary Ninja.
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use crate::architecture::Architecture;
|
||||
use crate::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt, Result};
|
||||
use crate::platform::Platform;
|
||||
use crate::settings::Settings;
|
||||
use crate::Endianness;
|
||||
|
||||
use crate::rc::*;
|
||||
use crate::string::*;
|
||||
|
||||
/// Registers a custom `BinaryViewType` with the core.
|
||||
///
|
||||
/// The `constructor` argument is called immediately after successful registration of the type with
|
||||
/// the core. The `BinaryViewType` argument passed to `constructor` is the object that the
|
||||
/// `AsRef<BinaryViewType>`
|
||||
/// implementation of the `CustomBinaryViewType` must return.
|
||||
pub fn register_view_type<S, T, F>(name: S, long_name: S, constructor: F) -> &'static T
|
||||
where
|
||||
S: BnStrCompatible,
|
||||
T: CustomBinaryViewType,
|
||||
F: FnOnce(BinaryViewType) -> T,
|
||||
{
|
||||
extern "C" fn cb_valid<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> bool
|
||||
where
|
||||
T: CustomBinaryViewType,
|
||||
{
|
||||
ffi_wrap!("BinaryViewTypeBase::is_valid_for", unsafe {
|
||||
let view_type = &*(ctxt as *mut T);
|
||||
let data = BinaryView::from_raw(BNNewViewReference(data));
|
||||
|
||||
view_type.is_valid_for(&data)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_deprecated<T>(ctxt: *mut c_void) -> bool
|
||||
where
|
||||
T: CustomBinaryViewType,
|
||||
{
|
||||
ffi_wrap!("BinaryViewTypeBase::is_deprecated", unsafe {
|
||||
let view_type = &*(ctxt as *mut T);
|
||||
view_type.is_deprecated()
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_create<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView
|
||||
where
|
||||
T: CustomBinaryViewType,
|
||||
{
|
||||
ffi_wrap!("BinaryViewTypeBase::create", unsafe {
|
||||
let view_type = &*(ctxt as *mut T);
|
||||
let data = BinaryView::from_raw(BNNewViewReference(data));
|
||||
|
||||
let builder = CustomViewBuilder {
|
||||
view_type,
|
||||
actual_parent: &data,
|
||||
};
|
||||
|
||||
if let Ok(bv) = view_type.create_custom_view(&data, builder) {
|
||||
// force a leak of the Ref; failure to do this would result
|
||||
// in the refcount going to 0 in the process of returning it
|
||||
// to the core -- we're transferring ownership of the Ref here
|
||||
Ref::into_raw(bv.handle).handle
|
||||
} else {
|
||||
error!("CustomBinaryViewType::create_custom_view returned Err");
|
||||
|
||||
ptr::null_mut()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::extra_unused_type_parameters)] // TODO : This is bad; need to finish this stub
|
||||
extern "C" fn cb_parse<T>(_ctxt: *mut c_void, _data: *mut BNBinaryView) -> *mut BNBinaryView
|
||||
where
|
||||
T: CustomBinaryViewType,
|
||||
{
|
||||
ffi_wrap!("BinaryViewTypeBase::parse", ptr::null_mut())
|
||||
}
|
||||
|
||||
extern "C" fn cb_load_settings<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNSettings
|
||||
where
|
||||
T: CustomBinaryViewType,
|
||||
{
|
||||
ffi_wrap!("BinaryViewTypeBase::load_settings", unsafe {
|
||||
let view_type = &*(ctxt as *mut T);
|
||||
let data = BinaryView::from_raw(BNNewViewReference(data));
|
||||
|
||||
match view_type.load_settings_for_data(&data) {
|
||||
Ok(settings) => Ref::into_raw(settings).handle,
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let name = name.into_bytes_with_nul();
|
||||
let name_ptr = name.as_ref().as_ptr() as *mut _;
|
||||
|
||||
let long_name = long_name.into_bytes_with_nul();
|
||||
let long_name_ptr = long_name.as_ref().as_ptr() as *mut _;
|
||||
|
||||
let ctxt = Box::new(unsafe { mem::zeroed() });
|
||||
let ctxt = Box::into_raw(ctxt);
|
||||
|
||||
let mut bn_obj = BNCustomBinaryViewType {
|
||||
context: ctxt as *mut _,
|
||||
create: Some(cb_create::<T>),
|
||||
parse: Some(cb_parse::<T>),
|
||||
isValidForData: Some(cb_valid::<T>),
|
||||
isDeprecated: Some(cb_deprecated::<T>),
|
||||
getLoadSettingsForData: Some(cb_load_settings::<T>),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let res = BNRegisterBinaryViewType(name_ptr, long_name_ptr, &mut bn_obj as *mut _);
|
||||
|
||||
if res.is_null() {
|
||||
// avoid leaking the space allocated for the type, but also
|
||||
// avoid running its Drop impl (if any -- not that there should
|
||||
// be one since view types live for the life of the process)
|
||||
mem::forget(*Box::from_raw(ctxt));
|
||||
|
||||
panic!("bvt registration failed");
|
||||
}
|
||||
|
||||
ptr::write(ctxt, constructor(BinaryViewType(res)));
|
||||
|
||||
&*ctxt
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BinaryViewTypeBase: AsRef<BinaryViewType> {
|
||||
fn is_valid_for(&self, data: &BinaryView) -> bool;
|
||||
|
||||
fn is_deprecated(&self) -> bool;
|
||||
|
||||
fn default_load_settings_for_data(&self, data: &BinaryView) -> Result<Ref<Settings>> {
|
||||
let settings_handle =
|
||||
unsafe { BNGetBinaryViewDefaultLoadSettingsForData(self.as_ref().0, data.handle) };
|
||||
|
||||
if settings_handle.is_null() {
|
||||
Err(())
|
||||
} else {
|
||||
unsafe { Ok(Settings::from_raw(settings_handle)) }
|
||||
}
|
||||
}
|
||||
|
||||
fn load_settings_for_data(&self, data: &BinaryView) -> Result<Ref<Settings>> {
|
||||
let settings_handle =
|
||||
unsafe { BNGetBinaryViewLoadSettingsForData(self.as_ref().0, data.handle) };
|
||||
|
||||
if settings_handle.is_null() {
|
||||
Err(())
|
||||
} else {
|
||||
unsafe { Ok(Settings::from_raw(settings_handle)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BinaryViewTypeExt: BinaryViewTypeBase {
|
||||
fn name(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNGetBinaryViewTypeName(self.as_ref().0)) }
|
||||
}
|
||||
|
||||
fn long_name(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNGetBinaryViewTypeLongName(self.as_ref().0)) }
|
||||
}
|
||||
|
||||
fn register_arch<A: Architecture>(&self, id: u32, endianness: Endianness, arch: &A) {
|
||||
unsafe {
|
||||
BNRegisterArchitectureForViewType(self.as_ref().0, id, endianness, arch.as_ref().0);
|
||||
}
|
||||
}
|
||||
|
||||
fn register_platform(&self, id: u32, plat: &Platform) {
|
||||
let arch = plat.arch();
|
||||
|
||||
unsafe {
|
||||
BNRegisterPlatformForViewType(self.as_ref().0, id, arch.0, plat.handle);
|
||||
}
|
||||
}
|
||||
|
||||
fn open(&self, data: &BinaryView) -> Result<Ref<BinaryView>> {
|
||||
let handle = unsafe { BNCreateBinaryViewOfType(self.as_ref().0, data.handle) };
|
||||
|
||||
if handle.is_null() {
|
||||
error!(
|
||||
"failed to create BinaryView of BinaryViewType '{}'",
|
||||
self.name()
|
||||
);
|
||||
return Err(());
|
||||
}
|
||||
|
||||
unsafe { Ok(BinaryView::from_raw(handle)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BinaryViewTypeBase> BinaryViewTypeExt for T {}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct BinaryViewType(pub *mut BNBinaryViewType);
|
||||
|
||||
impl BinaryViewType {
|
||||
pub fn list_all() -> Array<BinaryViewType> {
|
||||
unsafe {
|
||||
let mut count: usize = 0;
|
||||
let types = BNGetBinaryViewTypes(&mut count as *mut _);
|
||||
|
||||
Array::new(types, count, ())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_valid_types_for(data: &BinaryView) -> Array<BinaryViewType> {
|
||||
unsafe {
|
||||
let mut count: usize = 0;
|
||||
let types = BNGetBinaryViewTypesForData(data.handle, &mut count as *mut _);
|
||||
|
||||
Array::new(types, count, ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks up a BinaryViewType by its short name
|
||||
pub fn by_name<N: BnStrCompatible>(name: N) -> Result<Self> {
|
||||
let bytes = name.into_bytes_with_nul();
|
||||
|
||||
let res = unsafe { BNGetBinaryViewTypeByName(bytes.as_ref().as_ptr() as *const _) };
|
||||
|
||||
match res.is_null() {
|
||||
false => Ok(BinaryViewType(res)),
|
||||
true => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BinaryViewTypeBase for BinaryViewType {
|
||||
fn is_valid_for(&self, data: &BinaryView) -> bool {
|
||||
unsafe { BNIsBinaryViewTypeValidForData(self.0, data.handle) }
|
||||
}
|
||||
|
||||
fn is_deprecated(&self) -> bool {
|
||||
unsafe { BNIsBinaryViewTypeDeprecated(self.0) }
|
||||
}
|
||||
|
||||
fn default_load_settings_for_data(&self, data: &BinaryView) -> Result<Ref<Settings>> {
|
||||
let settings_handle =
|
||||
unsafe { BNGetBinaryViewDefaultLoadSettingsForData(self.0, data.handle) };
|
||||
|
||||
if settings_handle.is_null() {
|
||||
Err(())
|
||||
} else {
|
||||
unsafe { Ok(Settings::from_raw(settings_handle)) }
|
||||
}
|
||||
}
|
||||
|
||||
fn load_settings_for_data(&self, data: &BinaryView) -> Result<Ref<Settings>> {
|
||||
let settings_handle = unsafe { BNGetBinaryViewLoadSettingsForData(self.0, data.handle) };
|
||||
|
||||
if settings_handle.is_null() {
|
||||
Err(())
|
||||
} else {
|
||||
unsafe { Ok(Settings::from_raw(settings_handle)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for BinaryViewType {
|
||||
type Raw = *mut BNBinaryViewType;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for BinaryViewType {
|
||||
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
|
||||
BNFreeBinaryViewTypeList(raw);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for BinaryViewType {
|
||||
type Wrapped = BinaryViewType;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
BinaryViewType(*raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<BinaryViewType> for BinaryViewType {
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for BinaryViewType {}
|
||||
unsafe impl Sync for BinaryViewType {}
|
||||
|
||||
pub trait CustomBinaryViewType: 'static + BinaryViewTypeBase + Sync {
|
||||
fn create_custom_view<'builder>(
|
||||
&self,
|
||||
data: &BinaryView,
|
||||
builder: CustomViewBuilder<'builder, Self>,
|
||||
) -> Result<CustomView<'builder>>;
|
||||
}
|
||||
|
||||
/// Represents a request from the core to instantiate a custom BinaryView
|
||||
pub struct CustomViewBuilder<'a, T: CustomBinaryViewType + ?Sized> {
|
||||
view_type: &'a T,
|
||||
actual_parent: &'a BinaryView,
|
||||
}
|
||||
|
||||
pub unsafe trait CustomBinaryView: 'static + BinaryViewBase + Sync + Sized {
|
||||
type Args: Send;
|
||||
|
||||
fn new(handle: &BinaryView, args: &Self::Args) -> Result<Self>;
|
||||
fn init(&self, args: Self::Args) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Represents a partially initialized custom `BinaryView` that should be returned to the core
|
||||
/// from the `create_custom_view` method of a `CustomBinaryViewType`.
|
||||
#[must_use]
|
||||
pub struct CustomView<'builder> {
|
||||
// this object can't actually be treated like a real
|
||||
// BinaryView as it isn't fully initialized until the
|
||||
// core receives it from the BNCustomBinaryViewType::create
|
||||
// callback.
|
||||
handle: Ref<BinaryView>,
|
||||
_builder: PhantomData<&'builder ()>,
|
||||
}
|
||||
|
||||
impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
|
||||
/// Begins creating a custom BinaryView.
|
||||
///
|
||||
/// This function may only be called from the `create_custom_view` function of a
|
||||
/// `CustomBinaryViewType`.
|
||||
///
|
||||
/// `parent` specifies the view that the core will treat as the parent view, that
|
||||
/// Segments created against the created view will be backed by `parent`. It will
|
||||
/// usually be (but is not required to be) the `data` argument of the `create_custom_view`
|
||||
/// callback.
|
||||
///
|
||||
/// `constructor` will not be called until well after the value returned by this function
|
||||
/// has been returned by `create_custom_view` callback to the core, and may not ever
|
||||
/// be called if the value returned by this function is dropped or leaked.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will fail if the `FileMetadata` object associated with the *expected* parent
|
||||
/// (i.e., the `data` argument passed to the `create_custom_view` function) already has an
|
||||
/// associated `BinaryView` of the same `CustomBinaryViewType`. Multiple `BinaryView` objects
|
||||
/// of the same `BinaryViewType` belonging to the same `FileMetadata` object is prohibited and
|
||||
/// can cause strange, delayed segmentation faults.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `constructor` should avoid doing anything with the object it returns, especially anything
|
||||
/// that would cause the core to invoke any of the `BinaryViewBase` methods. The core isn't
|
||||
/// going to consider the object fully initialized until after that callback has run.
|
||||
///
|
||||
/// The `BinaryView` argument passed to the constructor function is the object that is expected
|
||||
/// to be returned by the `AsRef<BinaryView>` implementation required by the `BinaryViewBase` trait.
|
||||
/// TODO FIXME welp this is broke going to need 2 init callbacks
|
||||
pub fn create<V>(self, parent: &BinaryView, view_args: V::Args) -> Result<CustomView<'a>>
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
let file = self.actual_parent.file();
|
||||
let view_type = self.view_type;
|
||||
|
||||
let view_name = view_type.name();
|
||||
|
||||
if let Ok(bv) = file.get_view_of_type(view_name.as_cstr()) {
|
||||
// while it seems to work most of the time, you can get really unlucky
|
||||
// if the a free of the existing view of the same type kicks off while
|
||||
// BNCreateBinaryViewOfType is still running. the freeObject callback
|
||||
// will run for the new view before we've even finished initializing,
|
||||
// and that's all she wrote.
|
||||
//
|
||||
// even if we deal with it gracefully in cb_free_object,
|
||||
// BNCreateBinaryViewOfType is still going to crash, so we're just
|
||||
// going to try and stop this from happening in the first place.
|
||||
error!(
|
||||
"attempt to create duplicate view of type '{}' (existing: {:?})",
|
||||
view_name.as_str(),
|
||||
bv.handle
|
||||
);
|
||||
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// wildly unsafe struct representing the context of a BNCustomBinaryView
|
||||
// this type should *never* be allowed to drop as the fields are in varying
|
||||
// states of uninitialized/already consumed throughout the life of the object.
|
||||
struct CustomViewContext<V>
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
view: mem::MaybeUninit<V>,
|
||||
raw_handle: *mut BNBinaryView,
|
||||
initialized: bool,
|
||||
args: V::Args,
|
||||
}
|
||||
|
||||
extern "C" fn cb_init<V>(ctxt: *mut c_void) -> bool
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::init", unsafe {
|
||||
let context = &mut *(ctxt as *mut CustomViewContext<V>);
|
||||
let handle = BinaryView::from_raw(context.raw_handle);
|
||||
|
||||
match V::new(handle.as_ref(), &context.args) {
|
||||
Ok(v) => {
|
||||
ptr::write(&mut context.view, mem::MaybeUninit::new(v));
|
||||
context.initialized = true;
|
||||
|
||||
match context
|
||||
.view
|
||||
.assume_init_ref()
|
||||
.init(ptr::read(&context.args))
|
||||
{
|
||||
Ok(_) => true,
|
||||
Err(_) => {
|
||||
error!("CustomBinaryView::init failed; custom view returned Err");
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
error!("CustomBinaryView::new failed; custom view returned Err");
|
||||
false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_free_object<V>(ctxt: *mut c_void)
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::freeObject", unsafe {
|
||||
let context = ctxt as *mut CustomViewContext<V>;
|
||||
let context = *Box::from_raw(context);
|
||||
|
||||
if context.initialized {
|
||||
mem::forget(context.args); // already consumed
|
||||
// mem::drop(context.view); // cb_init was called
|
||||
} else {
|
||||
mem::drop(context.args); // never consumed
|
||||
// mem::forget(context.view); // cb_init was not called, is uninit
|
||||
|
||||
if context.raw_handle.is_null() {
|
||||
// being called here is essentially a guarantee that BNCreateBinaryViewOfType
|
||||
// is above above us on the call stack somewhere -- no matter what we do, a crash
|
||||
// is pretty much certain at this point.
|
||||
//
|
||||
// this has been observed when two views of the same BinaryViewType are created
|
||||
// against the same BNFileMetaData object, and one of the views gets freed while
|
||||
// the second one is being initialized -- somehow the partially initialized one
|
||||
// gets freed before BNCreateBinaryViewOfType returns.
|
||||
//
|
||||
// multiples views of the same BinaryViewType in a BNFileMetaData object are
|
||||
// prohibited, so an API contract was violated in order to get here.
|
||||
//
|
||||
// if we're here, it's too late to do anything about it, though we can at least not
|
||||
// run the destructor on the custom view since that memory is unitialized.
|
||||
error!(
|
||||
"BinaryViewBase::freeObject called on partially initialized object! crash imminent!"
|
||||
);
|
||||
} else if !context.initialized {
|
||||
// making it here means somebody went out of their way to leak a BinaryView
|
||||
// after calling BNCreateCustomView and never gave the BNBinaryView handle
|
||||
// to the core (which would have called cb_init)
|
||||
//
|
||||
// the result is a half-initialized BinaryView that the core will happily hand out
|
||||
// references to via BNGetFileViewofType even though it was never initialized
|
||||
// all the way.
|
||||
//
|
||||
// TODO update when this corner case gets fixed in the core?
|
||||
//
|
||||
// we can't do anything to prevent this, but we can at least have the crash
|
||||
// not be our fault.
|
||||
error!("BinaryViewBase::freeObject called on leaked/never initialized custom view!");
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_read<V>(
|
||||
ctxt: *mut c_void,
|
||||
dest: *mut c_void,
|
||||
offset: u64,
|
||||
len: usize,
|
||||
) -> usize
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::read", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
let dest = slice::from_raw_parts_mut(dest as *mut u8, len);
|
||||
|
||||
context.view.assume_init_ref().read(dest, offset)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_write<V>(
|
||||
ctxt: *mut c_void,
|
||||
offset: u64,
|
||||
src: *const c_void,
|
||||
len: usize,
|
||||
) -> usize
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::write", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
let src = slice::from_raw_parts(src as *const u8, len);
|
||||
|
||||
context.view.assume_init_ref().write(offset, src)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_insert<V>(
|
||||
ctxt: *mut c_void,
|
||||
offset: u64,
|
||||
src: *const c_void,
|
||||
len: usize,
|
||||
) -> usize
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::insert", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
let src = slice::from_raw_parts(src as *const u8, len);
|
||||
|
||||
context.view.assume_init_ref().insert(offset, src)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_remove<V>(ctxt: *mut c_void, offset: u64, len: u64) -> usize
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::remove", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().remove(offset, len as usize)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_modification<V>(ctxt: *mut c_void, offset: u64) -> ModificationStatus
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::modification_status", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().modification_status(offset)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_offset_valid<V>(ctxt: *mut c_void, offset: u64) -> bool
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::offset_valid", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().offset_valid(offset)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_offset_readable<V>(ctxt: *mut c_void, offset: u64) -> bool
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::readable", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().offset_readable(offset)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_offset_writable<V>(ctxt: *mut c_void, offset: u64) -> bool
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::writable", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().offset_writable(offset)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_offset_executable<V>(ctxt: *mut c_void, offset: u64) -> bool
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::offset_executable", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().offset_executable(offset)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_offset_backed_by_file<V>(ctxt: *mut c_void, offset: u64) -> bool
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::offset_backed_by_file", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().offset_backed_by_file(offset)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_next_valid_offset<V>(ctxt: *mut c_void, offset: u64) -> u64
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::next_valid_offset_after", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context
|
||||
.view
|
||||
.assume_init_ref()
|
||||
.next_valid_offset_after(offset)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_start<V>(ctxt: *mut c_void) -> u64
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::start", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().start()
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_length<V>(ctxt: *mut c_void) -> u64
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::len", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().len() as u64
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_entry_point<V>(ctxt: *mut c_void) -> u64
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::entry_point", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().entry_point()
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_executable<V>(ctxt: *mut c_void) -> bool
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::executable", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().executable()
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_endianness<V>(ctxt: *mut c_void) -> Endianness
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::default_endianness", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().default_endianness()
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_relocatable<V>(ctxt: *mut c_void) -> bool
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::relocatable", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().relocatable()
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_address_size<V>(ctxt: *mut c_void) -> usize
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::address_size", unsafe {
|
||||
let context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
|
||||
context.view.assume_init_ref().address_size()
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_save<V>(ctxt: *mut c_void, _fa: *mut BNFileAccessor) -> bool
|
||||
where
|
||||
V: CustomBinaryView,
|
||||
{
|
||||
ffi_wrap!("BinaryViewBase::save", unsafe {
|
||||
let _context = &*(ctxt as *mut CustomViewContext<V>);
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
let ctxt = Box::new(CustomViewContext::<V> {
|
||||
view: mem::MaybeUninit::uninit(),
|
||||
raw_handle: ptr::null_mut(),
|
||||
initialized: false,
|
||||
args: view_args,
|
||||
});
|
||||
|
||||
let ctxt = Box::into_raw(ctxt);
|
||||
|
||||
let mut bn_obj = BNCustomBinaryView {
|
||||
context: ctxt as *mut _,
|
||||
init: Some(cb_init::<V>),
|
||||
freeObject: Some(cb_free_object::<V>),
|
||||
externalRefTaken: None,
|
||||
externalRefReleased: None,
|
||||
read: Some(cb_read::<V>),
|
||||
write: Some(cb_write::<V>),
|
||||
insert: Some(cb_insert::<V>),
|
||||
remove: Some(cb_remove::<V>),
|
||||
getModification: Some(cb_modification::<V>),
|
||||
isValidOffset: Some(cb_offset_valid::<V>),
|
||||
isOffsetReadable: Some(cb_offset_readable::<V>),
|
||||
isOffsetWritable: Some(cb_offset_writable::<V>),
|
||||
isOffsetExecutable: Some(cb_offset_executable::<V>),
|
||||
isOffsetBackedByFile: Some(cb_offset_backed_by_file::<V>),
|
||||
getNextValidOffset: Some(cb_next_valid_offset::<V>),
|
||||
getStart: Some(cb_start::<V>),
|
||||
getLength: Some(cb_length::<V>),
|
||||
getEntryPoint: Some(cb_entry_point::<V>),
|
||||
isExecutable: Some(cb_executable::<V>),
|
||||
getDefaultEndianness: Some(cb_endianness::<V>),
|
||||
isRelocatable: Some(cb_relocatable::<V>),
|
||||
getAddressSize: Some(cb_address_size::<V>),
|
||||
save: Some(cb_save::<V>),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let res = BNCreateCustomBinaryView(
|
||||
view_name.as_cstr().as_ptr(),
|
||||
file.handle,
|
||||
parent.handle,
|
||||
&mut bn_obj,
|
||||
);
|
||||
|
||||
if res.is_null() {
|
||||
// TODO not sure when this can even happen, let alone what we're supposed to do about
|
||||
// it. cb_init isn't normally called until later, and cb_free_object definitely won't
|
||||
// have been called, so we'd at least be on the hook for freeing that stuff...
|
||||
// probably.
|
||||
//
|
||||
// no idea how to force this to fail so I can test this, so just going to do the
|
||||
// reasonable thing and panic.
|
||||
panic!("failed to create custom binary view!");
|
||||
}
|
||||
|
||||
(*ctxt).raw_handle = res;
|
||||
|
||||
Ok(CustomView {
|
||||
handle: BinaryView::from_raw(res),
|
||||
_builder: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wrap_existing(self, wrapped_view: Ref<BinaryView>) -> Result<CustomView<'a>> {
|
||||
Ok(CustomView {
|
||||
handle: wrapped_view,
|
||||
_builder: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
95
src/databuffer.rs
Normal file
95
src/databuffer.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! A basic wrapper around an array of binary data
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use std::ffi::c_void;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
pub struct DataBuffer(*mut BNDataBuffer);
|
||||
|
||||
impl DataBuffer {
|
||||
pub(crate) fn from_raw(raw: *mut BNDataBuffer) -> Self {
|
||||
DataBuffer(raw)
|
||||
}
|
||||
pub(crate) fn as_raw(&self) -> *mut BNDataBuffer {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn get_data(&self) -> &[u8] {
|
||||
if self.0.is_null() {
|
||||
// TODO : Change the default value and remove this
|
||||
return &[];
|
||||
}
|
||||
let buffer = unsafe { BNGetDataBufferContents(self.0) };
|
||||
if buffer.is_null() {
|
||||
&[]
|
||||
} else {
|
||||
unsafe { slice::from_raw_parts(buffer as *const _, self.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_data(&mut self, data: &[u8]) {
|
||||
unsafe {
|
||||
BNSetDataBufferContents(
|
||||
self.0,
|
||||
data.as_ptr() as *const c_void as *mut c_void,
|
||||
data.len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { BNGetDataBufferLength(self.0) }
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
unsafe { BNGetDataBufferLength(self.0) == 0 }
|
||||
}
|
||||
|
||||
pub fn new(data: &[u8]) -> Result<Self, ()> {
|
||||
let buffer = unsafe { BNCreateDataBuffer(data.as_ptr() as *const c_void, data.len()) };
|
||||
if buffer.is_null() {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(DataBuffer::from_raw(buffer))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : delete this
|
||||
impl Default for DataBuffer {
|
||||
fn default() -> Self {
|
||||
DataBuffer::from_raw(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DataBuffer {
|
||||
fn drop(&mut self) {
|
||||
if !self.0.is_null() {
|
||||
unsafe {
|
||||
BNFreeDataBuffer(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for DataBuffer {
|
||||
fn clone(&self) -> Self {
|
||||
Self::from_raw(unsafe { BNDuplicateDataBuffer(self.0) })
|
||||
}
|
||||
}
|
||||
897
src/debuginfo.rs
Normal file
897
src/debuginfo.rs
Normal file
@@ -0,0 +1,897 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// TODO : These docs are here, but could afford to be cleaned up
|
||||
|
||||
//! Parsers and providers of debug information to Binary Ninja.
|
||||
//!
|
||||
//! The debug information is used by Binary Ninja as ground-truth information about the attributes of functions,
|
||||
//! types, and variables that Binary Ninja's analysis pipeline would otherwise work to deduce. By providing
|
||||
//! debug info, Binary Ninja's output can be generated quicker, more accurately, and more completely.
|
||||
//!
|
||||
//! A DebugInfoParser consists of:
|
||||
//! 1. A name
|
||||
//! 2. An `is_valid` function which takes a BV and returns a bool
|
||||
//! 3. A `parse` function which takes a `DebugInfo` object and uses the member functions `add_type`, `add_function`, and `add_data_variable` to populate all the info it can.
|
||||
//! And finally calling `binaryninja::debuginfo::DebugInfoParser::register` to register it with the core.
|
||||
//!
|
||||
//! Here's a minimal, complete example boilerplate-plugin:
|
||||
//! ```
|
||||
//! use binaryninja::{
|
||||
//! binaryview::BinaryView,
|
||||
//! debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser},
|
||||
//! };
|
||||
//!
|
||||
//! struct ExampleDebugInfoParser;
|
||||
//!
|
||||
//! impl CustomDebugInfoParser for ExampleDebugInfoParser {
|
||||
//! fn is_valid(&self, _view: &BinaryView) -> bool {
|
||||
//! true
|
||||
//! }
|
||||
//!
|
||||
//! fn parse_info(&self, _debug_info: &mut DebugInfo, _view: &BinaryView, _debug_file: &BinaryView, _progress: Box<dyn Fn(usize, usize) -> bool>) {
|
||||
//! println!("Parsing info");
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! #[no_mangle]
|
||||
//! pub extern "C" fn CorePluginInit() -> bool {
|
||||
//! DebugInfoParser::register("example debug info parser", ExampleDebugInfoParser {});
|
||||
//! true
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! `DebugInfo` will then be automatically applied to binary views that contain debug information (via the setting `analysis.debugInfo.internal`), binary views that provide valid external debug info files (`analysis.debugInfo.external`), or manually fetched/applied as below:
|
||||
//! ```
|
||||
//! let valid_parsers = DebugInfoParser::parsers_for_view(bv);
|
||||
//! let parser = valid_parsers[0];
|
||||
//! let debug_info = parser.parse_debug_info(bv);
|
||||
//! bv.apply_debug_info(debug_info);
|
||||
//! ```
|
||||
//!
|
||||
//! Multiple debug-info parsers can manually contribute debug info for a binary view by simply calling `parse_debug_info` with the
|
||||
//! `DebugInfo` object just returned. This is automatic when opening a binary view with multiple valid debug info parsers. If you
|
||||
//! wish to set the debug info for a binary view without applying it as well, you can call `binaryninja::binaryview::BinaryView::set_debug_info`.
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::{
|
||||
binaryview::BinaryView,
|
||||
platform::Platform,
|
||||
rc::*,
|
||||
string::{raw_to_string, BnStrCompatible, BnString},
|
||||
types::{DataVariableAndName, NameAndType, Type},
|
||||
};
|
||||
|
||||
use std::{hash::Hash, mem, os::raw::c_void, ptr, slice};
|
||||
|
||||
struct ProgressContext(Option<Box<dyn Fn(usize, usize) -> Result<(), ()>>>);
|
||||
|
||||
//////////////////////
|
||||
// DebugInfoParser
|
||||
|
||||
/// Represents the registered parsers and providers of debug information to Binary Ninja.
|
||||
/// See `binaryninja::debuginfo` for more information
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct DebugInfoParser {
|
||||
pub(crate) handle: *mut BNDebugInfoParser,
|
||||
}
|
||||
|
||||
impl DebugInfoParser {
|
||||
pub(crate) unsafe fn from_raw(handle: *mut BNDebugInfoParser) -> Ref<Self> {
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Ref::new(Self { handle })
|
||||
}
|
||||
|
||||
/// Returns debug info parser of the given name, if it exists
|
||||
pub fn from_name<S: BnStrCompatible>(name: S) -> Result<Ref<Self>, ()> {
|
||||
let name = name.into_bytes_with_nul();
|
||||
let parser = unsafe { BNGetDebugInfoParserByName(name.as_ref().as_ptr() as *mut _) };
|
||||
|
||||
if parser.is_null() {
|
||||
Err(())
|
||||
} else {
|
||||
unsafe { Ok(Self::from_raw(parser)) }
|
||||
}
|
||||
}
|
||||
|
||||
/// List all debug-info parsers
|
||||
pub fn list() -> Array<DebugInfoParser> {
|
||||
let mut count: usize = unsafe { mem::zeroed() };
|
||||
let raw_parsers = unsafe { BNGetDebugInfoParsers(&mut count as *mut _) };
|
||||
unsafe { Array::new(raw_parsers, count, ()) }
|
||||
}
|
||||
|
||||
/// Returns a list of debug-info parsers that are valid for the provided binary view
|
||||
pub fn parsers_for_view(bv: &BinaryView) -> Array<DebugInfoParser> {
|
||||
let mut count: usize = unsafe { mem::zeroed() };
|
||||
let raw_parsers = unsafe { BNGetDebugInfoParsersForView(bv.handle, &mut count as *mut _) };
|
||||
unsafe { Array::new(raw_parsers, count, ()) }
|
||||
}
|
||||
|
||||
/// Returns the name of the current parser
|
||||
pub fn name(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNGetDebugInfoParserName(self.handle)) }
|
||||
}
|
||||
|
||||
/// Returns whether this debug-info parser is valid for the provided binary view
|
||||
pub fn is_valid_for_view(&self, view: &BinaryView) -> bool {
|
||||
unsafe { BNIsDebugInfoParserValidForView(self.handle, view.handle) }
|
||||
}
|
||||
|
||||
extern "C" fn cb_progress(ctxt: *mut c_void, cur: usize, max: usize) -> bool {
|
||||
ffi_wrap!("DebugInfoParser::cb_progress", unsafe {
|
||||
let progress = ctxt as *mut ProgressContext;
|
||||
match &(*progress).0 {
|
||||
Some(func) => (func)(cur, max).is_ok(),
|
||||
None => true,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a `DebugInfo` object populated with debug info by this debug-info parser. Only provide a `DebugInfo` object if you wish to append to the existing debug info
|
||||
pub fn parse_debug_info(
|
||||
&self,
|
||||
view: &BinaryView,
|
||||
debug_file: &BinaryView,
|
||||
existing_debug_info: Option<&DebugInfo>,
|
||||
progress: Option<Box<dyn Fn(usize, usize) -> Result<(), ()>>>,
|
||||
) -> Option<Ref<DebugInfo>> {
|
||||
let mut progress_raw = ProgressContext(progress);
|
||||
let info: *mut BNDebugInfo = match existing_debug_info {
|
||||
Some(debug_info) => unsafe {
|
||||
BNParseDebugInfo(
|
||||
self.handle,
|
||||
view.handle,
|
||||
debug_file.handle,
|
||||
debug_info.handle,
|
||||
Some(Self::cb_progress),
|
||||
&mut progress_raw as *mut _ as *mut c_void,
|
||||
)
|
||||
},
|
||||
None => unsafe {
|
||||
BNParseDebugInfo(
|
||||
self.handle,
|
||||
view.handle,
|
||||
debug_file.handle,
|
||||
ptr::null_mut(),
|
||||
Some(Self::cb_progress),
|
||||
&mut progress_raw as *mut _ as *mut c_void,
|
||||
)
|
||||
},
|
||||
};
|
||||
if info.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe { DebugInfo::from_raw(info) })
|
||||
}
|
||||
|
||||
// Registers a DebugInfoParser. See `binaryninja::debuginfo::DebugInfoParser` for more details.
|
||||
pub fn register<S, C>(name: S, parser_callbacks: C) -> Ref<Self>
|
||||
where
|
||||
S: BnStrCompatible,
|
||||
C: CustomDebugInfoParser,
|
||||
{
|
||||
extern "C" fn cb_is_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView) -> bool
|
||||
where
|
||||
C: CustomDebugInfoParser,
|
||||
{
|
||||
ffi_wrap!("CustomDebugInfoParser::is_valid", unsafe {
|
||||
let cmd = &*(ctxt as *const C);
|
||||
let view = BinaryView::from_raw(view);
|
||||
|
||||
cmd.is_valid(&view)
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_parse_info<C>(
|
||||
ctxt: *mut c_void,
|
||||
debug_info: *mut BNDebugInfo,
|
||||
view: *mut BNBinaryView,
|
||||
debug_file: *mut BNBinaryView,
|
||||
progress: Option<unsafe extern "C" fn(*mut c_void, usize, usize) -> bool>,
|
||||
progress_ctxt: *mut c_void,
|
||||
) -> bool
|
||||
where
|
||||
C: CustomDebugInfoParser,
|
||||
{
|
||||
ffi_wrap!("CustomDebugInfoParser::parse_info", unsafe {
|
||||
let cmd = &*(ctxt as *const C);
|
||||
let view = BinaryView::from_raw(view);
|
||||
let debug_file = BinaryView::from_raw(debug_file);
|
||||
let mut debug_info = DebugInfo::from_raw(debug_info);
|
||||
|
||||
cmd.parse_info(
|
||||
&mut debug_info,
|
||||
&view,
|
||||
&debug_file,
|
||||
Box::new(move |cur: usize, max: usize| match progress {
|
||||
Some(func) => {
|
||||
if func(progress_ctxt, cur, max) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
_ => Ok(()),
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
let name = name.into_bytes_with_nul();
|
||||
let name_ptr = name.as_ref().as_ptr() as *mut _;
|
||||
let ctxt = Box::into_raw(Box::new(parser_callbacks));
|
||||
|
||||
unsafe {
|
||||
DebugInfoParser::from_raw(BNRegisterDebugInfoParser(
|
||||
name_ptr,
|
||||
Some(cb_is_valid::<C>),
|
||||
Some(cb_parse_info::<C>),
|
||||
ctxt as *mut _,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for DebugInfoParser {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewDebugInfoParserReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeDebugInfoParserReference(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for DebugInfoParser {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for DebugInfoParser {
|
||||
type Raw = *mut BNDebugInfoParser;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for DebugInfoParser {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _: &Self::Context) {
|
||||
BNFreeDebugInfoParserList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// DebugFunctionInfo
|
||||
|
||||
/// Collates ground-truth function-external attributes for use in BinaryNinja's internal analysis.
|
||||
///
|
||||
/// When contributing function info, provide only what you know - BinaryNinja will figure out everything else that it can, as it usually does.
|
||||
///
|
||||
/// Functions will not be created if an address is not provided, but will be able to be queried from debug info for later user analysis.
|
||||
pub struct DebugFunctionInfo {
|
||||
short_name: Option<String>,
|
||||
full_name: Option<String>,
|
||||
raw_name: Option<String>,
|
||||
type_: Option<Ref<Type>>,
|
||||
address: u64,
|
||||
platform: Option<Ref<Platform>>,
|
||||
components: Vec<String>,
|
||||
}
|
||||
|
||||
impl From<&BNDebugFunctionInfo> for DebugFunctionInfo {
|
||||
fn from(raw: &BNDebugFunctionInfo) -> Self {
|
||||
let components = unsafe { slice::from_raw_parts(raw.components, raw.componentN) }
|
||||
.iter()
|
||||
.map(|component| raw_to_string(*component as *const _).unwrap())
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
short_name: raw_to_string(raw.shortName),
|
||||
full_name: raw_to_string(raw.fullName),
|
||||
raw_name: raw_to_string(raw.rawName),
|
||||
type_: if raw.type_.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { Type::ref_from_raw(raw.type_) })
|
||||
},
|
||||
address: raw.address,
|
||||
platform: if raw.platform.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { Platform::ref_from_raw(raw.platform) })
|
||||
},
|
||||
components,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugFunctionInfo {
|
||||
pub fn new(
|
||||
short_name: Option<String>,
|
||||
full_name: Option<String>,
|
||||
raw_name: Option<String>,
|
||||
type_: Option<Ref<Type>>,
|
||||
address: Option<u64>,
|
||||
platform: Option<Ref<Platform>>,
|
||||
components: Vec<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
short_name,
|
||||
full_name,
|
||||
raw_name,
|
||||
type_,
|
||||
address: match address {
|
||||
Some(address) => address,
|
||||
_ => 0,
|
||||
},
|
||||
platform,
|
||||
components,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////
|
||||
// DebugInfo
|
||||
|
||||
/// Provides an interface to both provide and query debug info. The DebugInfo object is used
|
||||
/// internally by the binary view to which it is applied to determine the attributes of functions, types, and variables
|
||||
/// that would otherwise be costly to deduce.
|
||||
///
|
||||
/// DebugInfo objects themselves are independent of binary views; their data can be sourced from any arbitrary binary
|
||||
/// views and be applied to any other arbitrary binary view. A DebugInfo object can also contain debug info from multiple
|
||||
/// DebugInfoParsers. This makes it possible to gather debug info that may be distributed across several different
|
||||
/// formats and files.
|
||||
///
|
||||
/// DebugInfo cannot be instantiated by the user, instead get it from either the binary view (see `binaryninja::binaryview::BinaryView::debug_info`)
|
||||
/// or a debug-info parser (see `binaryninja::debuginfo::DebugInfoParser::parse_debug_info`).
|
||||
///
|
||||
/// Please note that calling one of `add_*` functions will not work outside of a debuginfo plugin.
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct DebugInfo {
|
||||
pub(crate) handle: *mut BNDebugInfo,
|
||||
}
|
||||
|
||||
impl DebugInfo {
|
||||
pub(crate) unsafe fn from_raw(handle: *mut BNDebugInfo) -> Ref<Self> {
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Ref::new(Self { handle })
|
||||
}
|
||||
|
||||
/// Returns a generator of all types provided by a named DebugInfoParser
|
||||
pub fn types_by_name<S: BnStrCompatible>(&self, parser_name: S) -> Vec<NameAndType<String>> {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
|
||||
let mut count: usize = 0;
|
||||
let debug_types_ptr = unsafe {
|
||||
BNGetDebugTypes(
|
||||
self.handle,
|
||||
parser_name.as_ref().as_ptr() as *mut _,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
let result: Vec<NameAndType<String>> = unsafe {
|
||||
slice::from_raw_parts_mut(debug_types_ptr, count)
|
||||
.iter()
|
||||
.map(NameAndType::<String>::from_raw)
|
||||
.collect()
|
||||
};
|
||||
|
||||
unsafe { BNFreeDebugTypes(debug_types_ptr, count) };
|
||||
result
|
||||
}
|
||||
|
||||
/// A generator of all types provided by DebugInfoParsers
|
||||
pub fn types(&self) -> Vec<NameAndType<String>> {
|
||||
let mut count: usize = 0;
|
||||
let debug_types_ptr = unsafe { BNGetDebugTypes(self.handle, ptr::null_mut(), &mut count) };
|
||||
let result: Vec<NameAndType<String>> = unsafe {
|
||||
slice::from_raw_parts_mut(debug_types_ptr, count)
|
||||
.iter()
|
||||
.map(NameAndType::<String>::from_raw)
|
||||
.collect()
|
||||
};
|
||||
|
||||
unsafe { BNFreeDebugTypes(debug_types_ptr, count) };
|
||||
result
|
||||
}
|
||||
|
||||
/// Returns a generator of all functions provided by a named DebugInfoParser
|
||||
pub fn functions_by_name<S: BnStrCompatible>(
|
||||
&self,
|
||||
parser_name: S,
|
||||
) -> Vec<DebugFunctionInfo> {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
|
||||
let mut count: usize = 0;
|
||||
let functions_ptr = unsafe {
|
||||
BNGetDebugFunctions(
|
||||
self.handle,
|
||||
parser_name.as_ref().as_ptr() as *mut _,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
|
||||
let result: Vec<DebugFunctionInfo> = unsafe {
|
||||
slice::from_raw_parts_mut(functions_ptr, count)
|
||||
.iter()
|
||||
.map(DebugFunctionInfo::from)
|
||||
.collect()
|
||||
};
|
||||
|
||||
unsafe { BNFreeDebugFunctions(functions_ptr, count) };
|
||||
result
|
||||
}
|
||||
|
||||
/// A generator of all functions provided by DebugInfoParsers
|
||||
pub fn functions(&self) -> Vec<DebugFunctionInfo> {
|
||||
let mut count: usize = 0;
|
||||
let functions_ptr =
|
||||
unsafe { BNGetDebugFunctions(self.handle, ptr::null_mut(), &mut count) };
|
||||
|
||||
let result: Vec<DebugFunctionInfo> = unsafe {
|
||||
slice::from_raw_parts_mut(functions_ptr, count)
|
||||
.iter()
|
||||
.map(DebugFunctionInfo::from)
|
||||
.collect()
|
||||
};
|
||||
|
||||
unsafe { BNFreeDebugFunctions(functions_ptr, count) };
|
||||
result
|
||||
}
|
||||
|
||||
/// Returns a generator of all data variables provided by a named DebugInfoParser
|
||||
pub fn data_variables_by_name<S: BnStrCompatible>(
|
||||
&self,
|
||||
parser_name: S,
|
||||
) -> Vec<DataVariableAndName<String>> {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
|
||||
let mut count: usize = 0;
|
||||
let data_variables_ptr = unsafe {
|
||||
BNGetDebugDataVariables(
|
||||
self.handle,
|
||||
parser_name.as_ref().as_ptr() as *mut _,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
|
||||
let result: Vec<DataVariableAndName<String>> = unsafe {
|
||||
slice::from_raw_parts_mut(data_variables_ptr, count)
|
||||
.iter()
|
||||
.map(DataVariableAndName::<String>::from_raw)
|
||||
.collect()
|
||||
};
|
||||
|
||||
unsafe { BNFreeDataVariablesAndName(data_variables_ptr, count) };
|
||||
result
|
||||
}
|
||||
|
||||
/// A generator of all data variables provided by DebugInfoParsers
|
||||
pub fn data_variables(&self) -> Vec<DataVariableAndName<String>> {
|
||||
let mut count: usize = 0;
|
||||
let data_variables_ptr =
|
||||
unsafe { BNGetDebugDataVariables(self.handle, ptr::null_mut(), &mut count) };
|
||||
|
||||
let result: Vec<DataVariableAndName<String>> = unsafe {
|
||||
slice::from_raw_parts_mut(data_variables_ptr, count)
|
||||
.iter()
|
||||
.map(DataVariableAndName::<String>::from_raw)
|
||||
.collect()
|
||||
};
|
||||
|
||||
unsafe { BNFreeDataVariablesAndName(data_variables_ptr, count) };
|
||||
result
|
||||
}
|
||||
|
||||
/// May return nullptr
|
||||
pub fn type_by_name<S: BnStrCompatible>(&self, parser_name: S, name: S) -> Option<Ref<Type>> {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
let name = name.into_bytes_with_nul();
|
||||
|
||||
let result = unsafe {
|
||||
BNGetDebugTypeByName(
|
||||
self.handle,
|
||||
parser_name.as_ref().as_ptr() as *mut _,
|
||||
name.as_ref().as_ptr() as *mut _,
|
||||
)
|
||||
};
|
||||
if !result.is_null() {
|
||||
Some(unsafe { Type::ref_from_raw(result) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_data_variable_by_name<S: BnStrCompatible>(
|
||||
&self,
|
||||
parser_name: S,
|
||||
name: S,
|
||||
) -> Option<(u64, Ref<Type>)> {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
let name = name.into_bytes_with_nul();
|
||||
|
||||
let result = unsafe {
|
||||
BNGetDebugDataVariableByName(
|
||||
self.handle,
|
||||
parser_name.as_ref().as_ptr() as *mut _,
|
||||
name.as_ref().as_ptr() as *mut _,
|
||||
)
|
||||
};
|
||||
|
||||
if !result.is_null() {
|
||||
unsafe { BNFreeString((*result).name) };
|
||||
Some(unsafe { ((*result).address, Type::ref_from_raw((*result).type_)) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_data_variable_by_address<S: BnStrCompatible>(
|
||||
&self,
|
||||
parser_name: S,
|
||||
address: u64,
|
||||
) -> Option<(String, Ref<Type>)> {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
let name_and_var = unsafe {
|
||||
BNGetDebugDataVariableByAddress(
|
||||
self.handle,
|
||||
parser_name.as_ref().as_ptr() as *mut _,
|
||||
address,
|
||||
)
|
||||
};
|
||||
|
||||
if !name_and_var.is_null() {
|
||||
let result = unsafe {
|
||||
(
|
||||
raw_to_string((*name_and_var).name).unwrap(),
|
||||
Type::ref_from_raw((*name_and_var).type_),
|
||||
)
|
||||
};
|
||||
unsafe { BNFreeString((*name_and_var).name) };
|
||||
Some(result)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// The tuple is (DebugInfoParserName, type)
|
||||
pub fn get_types_by_name<S: BnStrCompatible>(&self, name: S) -> Vec<(String, Ref<Type>)> {
|
||||
let name = name.into_bytes_with_nul();
|
||||
|
||||
let mut count: usize = 0;
|
||||
let raw_names_and_types = unsafe {
|
||||
BNGetDebugTypesByName(self.handle, name.as_ref().as_ptr() as *mut _, &mut count)
|
||||
};
|
||||
|
||||
let names_and_types: &[*mut BNNameAndType] =
|
||||
unsafe { slice::from_raw_parts(raw_names_and_types as *mut _, count) };
|
||||
|
||||
let result = names_and_types
|
||||
.iter()
|
||||
.take(count)
|
||||
.map(|&name_and_type| unsafe {
|
||||
(
|
||||
raw_to_string((*name_and_type).name).unwrap(),
|
||||
Type::ref_from_raw(BNNewTypeReference((*name_and_type).type_)),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
unsafe { BNFreeNameAndTypeList(raw_names_and_types, count) };
|
||||
result
|
||||
}
|
||||
|
||||
// The tuple is (DebugInfoParserName, address, type)
|
||||
pub fn get_data_variables_by_name<S: BnStrCompatible>(
|
||||
&self,
|
||||
name: S,
|
||||
) -> Vec<(String, u64, Ref<Type>)> {
|
||||
let name = name.into_bytes_with_nul();
|
||||
|
||||
let mut count: usize = 0;
|
||||
let raw_variables_and_names = unsafe {
|
||||
BNGetDebugDataVariablesByName(self.handle, name.as_ref().as_ptr() as *mut _, &mut count)
|
||||
};
|
||||
|
||||
let variables_and_names: &[*mut BNDataVariableAndName] =
|
||||
unsafe { slice::from_raw_parts(raw_variables_and_names as *mut _, count) };
|
||||
|
||||
let result = variables_and_names
|
||||
.iter()
|
||||
.take(count)
|
||||
.map(|&variable_and_name| unsafe {
|
||||
(
|
||||
raw_to_string((*variable_and_name).name).unwrap(),
|
||||
(*variable_and_name).address,
|
||||
Type::ref_from_raw(BNNewTypeReference((*variable_and_name).type_)),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
unsafe { BNFreeDataVariablesAndName(raw_variables_and_names, count) };
|
||||
result
|
||||
}
|
||||
|
||||
/// The tuple is (DebugInfoParserName, TypeName, type)
|
||||
pub fn get_data_variables_by_address(&self, address: u64) -> Vec<(String, String, Ref<Type>)> {
|
||||
let mut count: usize = 0;
|
||||
let raw_variables_and_names =
|
||||
unsafe { BNGetDebugDataVariablesByAddress(self.handle, address, &mut count) };
|
||||
|
||||
let variables_and_names: &[*mut BNDataVariableAndNameAndDebugParser] =
|
||||
unsafe { slice::from_raw_parts(raw_variables_and_names as *mut _, count) };
|
||||
|
||||
let result = variables_and_names
|
||||
.iter()
|
||||
.take(count)
|
||||
.map(|&variable_and_name| unsafe {
|
||||
(
|
||||
raw_to_string((*variable_and_name).parser).unwrap(),
|
||||
raw_to_string((*variable_and_name).name).unwrap(),
|
||||
Type::ref_from_raw(BNNewTypeReference((*variable_and_name).type_)),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
unsafe { BNFreeDataVariableAndNameAndDebugParserList(raw_variables_and_names, count) };
|
||||
result
|
||||
}
|
||||
|
||||
pub fn remove_parser_info<S: BnStrCompatible>(&self, parser_name: S) -> bool {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
|
||||
unsafe { BNRemoveDebugParserInfo(self.handle, parser_name.as_ref().as_ptr() as *mut _) }
|
||||
}
|
||||
|
||||
pub fn remove_parser_types<S: BnStrCompatible>(&self, parser_name: S) -> bool {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
|
||||
unsafe { BNRemoveDebugParserTypes(self.handle, parser_name.as_ref().as_ptr() as *mut _) }
|
||||
}
|
||||
|
||||
pub fn remove_parser_functions<S: BnStrCompatible>(&self, parser_name: S) -> bool {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
BNRemoveDebugParserFunctions(self.handle, parser_name.as_ref().as_ptr() as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_parser_data_variables<S: BnStrCompatible>(&self, parser_name: S) -> bool {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
BNRemoveDebugParserDataVariables(self.handle, parser_name.as_ref().as_ptr() as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_type_by_name<S: BnStrCompatible>(&self, parser_name: S, name: S) -> bool {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
let name = name.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
BNRemoveDebugTypeByName(
|
||||
self.handle,
|
||||
parser_name.as_ref().as_ptr() as *mut _,
|
||||
name.as_ref().as_ptr() as *mut _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_function_by_index<S: BnStrCompatible>(
|
||||
&self,
|
||||
parser_name: S,
|
||||
index: usize,
|
||||
) -> bool {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
BNRemoveDebugFunctionByIndex(
|
||||
self.handle,
|
||||
parser_name.as_ref().as_ptr() as *mut _,
|
||||
index,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_data_variable_by_address<S: BnStrCompatible>(
|
||||
&self,
|
||||
parser_name: S,
|
||||
address: u64,
|
||||
) -> bool {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
BNRemoveDebugDataVariableByAddress(
|
||||
self.handle,
|
||||
parser_name.as_ref().as_ptr() as *mut _,
|
||||
address,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a type scoped under the current parser's name to the debug info
|
||||
pub fn add_type<S: BnStrCompatible>(
|
||||
&self,
|
||||
name: S,
|
||||
new_type: &Type,
|
||||
components: &[&str],
|
||||
) -> bool {
|
||||
let mut components_array: Vec<*const ::std::os::raw::c_char> =
|
||||
Vec::with_capacity(components.len());
|
||||
for component in components {
|
||||
components_array.push(component.as_ptr() as _);
|
||||
}
|
||||
|
||||
let name = name.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNAddDebugType(
|
||||
self.handle,
|
||||
name.as_ref().as_ptr() as *mut _,
|
||||
new_type.handle,
|
||||
components_array.as_ptr() as _,
|
||||
components.len(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a function scoped under the current parser's name to the debug info
|
||||
pub fn add_function(&self, new_func: DebugFunctionInfo) -> bool {
|
||||
let short_name_bytes = new_func.short_name.map(|name| name.into_bytes_with_nul());
|
||||
let short_name = short_name_bytes
|
||||
.as_ref()
|
||||
.map_or(ptr::null_mut() as *mut _, |name| {
|
||||
name.as_ptr() as _
|
||||
});
|
||||
let full_name_bytes = new_func.full_name.map(|name| name.into_bytes_with_nul());
|
||||
let full_name = full_name_bytes
|
||||
.as_ref()
|
||||
.map_or(ptr::null_mut() as *mut _, |name| {
|
||||
name.as_ptr() as _
|
||||
});
|
||||
let raw_name_bytes = new_func.raw_name.map(|name| name.into_bytes_with_nul());
|
||||
let raw_name = raw_name_bytes
|
||||
.as_ref()
|
||||
.map_or(ptr::null_mut() as *mut _, |name| {
|
||||
name.as_ptr() as _
|
||||
});
|
||||
|
||||
let mut components_array: Vec<*const ::std::os::raw::c_char> =
|
||||
Vec::with_capacity(new_func.components.len());
|
||||
for component in &new_func.components {
|
||||
components_array.push(component.as_ptr() as _);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
BNAddDebugFunction(
|
||||
self.handle,
|
||||
&mut BNDebugFunctionInfo {
|
||||
shortName: short_name,
|
||||
fullName: full_name,
|
||||
rawName: raw_name,
|
||||
address: new_func.address,
|
||||
type_: match new_func.type_ {
|
||||
Some(type_) => type_.handle,
|
||||
_ => ptr::null_mut(),
|
||||
},
|
||||
platform: match new_func.platform {
|
||||
Some(platform) => platform.handle,
|
||||
_ => ptr::null_mut(),
|
||||
},
|
||||
components: components_array.as_ptr() as _,
|
||||
componentN: new_func.components.len(),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a data variable scoped under the current parser's name to the debug info
|
||||
pub fn add_data_variable<S: BnStrCompatible>(
|
||||
&self,
|
||||
address: u64,
|
||||
t: &Type,
|
||||
name: Option<S>,
|
||||
components: &[&str],
|
||||
) -> bool {
|
||||
let mut components_array: Vec<*const ::std::os::raw::c_char> =
|
||||
Vec::with_capacity(components.len());
|
||||
for component in components {
|
||||
components_array.push(component.as_ptr() as _);
|
||||
}
|
||||
|
||||
match name {
|
||||
Some(name) => {
|
||||
let name = name.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNAddDebugDataVariable(
|
||||
self.handle,
|
||||
address,
|
||||
t.handle,
|
||||
name.as_ref().as_ptr() as *mut _,
|
||||
components.as_ptr() as _,
|
||||
components.len(),
|
||||
)
|
||||
}
|
||||
}
|
||||
None => unsafe {
|
||||
BNAddDebugDataVariable(
|
||||
self.handle,
|
||||
address,
|
||||
t.handle,
|
||||
ptr::null_mut(),
|
||||
components.as_ptr() as _,
|
||||
components.len(),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_data_variable_info<S: BnStrCompatible>(&self, var: DataVariableAndName<S>) -> bool {
|
||||
let name = var.name.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNAddDebugDataVariableInfo(
|
||||
self.handle,
|
||||
&BNDataVariableAndName {
|
||||
address: var.address,
|
||||
type_: var.t.contents.handle,
|
||||
name: name.as_ref().as_ptr() as *mut _,
|
||||
autoDiscovered: var.auto_discovered,
|
||||
typeConfidence: var.t.confidence,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for DebugInfo {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewDebugInfoReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeDebugInfoReference(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for DebugInfo {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
// CustomDebugInfoParser
|
||||
|
||||
/// Implement this trait to implement a debug info parser. See `DebugInfoParser` for more details.
|
||||
pub trait CustomDebugInfoParser: 'static + Sync {
|
||||
fn is_valid(&self, view: &BinaryView) -> bool;
|
||||
fn parse_info(
|
||||
&self,
|
||||
debug_info: &mut DebugInfo,
|
||||
view: &BinaryView,
|
||||
debug_file: &BinaryView,
|
||||
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
|
||||
) -> bool;
|
||||
}
|
||||
138
src/demangle.rs
Normal file
138
src/demangle.rs
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright 2022-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Interfaces for demangling and simplifying mangled names in binaries.
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
use std::os::raw::c_char;
|
||||
use std::{ffi::CStr, result};
|
||||
|
||||
use crate::architecture::CoreArchitecture;
|
||||
use crate::string::{BnStrCompatible, BnString};
|
||||
use crate::types::Type;
|
||||
|
||||
use crate::rc::*;
|
||||
|
||||
pub type Result<R> = result::Result<R, ()>;
|
||||
|
||||
pub fn demangle_gnu3<S: BnStrCompatible>(
|
||||
arch: &CoreArchitecture,
|
||||
mangled_name: S,
|
||||
simplify: bool,
|
||||
) -> Result<(Option<Ref<Type>>, Vec<String>)> {
|
||||
let mangled_name_bwn = mangled_name.into_bytes_with_nul();
|
||||
let mangled_name_ptr = mangled_name_bwn.as_ref();
|
||||
let mut out_type: *mut BNType = unsafe { std::mem::zeroed() };
|
||||
let mut out_name: *mut *mut std::os::raw::c_char = unsafe { std::mem::zeroed() };
|
||||
let mut out_size: usize = 0;
|
||||
let res = unsafe {
|
||||
BNDemangleGNU3(
|
||||
arch.0,
|
||||
mangled_name_ptr.as_ptr() as *const c_char,
|
||||
&mut out_type,
|
||||
&mut out_name,
|
||||
&mut out_size,
|
||||
simplify,
|
||||
)
|
||||
};
|
||||
|
||||
if !res || out_size == 0 {
|
||||
let cstr = match CStr::from_bytes_with_nul(mangled_name_ptr) {
|
||||
Ok(cstr) => cstr,
|
||||
Err(_) => {
|
||||
log::error!("demangle_gnu3: failed to parse mangled name");
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
return Ok((None, vec![cstr.to_string_lossy().into_owned()]));
|
||||
}
|
||||
|
||||
let out_type = match out_type.is_null() {
|
||||
true => {
|
||||
log::debug!("demangle_gnu3: out_type is NULL");
|
||||
None
|
||||
}
|
||||
false => Some(unsafe { Type::ref_from_raw(out_type) }),
|
||||
};
|
||||
|
||||
if out_name.is_null() {
|
||||
log::error!("demangle_gnu3: out_name is NULL");
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let names = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
|
||||
.iter()
|
||||
.map(|name| name.to_string())
|
||||
.collect();
|
||||
|
||||
unsafe { BNFreeDemangledName(&mut out_name, out_size) };
|
||||
|
||||
Ok((out_type, names))
|
||||
}
|
||||
|
||||
pub fn demangle_ms<S: BnStrCompatible>(
|
||||
arch: &CoreArchitecture,
|
||||
mangled_name: S,
|
||||
simplify: bool,
|
||||
) -> Result<(Option<Ref<Type>>, Vec<String>)> {
|
||||
let mangled_name_bwn = mangled_name.into_bytes_with_nul();
|
||||
let mangled_name_ptr = mangled_name_bwn.as_ref();
|
||||
|
||||
let mut out_type: *mut BNType = unsafe { std::mem::zeroed() };
|
||||
let mut out_name: *mut *mut std::os::raw::c_char = unsafe { std::mem::zeroed() };
|
||||
let mut out_size: usize = 0;
|
||||
let res = unsafe {
|
||||
BNDemangleMS(
|
||||
arch.0,
|
||||
mangled_name_ptr.as_ptr() as *const c_char,
|
||||
&mut out_type,
|
||||
&mut out_name,
|
||||
&mut out_size,
|
||||
simplify,
|
||||
)
|
||||
};
|
||||
|
||||
if !res || out_size == 0 {
|
||||
let cstr = match CStr::from_bytes_with_nul(mangled_name_ptr) {
|
||||
Ok(cstr) => cstr,
|
||||
Err(_) => {
|
||||
log::error!("demangle_ms: failed to parse mangled name");
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
return Ok((None, vec![cstr.to_string_lossy().into_owned()]));
|
||||
}
|
||||
|
||||
let out_type = match out_type.is_null() {
|
||||
true => {
|
||||
log::debug!("demangle_ms: out_type is NULL");
|
||||
None
|
||||
}
|
||||
false => Some(unsafe { Type::ref_from_raw(out_type) }),
|
||||
};
|
||||
|
||||
if out_name.is_null() {
|
||||
log::error!("demangle_ms: out_name is NULL");
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let names = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
|
||||
.iter()
|
||||
.map(|name| name.to_string())
|
||||
.collect();
|
||||
|
||||
unsafe { BNFreeDemangledName(&mut out_name, out_size) };
|
||||
|
||||
Ok((out_type, names))
|
||||
}
|
||||
458
src/disassembly.rs
Normal file
458
src/disassembly.rs
Normal file
@@ -0,0 +1,458 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// TODO : Combine this with the architecture implementation
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::string::{BnStr, BnString};
|
||||
use crate::{BN_FULL_CONFIDENCE, BN_INVALID_EXPR};
|
||||
|
||||
use crate::rc::*;
|
||||
|
||||
use std::convert::From;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
pub type InstructionTextTokenType = BNInstructionTextTokenType;
|
||||
pub type InstructionTextTokenContext = BNInstructionTextTokenContext;
|
||||
|
||||
// InstructionTextTokenType's; * = Implemented
|
||||
// *TextToken = 0,
|
||||
// *InstructionToken = 1,
|
||||
// *OperandSeparatorToken = 2,
|
||||
// *RegisterToken = 3,
|
||||
// *IntegerToken = 4,
|
||||
// *PossibleAddressToken = 5,
|
||||
// *BeginMemoryOperandToken = 6,
|
||||
// *EndMemoryOperandToken = 7,
|
||||
// *FloatingPointToken = 8,
|
||||
// AnnotationToken = 9,
|
||||
// *CodeRelativeAddressToken = 10,
|
||||
// ArgumentNameToken = 11,
|
||||
// HexDumpByteValueToken = 12,
|
||||
// HexDumpSkippedByteToken = 13,
|
||||
// HexDumpInvalidByteToken = 14,
|
||||
// HexDumpTextToken = 15,
|
||||
// OpcodeToken = 16,
|
||||
// *StringToken = 17,
|
||||
// CharacterConstantToken = 18,
|
||||
// *KeywordToken = 19,
|
||||
// *TypeNameToken = 20,
|
||||
// *FieldNameToken = 21,
|
||||
// *NameSpaceToken = 22,
|
||||
// NameSpaceSeparatorToken = 23,
|
||||
// TagToken = 24,
|
||||
// StructOffsetToken = 25,
|
||||
// StructOffsetByteValueToken = 26,
|
||||
// StructureHexDumpTextToken = 27,
|
||||
// *GotoLabelToken = 28,
|
||||
// CommentToken = 29,
|
||||
// PossibleValueToken = 30,
|
||||
// PossibleValueTypeToken = 31,
|
||||
// ArrayIndexToken = 32,
|
||||
// *IndentationToken = 33,
|
||||
// UnknownMemoryToken = 34,
|
||||
// CodeSymbolToken = 64,
|
||||
// DataSymbolToken = 65,
|
||||
// LocalVariableToken = 66,
|
||||
// ImportToken = 67,
|
||||
// AddressDisplayToken = 68,
|
||||
// IndirectImportToken = 69,
|
||||
// ExternalSymbolToken = 70,
|
||||
|
||||
#[repr(C)]
|
||||
pub struct InstructionTextToken(pub(crate) BNInstructionTextToken);
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum InstructionTextTokenContents {
|
||||
Text,
|
||||
Instruction,
|
||||
OperandSeparator,
|
||||
Register,
|
||||
Integer(u64), // TODO size?
|
||||
PossibleAddress(u64), // TODO size?
|
||||
BeginMemoryOperand,
|
||||
EndMemoryOperand,
|
||||
FloatingPoint,
|
||||
CodeRelativeAddress(u64),
|
||||
String(u64),
|
||||
Keyword,
|
||||
TypeName,
|
||||
FieldName,
|
||||
NameSpace,
|
||||
GotoLabel(u64),
|
||||
Indentation,
|
||||
}
|
||||
|
||||
impl InstructionTextToken {
|
||||
pub(crate) unsafe fn from_raw(raw: &BNInstructionTextToken) -> Self {
|
||||
Self(*raw)
|
||||
}
|
||||
|
||||
pub fn new(text: BnString, contents: InstructionTextTokenContents) -> Self {
|
||||
let (value, address) = match contents {
|
||||
InstructionTextTokenContents::Integer(v) => (v, 0),
|
||||
InstructionTextTokenContents::PossibleAddress(v)
|
||||
| InstructionTextTokenContents::CodeRelativeAddress(v)
|
||||
| InstructionTextTokenContents::GotoLabel(v) => (v, v),
|
||||
InstructionTextTokenContents::String(v) => (v, 0),
|
||||
_ => (0, 0),
|
||||
};
|
||||
|
||||
let type_ = match contents {
|
||||
InstructionTextTokenContents::Text => InstructionTextTokenType::TextToken,
|
||||
InstructionTextTokenContents::Instruction => InstructionTextTokenType::InstructionToken,
|
||||
InstructionTextTokenContents::OperandSeparator => {
|
||||
InstructionTextTokenType::OperandSeparatorToken
|
||||
}
|
||||
InstructionTextTokenContents::Register => InstructionTextTokenType::RegisterToken,
|
||||
InstructionTextTokenContents::Integer(_) => InstructionTextTokenType::IntegerToken,
|
||||
InstructionTextTokenContents::PossibleAddress(_) => {
|
||||
InstructionTextTokenType::PossibleAddressToken
|
||||
}
|
||||
InstructionTextTokenContents::BeginMemoryOperand => {
|
||||
InstructionTextTokenType::BeginMemoryOperandToken
|
||||
}
|
||||
InstructionTextTokenContents::EndMemoryOperand => {
|
||||
InstructionTextTokenType::EndMemoryOperandToken
|
||||
}
|
||||
InstructionTextTokenContents::FloatingPoint => {
|
||||
InstructionTextTokenType::FloatingPointToken
|
||||
}
|
||||
InstructionTextTokenContents::CodeRelativeAddress(_) => {
|
||||
InstructionTextTokenType::CodeRelativeAddressToken
|
||||
}
|
||||
InstructionTextTokenContents::String(_) => InstructionTextTokenType::StringToken,
|
||||
InstructionTextTokenContents::Keyword => InstructionTextTokenType::KeywordToken,
|
||||
InstructionTextTokenContents::TypeName => InstructionTextTokenType::TypeNameToken,
|
||||
InstructionTextTokenContents::FieldName => InstructionTextTokenType::FieldNameToken,
|
||||
InstructionTextTokenContents::NameSpace => InstructionTextTokenType::NameSpaceToken,
|
||||
InstructionTextTokenContents::GotoLabel(_) => InstructionTextTokenType::GotoLabelToken,
|
||||
InstructionTextTokenContents::Indentation => InstructionTextTokenType::IndentationToken,
|
||||
};
|
||||
|
||||
let width = text.len() as u64;
|
||||
|
||||
InstructionTextToken(BNInstructionTextToken {
|
||||
type_,
|
||||
text: text.into_raw(),
|
||||
value,
|
||||
width,
|
||||
size: 0,
|
||||
operand: 0xffff_ffff,
|
||||
context: InstructionTextTokenContext::NoTokenContext,
|
||||
confidence: BN_FULL_CONFIDENCE,
|
||||
address,
|
||||
typeNames: ptr::null_mut(),
|
||||
namesCount: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_value(&mut self, value: u64) {
|
||||
self.0.value = value;
|
||||
}
|
||||
|
||||
pub fn set_context(&mut self, context: InstructionTextTokenContext) {
|
||||
self.0.context = context;
|
||||
}
|
||||
|
||||
pub fn text(&self) -> &BnStr {
|
||||
unsafe { BnStr::from_raw(self.0.text) }
|
||||
}
|
||||
|
||||
pub fn contents(&self) -> InstructionTextTokenContents {
|
||||
use self::BNInstructionTextTokenType::*;
|
||||
use self::InstructionTextTokenContents::*;
|
||||
|
||||
match self.0.type_ {
|
||||
TextToken => Text,
|
||||
InstructionToken => Instruction,
|
||||
OperandSeparatorToken => OperandSeparator,
|
||||
RegisterToken => Register,
|
||||
IntegerToken => Integer(self.0.value),
|
||||
PossibleAddressToken => PossibleAddress(self.0.value),
|
||||
BeginMemoryOperandToken => BeginMemoryOperand,
|
||||
EndMemoryOperandToken => EndMemoryOperand,
|
||||
FloatingPointToken => FloatingPoint,
|
||||
CodeRelativeAddressToken => CodeRelativeAddress(self.0.value),
|
||||
_ => unimplemented!("woops"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn context(&self) -> InstructionTextTokenContext {
|
||||
self.0.context
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
self.0.size
|
||||
}
|
||||
|
||||
pub fn operand(&self) -> usize {
|
||||
self.0.operand
|
||||
}
|
||||
|
||||
pub fn address(&self) -> u64 {
|
||||
self.0.address
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for InstructionTextToken {
|
||||
fn default() -> Self {
|
||||
InstructionTextToken(BNInstructionTextToken {
|
||||
type_: InstructionTextTokenType::TextToken,
|
||||
text: ptr::null_mut(),
|
||||
value: 0,
|
||||
width: 0,
|
||||
size: 0,
|
||||
operand: 0,
|
||||
context: InstructionTextTokenContext::NoTokenContext,
|
||||
confidence: BN_FULL_CONFIDENCE,
|
||||
address: 0,
|
||||
typeNames: ptr::null_mut(),
|
||||
namesCount: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for InstructionTextToken {
|
||||
fn clone(&self) -> Self {
|
||||
InstructionTextToken(BNInstructionTextToken {
|
||||
type_: self.0.type_,
|
||||
context: self.0.context,
|
||||
address: self.0.address,
|
||||
size: self.0.size,
|
||||
operand: self.0.operand,
|
||||
value: self.0.value,
|
||||
width: 0,
|
||||
text: BnString::new(self.text()).into_raw(),
|
||||
confidence: 0xff,
|
||||
typeNames: ptr::null_mut(),
|
||||
namesCount: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : There is almost certainly a memory leak here - in the case where
|
||||
// `impl CoreOwnedArrayProvider for InstructionTextToken` doesn't get triggered
|
||||
// impl Drop for InstructionTextToken {
|
||||
// fn drop(&mut self) {
|
||||
// let _owned = unsafe { BnString::from_raw(self.0.text) };
|
||||
// }
|
||||
// }
|
||||
|
||||
pub struct DisassemblyTextLine(pub(crate) BNDisassemblyTextLine);
|
||||
|
||||
impl DisassemblyTextLine {
|
||||
// TODO : this should probably be removed, though it doesn't actually hurt anything
|
||||
pub fn debug_print(&self) {
|
||||
println!("{}", self);
|
||||
}
|
||||
|
||||
pub fn addr(&self) -> u64 {
|
||||
self.0.addr
|
||||
}
|
||||
|
||||
pub fn instr_idx(&self) -> usize {
|
||||
self.0.instrIndex
|
||||
}
|
||||
|
||||
pub fn count(&self) -> usize {
|
||||
self.0.count
|
||||
}
|
||||
|
||||
pub fn tag_count(&self) -> usize {
|
||||
self.0.tagCount
|
||||
}
|
||||
|
||||
pub fn tokens(&self) -> Vec<InstructionTextToken> {
|
||||
unsafe {
|
||||
std::slice::from_raw_parts::<BNInstructionTextToken>(self.0.tokens, self.0.count)
|
||||
.iter()
|
||||
.map(|&x| InstructionTextToken::from_raw(&x))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DisassemblyTextLine {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for token in self.tokens() {
|
||||
write!(f, "{}", token.text())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<InstructionTextToken>> for DisassemblyTextLine {
|
||||
fn from(mut tokens: Vec<InstructionTextToken>) -> Self {
|
||||
tokens.shrink_to_fit();
|
||||
|
||||
assert!(tokens.len() == tokens.capacity());
|
||||
// TODO: let (tokens_pointer, tokens_len, _) = unsafe { tokens.into_raw_parts() }; // Can't use for now...still a rust nightly feature
|
||||
let tokens_pointer = tokens.as_mut_ptr();
|
||||
let tokens_len = tokens.len();
|
||||
mem::forget(tokens);
|
||||
|
||||
DisassemblyTextLine(BNDisassemblyTextLine {
|
||||
addr: 0,
|
||||
instrIndex: BN_INVALID_EXPR,
|
||||
tokens: tokens_pointer as *mut _,
|
||||
count: tokens_len,
|
||||
highlight: BNHighlightColor {
|
||||
style: BNHighlightColorStyle::StandardHighlightColor,
|
||||
color: BNHighlightStandardColor::NoHighlightColor,
|
||||
mixColor: BNHighlightStandardColor::NoHighlightColor,
|
||||
mix: 0,
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
alpha: 0,
|
||||
},
|
||||
tags: ptr::null_mut(),
|
||||
tagCount: 0,
|
||||
typeInfo: BNDisassemblyTextLineTypeInfo {
|
||||
hasTypeInfo: false,
|
||||
parentType: ptr::null_mut(),
|
||||
fieldIndex: usize::MAX,
|
||||
offset: 0,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Vec<&str>> for DisassemblyTextLine {
|
||||
fn from(string_tokens: &Vec<&str>) -> Self {
|
||||
let mut tokens: Vec<BNInstructionTextToken> = Vec::with_capacity(string_tokens.len());
|
||||
tokens.extend(string_tokens.iter().map(|&token| {
|
||||
InstructionTextToken::new(BnString::new(token), InstructionTextTokenContents::Text).0
|
||||
}));
|
||||
|
||||
assert!(tokens.len() == tokens.capacity());
|
||||
// let (tokens_pointer, tokens_len, _) = unsafe { tokens.into_raw_parts() }; // Can't use for now...still a rust nighly feature
|
||||
let tokens_pointer = tokens.as_mut_ptr();
|
||||
let tokens_len = tokens.len();
|
||||
mem::forget(tokens);
|
||||
|
||||
DisassemblyTextLine(BNDisassemblyTextLine {
|
||||
addr: 0,
|
||||
instrIndex: BN_INVALID_EXPR,
|
||||
tokens: tokens_pointer as *mut _,
|
||||
count: tokens_len,
|
||||
highlight: BNHighlightColor {
|
||||
style: BNHighlightColorStyle::StandardHighlightColor,
|
||||
color: BNHighlightStandardColor::NoHighlightColor,
|
||||
mixColor: BNHighlightStandardColor::NoHighlightColor,
|
||||
mix: 0,
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
alpha: 0,
|
||||
},
|
||||
tags: ptr::null_mut(),
|
||||
tagCount: 0,
|
||||
typeInfo: BNDisassemblyTextLineTypeInfo {
|
||||
hasTypeInfo: false,
|
||||
parentType: ptr::null_mut(),
|
||||
fieldIndex: usize::MAX,
|
||||
offset: 0,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DisassemblyTextLine {
|
||||
fn default() -> Self {
|
||||
DisassemblyTextLine(BNDisassemblyTextLine {
|
||||
addr: 0,
|
||||
instrIndex: BN_INVALID_EXPR,
|
||||
tokens: ptr::null_mut(),
|
||||
count: 0,
|
||||
highlight: BNHighlightColor {
|
||||
style: BNHighlightColorStyle::StandardHighlightColor,
|
||||
color: BNHighlightStandardColor::NoHighlightColor,
|
||||
mixColor: BNHighlightStandardColor::NoHighlightColor,
|
||||
mix: 0,
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
alpha: 0,
|
||||
},
|
||||
tags: ptr::null_mut(),
|
||||
tagCount: 0,
|
||||
typeInfo: BNDisassemblyTextLineTypeInfo {
|
||||
hasTypeInfo: false,
|
||||
parentType: ptr::null_mut(),
|
||||
fieldIndex: usize::MAX,
|
||||
offset: 0,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DisassemblyTextLine {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
Vec::from_raw_parts(self.0.tokens, self.0.count, self.0.count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type DisassemblyOption = BNDisassemblyOption;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct DisassemblySettings {
|
||||
pub(crate) handle: *mut BNDisassemblySettings,
|
||||
}
|
||||
|
||||
impl DisassemblySettings {
|
||||
pub fn new() -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle = BNCreateDisassemblySettings();
|
||||
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Ref::new(Self { handle })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_option(&self, option: DisassemblyOption, state: bool) {
|
||||
unsafe { BNSetDisassemblySettingsOption(self.handle, option, state) }
|
||||
}
|
||||
|
||||
pub fn is_option_set(&self, option: DisassemblyOption) -> bool {
|
||||
unsafe { BNIsDisassemblySettingsOptionSet(self.handle, option) }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for DisassemblySettings {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for DisassemblySettings {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewDisassemblySettingsReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeDisassemblySettings(handle.handle);
|
||||
}
|
||||
}
|
||||
308
src/downloadprovider.rs
Normal file
308
src/downloadprovider.rs
Normal file
@@ -0,0 +1,308 @@
|
||||
use crate::rc::{
|
||||
Array, CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Ref, RefCountable,
|
||||
};
|
||||
use crate::settings::Settings;
|
||||
use crate::string::{BnStr, BnStrCompatible, BnString};
|
||||
use binaryninjacore_sys::*;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr::null_mut;
|
||||
use std::slice;
|
||||
|
||||
pub struct DownloadProvider {
|
||||
handle: *mut BNDownloadProvider,
|
||||
}
|
||||
|
||||
impl DownloadProvider {
|
||||
pub fn get<S: BnStrCompatible>(name: S) -> Option<DownloadProvider> {
|
||||
let result = unsafe {
|
||||
BNGetDownloadProviderByName(
|
||||
name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char
|
||||
)
|
||||
};
|
||||
if result.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(DownloadProvider { handle: result })
|
||||
}
|
||||
|
||||
pub fn list() -> Result<Array<DownloadProvider>, ()> {
|
||||
let mut count = 0;
|
||||
let list: *mut *mut BNDownloadProvider = unsafe { BNGetDownloadProviderList(&mut count) };
|
||||
|
||||
if list.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(unsafe { Array::new(list, count, ()) })
|
||||
}
|
||||
|
||||
/// TODO : We may want to `impl Default`....excessive error checking might be preventing us from doing so
|
||||
pub fn try_default() -> Result<DownloadProvider, ()> {
|
||||
let s = Settings::new("");
|
||||
let dp_name = s.get_string("network.downloadProviderName", None, None);
|
||||
Self::get(dp_name).ok_or(())
|
||||
}
|
||||
|
||||
pub(crate) fn from_raw(handle: *mut BNDownloadProvider) -> DownloadProvider {
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub fn create_instance(&self) -> Result<Ref<DownloadInstance>, ()> {
|
||||
let result: *mut BNDownloadInstance =
|
||||
unsafe { BNCreateDownloadProviderInstance(self.handle) };
|
||||
if result.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(unsafe { DownloadInstance::ref_from_raw(result) })
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for DownloadProvider {
|
||||
type Raw = *mut BNDownloadProvider;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for DownloadProvider {
|
||||
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
|
||||
BNFreeDownloadProviderList(raw);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for DownloadProvider {
|
||||
type Wrapped = DownloadProvider;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
DownloadProvider::from_raw(*raw)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DownloadInstanceOutputCallbacks {
|
||||
pub write: Option<Box<dyn FnMut(&[u8]) -> usize>>,
|
||||
pub progress: Option<Box<dyn FnMut(u64, u64) -> bool>>,
|
||||
}
|
||||
|
||||
pub struct DownloadInstanceInputOutputCallbacks {
|
||||
pub read: Option<Box<dyn FnMut(&mut [u8]) -> Option<isize>>>,
|
||||
pub write: Option<Box<dyn FnMut(&[u8]) -> usize>>,
|
||||
pub progress: Option<Box<dyn FnMut(u64, u64) -> bool>>,
|
||||
}
|
||||
|
||||
pub struct DownloadResponse {
|
||||
pub status_code: u16,
|
||||
pub headers: HashMap<String, String>,
|
||||
}
|
||||
|
||||
pub struct DownloadInstance {
|
||||
handle: *mut BNDownloadInstance,
|
||||
}
|
||||
|
||||
impl DownloadInstance {
|
||||
pub(crate) unsafe fn from_raw(handle: *mut BNDownloadInstance) -> Self {
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn ref_from_raw(handle: *mut BNDownloadInstance) -> Ref<Self> {
|
||||
Ref::new(Self::from_raw(handle))
|
||||
}
|
||||
|
||||
fn get_error(&self) -> BnString {
|
||||
let err: *mut c_char = unsafe { BNGetErrorForDownloadInstance(self.handle) };
|
||||
unsafe { BnString::from_raw(err) }
|
||||
}
|
||||
|
||||
unsafe extern "C" fn o_write_callback(data: *mut u8, len: u64, ctxt: *mut c_void) -> u64 {
|
||||
let callbacks = ctxt as *mut DownloadInstanceOutputCallbacks;
|
||||
if let Some(func) = &mut (*callbacks).write {
|
||||
let slice = slice::from_raw_parts(data, len as usize);
|
||||
let result = (func)(slice);
|
||||
result as u64
|
||||
} else {
|
||||
0u64
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn o_progress_callback(ctxt: *mut c_void, progress: u64, total: u64) -> bool {
|
||||
let callbacks = ctxt as *mut DownloadInstanceOutputCallbacks;
|
||||
if let Some(func) = &mut (*callbacks).progress {
|
||||
(func)(progress, total)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn perform_request<S: BnStrCompatible>(
|
||||
&mut self,
|
||||
url: S,
|
||||
callbacks: DownloadInstanceOutputCallbacks,
|
||||
) -> Result<(), BnString> {
|
||||
let callbacks = Box::into_raw(Box::new(callbacks));
|
||||
let mut cbs = BNDownloadInstanceOutputCallbacks {
|
||||
writeCallback: Some(Self::o_write_callback),
|
||||
writeContext: callbacks as *mut c_void,
|
||||
progressCallback: Some(Self::o_progress_callback),
|
||||
progressContext: callbacks as *mut c_void,
|
||||
};
|
||||
|
||||
let result = unsafe {
|
||||
BNPerformDownloadRequest(
|
||||
self.handle,
|
||||
url.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
|
||||
&mut cbs as *mut BNDownloadInstanceOutputCallbacks,
|
||||
)
|
||||
};
|
||||
|
||||
// Drop it
|
||||
unsafe { drop(Box::from_raw(callbacks)) };
|
||||
if result < 0 {
|
||||
Err(self.get_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn i_read_callback(data: *mut u8, len: u64, ctxt: *mut c_void) -> i64 {
|
||||
let callbacks = ctxt as *mut DownloadInstanceInputOutputCallbacks;
|
||||
if let Some(func) = &mut (*callbacks).read {
|
||||
let slice = slice::from_raw_parts_mut(data, len as usize);
|
||||
let result = (func)(slice);
|
||||
if let Some(count) = result {
|
||||
count as i64
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn i_write_callback(data: *mut u8, len: u64, ctxt: *mut c_void) -> u64 {
|
||||
let callbacks = ctxt as *mut DownloadInstanceInputOutputCallbacks;
|
||||
if let Some(func) = &mut (*callbacks).write {
|
||||
let slice = slice::from_raw_parts(data, len as usize);
|
||||
let result = (func)(slice);
|
||||
result as u64
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn i_progress_callback(ctxt: *mut c_void, progress: u64, total: u64) -> bool {
|
||||
let callbacks = ctxt as *mut DownloadInstanceInputOutputCallbacks;
|
||||
if let Some(func) = &mut (*callbacks).progress {
|
||||
(func)(progress, total)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn perform_custom_request<
|
||||
M: BnStrCompatible,
|
||||
U: BnStrCompatible,
|
||||
HK: BnStrCompatible,
|
||||
HV: BnStrCompatible,
|
||||
I: IntoIterator<Item = (HK, HV)>,
|
||||
>(
|
||||
&mut self,
|
||||
method: M,
|
||||
url: U,
|
||||
headers: I,
|
||||
callbacks: DownloadInstanceInputOutputCallbacks,
|
||||
) -> Result<DownloadResponse, BnString> {
|
||||
let mut header_keys = vec![];
|
||||
let mut header_values = vec![];
|
||||
for (key, value) in headers {
|
||||
header_keys.push(key.into_bytes_with_nul());
|
||||
header_values.push(value.into_bytes_with_nul());
|
||||
}
|
||||
|
||||
let mut header_key_ptrs = vec![];
|
||||
let mut header_value_ptrs = vec![];
|
||||
|
||||
for (key, value) in header_keys.iter().zip(header_values.iter()) {
|
||||
header_key_ptrs.push(key.as_ref().as_ptr() as *const c_char);
|
||||
header_value_ptrs.push(value.as_ref().as_ptr() as *const c_char);
|
||||
}
|
||||
|
||||
let callbacks = Box::into_raw(Box::new(callbacks));
|
||||
let mut cbs = BNDownloadInstanceInputOutputCallbacks {
|
||||
readCallback: Some(Self::i_read_callback),
|
||||
readContext: callbacks as *mut c_void,
|
||||
writeCallback: Some(Self::i_write_callback),
|
||||
writeContext: callbacks as *mut c_void,
|
||||
progressCallback: Some(Self::i_progress_callback),
|
||||
progressContext: callbacks as *mut c_void,
|
||||
};
|
||||
|
||||
let mut response: *mut BNDownloadInstanceResponse = null_mut();
|
||||
|
||||
let result = unsafe {
|
||||
BNPerformCustomRequest(
|
||||
self.handle,
|
||||
method.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
|
||||
url.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
|
||||
header_key_ptrs.len() as u64,
|
||||
header_key_ptrs.as_ptr(),
|
||||
header_value_ptrs.as_ptr(),
|
||||
&mut response as *mut *mut BNDownloadInstanceResponse,
|
||||
&mut cbs as *mut BNDownloadInstanceInputOutputCallbacks,
|
||||
)
|
||||
};
|
||||
|
||||
if result < 0 {
|
||||
unsafe { BNFreeDownloadInstanceResponse(response) };
|
||||
return Err(self.get_error());
|
||||
}
|
||||
|
||||
let mut response_headers = HashMap::new();
|
||||
unsafe {
|
||||
let response_header_keys: &[*mut c_char] =
|
||||
slice::from_raw_parts((*response).headerKeys, (*response).headerCount as usize);
|
||||
let response_header_values: &[*mut c_char] =
|
||||
slice::from_raw_parts((*response).headerValues, (*response).headerCount as usize);
|
||||
|
||||
for (key, value) in response_header_keys
|
||||
.iter()
|
||||
.zip(response_header_values.iter())
|
||||
{
|
||||
response_headers.insert(
|
||||
BnStr::from_raw(*key).to_string(),
|
||||
BnStr::from_raw(*value).to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let r = DownloadResponse {
|
||||
status_code: unsafe { (*response).statusCode },
|
||||
headers: response_headers,
|
||||
};
|
||||
|
||||
unsafe { BNFreeDownloadInstanceResponse(response) };
|
||||
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for DownloadInstance {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for DownloadInstance {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewDownloadInstanceReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeDownloadInstance(handle.handle);
|
||||
}
|
||||
}
|
||||
25
src/ffi.rs
Normal file
25
src/ffi.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
macro_rules! ffi_wrap {
|
||||
($n:expr, $b:expr) => {{
|
||||
use std::panic;
|
||||
use std::process;
|
||||
|
||||
panic::catch_unwind(|| $b).unwrap_or_else(|_| {
|
||||
error!("ffi callback caught panic: {}", $n);
|
||||
process::abort()
|
||||
})
|
||||
}};
|
||||
}
|
||||
90
src/fileaccessor.rs
Normal file
90
src/fileaccessor.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use binaryninjacore_sys::BNFileAccessor;
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::marker::PhantomData;
|
||||
use std::slice;
|
||||
|
||||
pub struct FileAccessor<'a> {
|
||||
pub(crate) api_object: BNFileAccessor,
|
||||
_ref: PhantomData<&'a mut ()>,
|
||||
}
|
||||
|
||||
impl<'a> FileAccessor<'a> {
|
||||
pub fn new<F>(f: &'a mut F) -> Self
|
||||
where
|
||||
F: 'a + Read + Write + Seek + Sized,
|
||||
{
|
||||
use std::os::raw::c_void;
|
||||
|
||||
extern "C" fn cb_get_length<F>(ctxt: *mut c_void) -> u64
|
||||
where
|
||||
F: Read + Write + Seek + Sized,
|
||||
{
|
||||
let f = unsafe { &mut *(ctxt as *mut F) };
|
||||
|
||||
f.seek(SeekFrom::End(0)).unwrap_or(0)
|
||||
}
|
||||
|
||||
extern "C" fn cb_read<F>(
|
||||
ctxt: *mut c_void,
|
||||
dest: *mut c_void,
|
||||
offset: u64,
|
||||
len: usize,
|
||||
) -> usize
|
||||
where
|
||||
F: Read + Write + Seek + Sized,
|
||||
{
|
||||
let f = unsafe { &mut *(ctxt as *mut F) };
|
||||
let dest = unsafe { slice::from_raw_parts_mut(dest as *mut u8, len) };
|
||||
|
||||
if f.seek(SeekFrom::Start(offset)).is_err() {
|
||||
debug!("Failed to seek to offset {:x}", offset);
|
||||
0
|
||||
} else {
|
||||
f.read(dest).unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn cb_write<F>(
|
||||
ctxt: *mut c_void,
|
||||
offset: u64,
|
||||
src: *const c_void,
|
||||
len: usize,
|
||||
) -> usize
|
||||
where
|
||||
F: Read + Write + Seek + Sized,
|
||||
{
|
||||
let f = unsafe { &mut *(ctxt as *mut F) };
|
||||
let src = unsafe { slice::from_raw_parts(src as *const u8, len) };
|
||||
|
||||
if f.seek(SeekFrom::Start(offset)).is_err() {
|
||||
0
|
||||
} else {
|
||||
f.write(src).unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
api_object: BNFileAccessor {
|
||||
context: f as *mut F as *mut _,
|
||||
getLength: Some(cb_get_length::<F>),
|
||||
read: Some(cb_read::<F>),
|
||||
write: Some(cb_write::<F>),
|
||||
},
|
||||
_ref: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
290
src/filemetadata.rs
Normal file
290
src/filemetadata.rs
Normal file
@@ -0,0 +1,290 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use binaryninjacore_sys::{
|
||||
BNBeginUndoActions,
|
||||
BNCloseFile,
|
||||
BNCommitUndoActions,
|
||||
BNCreateDatabase,
|
||||
BNCreateFileMetadata,
|
||||
BNFileMetadata,
|
||||
BNFreeFileMetadata,
|
||||
BNGetCurrentOffset,
|
||||
BNGetCurrentView,
|
||||
BNGetFileViewOfType,
|
||||
BNGetFilename,
|
||||
BNIsAnalysisChanged,
|
||||
BNIsBackedByDatabase,
|
||||
//BNSetFileMetadataNavigationHandler,
|
||||
BNIsFileModified,
|
||||
BNMarkFileModified,
|
||||
BNMarkFileSaved,
|
||||
BNNavigate,
|
||||
BNNewFileReference,
|
||||
BNOpenDatabaseForConfiguration,
|
||||
BNOpenExistingDatabase,
|
||||
BNRedo,
|
||||
BNRevertUndoActions,
|
||||
BNSaveAutoSnapshot,
|
||||
BNSetFilename,
|
||||
BNUndo,
|
||||
};
|
||||
|
||||
use crate::binaryview::BinaryView;
|
||||
|
||||
use crate::rc::*;
|
||||
use crate::string::*;
|
||||
|
||||
use std::ptr;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct FileMetadata {
|
||||
pub(crate) handle: *mut BNFileMetadata,
|
||||
}
|
||||
|
||||
unsafe impl Send for FileMetadata {}
|
||||
unsafe impl Sync for FileMetadata {}
|
||||
|
||||
impl FileMetadata {
|
||||
pub(crate) fn from_raw(handle: *mut BNFileMetadata) -> Self {
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub fn new() -> Ref<Self> {
|
||||
unsafe {
|
||||
Ref::new(Self {
|
||||
handle: BNCreateFileMetadata(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_filename<S: BnStrCompatible>(name: S) -> Ref<Self> {
|
||||
let ret = FileMetadata::new();
|
||||
ret.set_filename(name);
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn close(&self) {
|
||||
unsafe {
|
||||
BNCloseFile(self.handle);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filename(&self) -> BnString {
|
||||
unsafe {
|
||||
let raw = BNGetFilename(self.handle);
|
||||
BnString::from_raw(raw)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_filename<S: BnStrCompatible>(&self, name: S) {
|
||||
let name = name.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
BNSetFilename(self.handle, name.as_ref().as_ptr() as *mut _);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn modified(&self) -> bool {
|
||||
unsafe { BNIsFileModified(self.handle) }
|
||||
}
|
||||
|
||||
pub fn mark_modified(&self) {
|
||||
unsafe {
|
||||
BNMarkFileModified(self.handle);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mark_saved(&self) {
|
||||
unsafe {
|
||||
BNMarkFileSaved(self.handle);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_analysis_changed(&self) -> bool {
|
||||
unsafe { BNIsAnalysisChanged(self.handle) }
|
||||
}
|
||||
|
||||
pub fn is_database_backed<S: BnStrCompatible>(&self, view_type: S) -> bool {
|
||||
let view_type = view_type.into_bytes_with_nul();
|
||||
|
||||
unsafe { BNIsBackedByDatabase(self.handle, view_type.as_ref().as_ptr() as *const _) }
|
||||
}
|
||||
|
||||
pub fn run_undoable_transaction<F: FnOnce() -> Result<T, E>, T, E>(
|
||||
&self,
|
||||
func: F,
|
||||
) -> Result<T, E> {
|
||||
let undo = self.begin_undo_actions(false);
|
||||
let result = func();
|
||||
match result {
|
||||
Ok(t) => {
|
||||
self.commit_undo_actions(undo);
|
||||
Ok(t)
|
||||
}
|
||||
Err(e) => {
|
||||
self.revert_undo_actions(undo);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn begin_undo_actions(&self, anonymous_allowed: bool) -> BnString {
|
||||
unsafe { BnString::from_raw(BNBeginUndoActions(self.handle, anonymous_allowed)) }
|
||||
}
|
||||
|
||||
pub fn commit_undo_actions<S: BnStrCompatible>(&self, id: S) {
|
||||
let id = id.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNCommitUndoActions(self.handle, id.as_ref().as_ptr() as *const _);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn revert_undo_actions<S: BnStrCompatible>(&self, id: S) {
|
||||
let id = id.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNRevertUndoActions(self.handle, id.as_ref().as_ptr() as *const _);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn undo(&self) {
|
||||
unsafe {
|
||||
BNUndo(self.handle);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redo(&self) {
|
||||
unsafe {
|
||||
BNRedo(self.handle);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_view(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNGetCurrentView(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn current_offset(&self) -> u64 {
|
||||
unsafe { BNGetCurrentOffset(self.handle) }
|
||||
}
|
||||
|
||||
pub fn navigate_to<S: BnStrCompatible>(&self, view: S, offset: u64) -> Result<(), ()> {
|
||||
let view = view.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
if BNNavigate(self.handle, view.as_ref().as_ptr() as *const _, offset) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_view_of_type<S: BnStrCompatible>(&self, view: S) -> Result<Ref<BinaryView>, ()> {
|
||||
let view = view.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
let res = BNGetFileViewOfType(self.handle, view.as_ref().as_ptr() as *const _);
|
||||
|
||||
if res.is_null() {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(BinaryView::from_raw(res))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_database<S: BnStrCompatible>(&self, filename: S) -> bool {
|
||||
let filename = filename.into_bytes_with_nul();
|
||||
let raw = "Raw".into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
BNCreateDatabase(
|
||||
BNGetFileViewOfType(self.handle, raw.as_ptr() as *mut _),
|
||||
filename.as_ref().as_ptr() as *mut _,
|
||||
ptr::null_mut() as *mut _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_auto_snapshot(&self) -> bool {
|
||||
let raw = "Raw".into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNSaveAutoSnapshot(
|
||||
BNGetFileViewOfType(self.handle, raw.as_ptr() as *mut _),
|
||||
ptr::null_mut() as *mut _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_database_for_configuration<S: BnStrCompatible>(
|
||||
&self,
|
||||
filename: S,
|
||||
) -> Result<Ref<BinaryView>, ()> {
|
||||
let filename = filename.into_bytes_with_nul();
|
||||
unsafe {
|
||||
let bv =
|
||||
BNOpenDatabaseForConfiguration(self.handle, filename.as_ref().as_ptr() as *const _);
|
||||
|
||||
if bv.is_null() {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(BinaryView::from_raw(bv))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_database<S: BnStrCompatible>(&self, filename: S) -> Result<Ref<BinaryView>, ()> {
|
||||
let filename = filename.into_bytes_with_nul();
|
||||
let filename_ptr = filename.as_ref().as_ptr() as *mut _;
|
||||
|
||||
let view = unsafe { BNOpenExistingDatabase(self.handle, filename_ptr) };
|
||||
|
||||
// TODO : add optional progress function
|
||||
// let view = match progress_func {
|
||||
// None => BNOpenExistingDatabase(self.handle, filename_ptr),
|
||||
// _ => BNOpenExistingDatabaseWithProgress(self.handle, str(filename), None, ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, cur, total: progress_func(cur, total)))
|
||||
// };
|
||||
|
||||
if view.is_null() {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(unsafe { BinaryView::from_raw(view) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for FileMetadata {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for FileMetadata {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewFileReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeFileMetadata(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
BNCreateDatabase,
|
||||
BNCreateDatabaseWithProgress,
|
||||
*/
|
||||
164
src/flowgraph.rs
Normal file
164
src/flowgraph.rs
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Interfaces for creating and displaying pretty CFGs in Binary Ninja.
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::disassembly::DisassemblyTextLine;
|
||||
|
||||
use crate::rc::*;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub type BranchType = BNBranchType;
|
||||
pub type EdgePenStyle = BNEdgePenStyle;
|
||||
pub type ThemeColor = BNThemeColor;
|
||||
pub type FlowGraphOption = BNFlowGraphOption;
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct EdgeStyle(pub(crate) BNEdgeStyle);
|
||||
|
||||
impl EdgeStyle {
|
||||
pub fn new(style: EdgePenStyle, width: usize, color: ThemeColor) -> Self {
|
||||
EdgeStyle(BNEdgeStyle {
|
||||
style,
|
||||
width,
|
||||
color,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EdgeStyle {
|
||||
fn default() -> Self {
|
||||
EdgeStyle(BNEdgeStyle {
|
||||
style: EdgePenStyle::SolidLine,
|
||||
width: 0,
|
||||
color: ThemeColor::AddressColor,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct FlowGraphNode<'a> {
|
||||
pub(crate) handle: *mut BNFlowGraphNode,
|
||||
_data: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> FlowGraphNode<'a> {
|
||||
pub(crate) unsafe fn from_raw(raw: *mut BNFlowGraphNode) -> Self {
|
||||
Self {
|
||||
handle: raw,
|
||||
_data: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(graph: &FlowGraph) -> Self {
|
||||
unsafe { FlowGraphNode::from_raw(BNCreateFlowGraphNode(graph.handle)) }
|
||||
}
|
||||
|
||||
pub fn set_disassembly_lines(&self, lines: &'a Vec<DisassemblyTextLine>) {
|
||||
unsafe {
|
||||
BNSetFlowGraphNodeLines(self.handle, lines.as_ptr() as *mut _, lines.len());
|
||||
// BNFreeDisassemblyTextLines(lines.as_ptr() as *mut _, lines.len()); // Shouldn't need...would be a double free?
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_lines(&self, lines: Vec<&str>) {
|
||||
let lines = lines
|
||||
.iter()
|
||||
.map(|&line| DisassemblyTextLine::from(&vec![line]))
|
||||
.collect();
|
||||
self.set_disassembly_lines(&lines);
|
||||
}
|
||||
|
||||
pub fn add_outgoing_edge(
|
||||
&self,
|
||||
type_: BranchType,
|
||||
target: &'a FlowGraphNode,
|
||||
edge_style: &'a EdgeStyle,
|
||||
) {
|
||||
unsafe { BNAddFlowGraphNodeOutgoingEdge(self.handle, type_, target.handle, edge_style.0) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> RefCountable for FlowGraphNode<'a> {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewFlowGraphNodeReference(handle.handle),
|
||||
_data: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeFlowGraphNode(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToOwned for FlowGraphNode<'a> {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : FlowGraph are RefCounted objects, this needs to be changed to only return Refs to FlowGraph
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct FlowGraph {
|
||||
pub(crate) handle: *mut BNFlowGraph,
|
||||
}
|
||||
|
||||
impl FlowGraph {
|
||||
pub(crate) unsafe fn from_raw(raw: *mut BNFlowGraph) -> Self {
|
||||
Self { handle: raw }
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
unsafe { FlowGraph::from_raw(BNCreateFlowGraph()) }
|
||||
}
|
||||
|
||||
pub fn append(&self, node: &FlowGraphNode) -> usize {
|
||||
unsafe { BNAddFlowGraphNode(self.handle, node.handle) }
|
||||
}
|
||||
|
||||
pub fn set_option(&self, option: FlowGraphOption, value: bool) {
|
||||
unsafe { BNSetFlowGraphOption(self.handle, option, value) }
|
||||
}
|
||||
|
||||
pub fn is_option_set(&self, option: FlowGraphOption) -> bool {
|
||||
unsafe { BNIsFlowGraphOptionSet(self.handle, option) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for FlowGraph {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewFlowGraphReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeFlowGraph(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for FlowGraph {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
389
src/function.rs
Normal file
389
src/function.rs
Normal file
@@ -0,0 +1,389 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::rc::*;
|
||||
use crate::string::*;
|
||||
use crate::types::Variable;
|
||||
use crate::{
|
||||
architecture::CoreArchitecture,
|
||||
basicblock::{BasicBlock, BlockContext},
|
||||
binaryview::{BinaryView, BinaryViewExt},
|
||||
llil, mlil,
|
||||
platform::Platform,
|
||||
symbol::Symbol,
|
||||
types::{Conf, NamedTypedVariable, Type},
|
||||
};
|
||||
|
||||
use std::{fmt, mem};
|
||||
|
||||
pub struct Location {
|
||||
pub arch: Option<CoreArchitecture>,
|
||||
pub addr: u64,
|
||||
}
|
||||
|
||||
impl From<u64> for Location {
|
||||
fn from(addr: u64) -> Self {
|
||||
Location { arch: None, addr }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(CoreArchitecture, u64)> for Location {
|
||||
fn from(loc: (CoreArchitecture, u64)) -> Self {
|
||||
Location {
|
||||
arch: Some(loc.0),
|
||||
addr: loc.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NativeBlockIter {
|
||||
arch: CoreArchitecture,
|
||||
bv: Ref<BinaryView>,
|
||||
cur: u64,
|
||||
end: u64,
|
||||
}
|
||||
|
||||
impl Iterator for NativeBlockIter {
|
||||
type Item = u64;
|
||||
|
||||
fn next(&mut self) -> Option<u64> {
|
||||
let res = self.cur;
|
||||
|
||||
if res >= self.end {
|
||||
None
|
||||
} else {
|
||||
self.bv
|
||||
.instruction_len(&self.arch, res)
|
||||
.map(|x| {
|
||||
self.cur += x as u64;
|
||||
res
|
||||
})
|
||||
.or_else(|| {
|
||||
self.cur = self.end;
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NativeBlock {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl NativeBlock {
|
||||
pub(crate) fn new() -> Self {
|
||||
NativeBlock { _priv: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockContext for NativeBlock {
|
||||
type Iter = NativeBlockIter;
|
||||
type Instruction = u64;
|
||||
|
||||
fn start(&self, block: &BasicBlock<Self>) -> u64 {
|
||||
block.raw_start()
|
||||
}
|
||||
|
||||
fn iter(&self, block: &BasicBlock<Self>) -> NativeBlockIter {
|
||||
NativeBlockIter {
|
||||
arch: block.arch(),
|
||||
bv: block.function().view(),
|
||||
cur: block.raw_start(),
|
||||
end: block.raw_end(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct Function {
|
||||
pub(crate) handle: *mut BNFunction,
|
||||
}
|
||||
|
||||
unsafe impl Send for Function {}
|
||||
unsafe impl Sync for Function {}
|
||||
|
||||
impl Function {
|
||||
pub(crate) unsafe fn from_raw(handle: *mut BNFunction) -> Ref<Self> {
|
||||
Ref::new(Self { handle })
|
||||
}
|
||||
|
||||
pub fn arch(&self) -> CoreArchitecture {
|
||||
unsafe {
|
||||
let arch = BNGetFunctionArchitecture(self.handle);
|
||||
CoreArchitecture::from_raw(arch)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn platform(&self) -> Ref<Platform> {
|
||||
unsafe {
|
||||
let plat = BNGetFunctionPlatform(self.handle);
|
||||
Platform::ref_from_raw(plat)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view(&self) -> Ref<BinaryView> {
|
||||
unsafe {
|
||||
let view = BNGetFunctionData(self.handle);
|
||||
BinaryView::from_raw(view)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbol(&self) -> Ref<Symbol> {
|
||||
unsafe {
|
||||
let sym = BNGetFunctionSymbol(self.handle);
|
||||
Symbol::ref_from_raw(sym)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&self) -> u64 {
|
||||
unsafe { BNGetFunctionStart(self.handle) }
|
||||
}
|
||||
|
||||
pub fn highest_address(&self) -> u64 {
|
||||
unsafe { BNGetFunctionHighestAddress(self.handle) }
|
||||
}
|
||||
|
||||
pub fn address_ranges(&self) -> Array<AddressRange> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let addresses = BNGetFunctionAddressRanges(self.handle, &mut count);
|
||||
|
||||
Array::new(addresses, count, ())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn comment(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNGetFunctionComment(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn set_comment<S: BnStrCompatible>(&self, comment: S) {
|
||||
let raw = comment.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
BNSetFunctionComment(self.handle, raw.as_ref().as_ptr() as *mut _);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn comment_at(&self, addr: u64) -> BnString {
|
||||
unsafe { BnString::from_raw(BNGetCommentForAddress(self.handle, addr)) }
|
||||
}
|
||||
|
||||
pub fn set_comment_at<S: BnStrCompatible>(&self, addr: u64, comment: S) {
|
||||
let raw = comment.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
BNSetCommentForAddress(self.handle, addr, raw.as_ref().as_ptr() as *mut _);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn basic_blocks(&self) -> Array<BasicBlock<NativeBlock>> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let blocks = BNGetFunctionBasicBlockList(self.handle, &mut count);
|
||||
let context = NativeBlock { _priv: () };
|
||||
|
||||
Array::new(blocks, count, context)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn basic_block_containing(
|
||||
&self,
|
||||
arch: &CoreArchitecture,
|
||||
addr: u64,
|
||||
) -> Option<Ref<BasicBlock<NativeBlock>>> {
|
||||
unsafe {
|
||||
let block = BNGetFunctionBasicBlockAtAddress(self.handle, arch.0, addr);
|
||||
let context = NativeBlock { _priv: () };
|
||||
|
||||
if block.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Ref::new(BasicBlock::from_raw(block, context)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_variable_name(&self, var: &Variable) -> BnString {
|
||||
unsafe {
|
||||
let raw_var = var.raw();
|
||||
let raw_name = BNGetVariableName(self.handle, &raw_var);
|
||||
BnString::from_raw(raw_name)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn medium_level_il(&self) -> Result<Ref<mlil::MediumLevelILFunction>, ()> {
|
||||
unsafe {
|
||||
let mlil = BNGetFunctionMediumLevelIL(self.handle);
|
||||
|
||||
if mlil.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(Ref::new(mlil::MediumLevelILFunction::from_raw(mlil)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn low_level_il(&self) -> Result<Ref<llil::RegularFunction<CoreArchitecture>>, ()> {
|
||||
unsafe {
|
||||
let llil = BNGetFunctionLowLevelIL(self.handle);
|
||||
|
||||
if llil.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(Ref::new(llil::RegularFunction::from_raw(self.arch(), llil)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lifted_il(&self) -> Result<Ref<llil::LiftedFunction<CoreArchitecture>>, ()> {
|
||||
unsafe {
|
||||
let llil = BNGetFunctionLiftedIL(self.handle);
|
||||
|
||||
if llil.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(Ref::new(llil::LiftedFunction::from_raw(self.arch(), llil)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn return_type(&self) -> Conf<Ref<Type>> {
|
||||
let result = unsafe { BNGetFunctionReturnType(self.handle) };
|
||||
|
||||
Conf::new(
|
||||
unsafe { Type::ref_from_raw(result.type_) },
|
||||
result.confidence,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn function_type(&self) -> Ref<Type> {
|
||||
unsafe { Type::ref_from_raw(BNGetFunctionType(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn set_user_type(&self, t: Type) {
|
||||
unsafe {
|
||||
BNSetFunctionUserType(self.handle, t.handle);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stack_layout(&self) -> Array<NamedTypedVariable> {
|
||||
let mut count = 0;
|
||||
unsafe {
|
||||
let variables = BNGetStackLayout(self.handle, &mut count);
|
||||
Array::new(variables, count, ())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_imported_types(&self, sym: &Symbol, t: Option<&Type>) {
|
||||
unsafe {
|
||||
BNApplyImportedTypes(
|
||||
self.handle,
|
||||
sym.handle,
|
||||
if let Some(t) = t {
|
||||
t.handle
|
||||
} else {
|
||||
core::ptr::null_mut()
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Function {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"<func '{}' ({}) {:x}>",
|
||||
self.symbol().full_name(),
|
||||
self.platform().name(),
|
||||
self.start()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for Function {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for Function {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewFunctionReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeFunction(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for Function {
|
||||
type Raw = *mut BNFunction;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for Function {
|
||||
unsafe fn free(raw: *mut *mut BNFunction, count: usize, _context: &()) {
|
||||
BNFreeFunctionList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for Function {
|
||||
type Wrapped = Guard<'a, Function>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a *mut BNFunction, context: &'a ()) -> Guard<'a, Function> {
|
||||
Guard::new(Function { handle: *raw }, context)
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////
|
||||
// AddressRange
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct AddressRange(pub(crate) BNAddressRange);
|
||||
|
||||
impl AddressRange {
|
||||
pub fn start(&self) -> u64 {
|
||||
self.0.start
|
||||
}
|
||||
|
||||
pub fn end(&self) -> u64 {
|
||||
self.0.end
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for AddressRange {
|
||||
type Raw = BNAddressRange;
|
||||
type Context = ();
|
||||
}
|
||||
unsafe impl CoreOwnedArrayProvider for AddressRange {
|
||||
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
|
||||
BNFreeAddressRanges(raw);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for AddressRange {
|
||||
type Wrapped = &'a AddressRange;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
mem::transmute(raw)
|
||||
}
|
||||
}
|
||||
107
src/functionrecognizer.rs
Normal file
107
src/functionrecognizer.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use crate::architecture::Architecture;
|
||||
use crate::{
|
||||
architecture::CoreArchitecture, binaryview::BinaryView, function::Function, llil, mlil,
|
||||
};
|
||||
use binaryninjacore_sys::*;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
pub trait FunctionRecognizer {
|
||||
fn recognize_low_level_il(
|
||||
&self,
|
||||
_bv: &BinaryView,
|
||||
_func: &Function,
|
||||
_llil: &llil::RegularFunction<CoreArchitecture>,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn recognize_medium_level_il(
|
||||
&self,
|
||||
_bv: &BinaryView,
|
||||
_func: &Function,
|
||||
_mlil: &mlil::MediumLevelILFunction,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn create_function_recognizer_registration<R>(recognizer: R) -> BNFunctionRecognizer
|
||||
where
|
||||
R: 'static + FunctionRecognizer + Send + Sync + Sized,
|
||||
{
|
||||
#[repr(C)]
|
||||
struct FunctionRecognizerHandlerContext<R>
|
||||
where
|
||||
R: 'static + FunctionRecognizer + Send + Sync,
|
||||
{
|
||||
recognizer: R,
|
||||
}
|
||||
|
||||
extern "C" fn cb_recognize_low_level_il<R>(
|
||||
ctxt: *mut c_void,
|
||||
bv: *mut BNBinaryView,
|
||||
func: *mut BNFunction,
|
||||
llil: *mut BNLowLevelILFunction,
|
||||
) -> bool
|
||||
where
|
||||
R: 'static + FunctionRecognizer + Send + Sync,
|
||||
{
|
||||
let custom_handler = unsafe { &*(ctxt as *mut R) };
|
||||
let bv = unsafe { BinaryView::from_raw(BNNewViewReference(bv)) };
|
||||
let arch = unsafe { BNGetFunctionArchitecture(func) };
|
||||
let func = unsafe { Function::from_raw(BNNewFunctionReference(func)) };
|
||||
if arch.is_null() {
|
||||
return false;
|
||||
}
|
||||
let arch = unsafe { CoreArchitecture::from_raw(arch) };
|
||||
let llil = unsafe { llil::RegularFunction::from_raw(arch, llil) };
|
||||
custom_handler.recognize_low_level_il(bv.as_ref(), func.as_ref(), &llil)
|
||||
}
|
||||
|
||||
extern "C" fn cb_recognize_medium_level_il<R>(
|
||||
ctxt: *mut c_void,
|
||||
bv: *mut BNBinaryView,
|
||||
func: *mut BNFunction,
|
||||
mlil: *mut BNMediumLevelILFunction,
|
||||
) -> bool
|
||||
where
|
||||
R: 'static + FunctionRecognizer + Send + Sync,
|
||||
{
|
||||
let custom_handler = unsafe { &*(ctxt as *mut R) };
|
||||
let bv = unsafe { BinaryView::from_raw(BNNewViewReference(bv)) };
|
||||
let func = unsafe { Function::from_raw(BNNewFunctionReference(func)) };
|
||||
let mlil = unsafe { mlil::MediumLevelILFunction::from_raw(mlil) };
|
||||
custom_handler.recognize_medium_level_il(bv.as_ref(), func.as_ref(), &mlil)
|
||||
}
|
||||
|
||||
let recognizer = FunctionRecognizerHandlerContext { recognizer };
|
||||
let raw = Box::into_raw(Box::new(recognizer));
|
||||
BNFunctionRecognizer {
|
||||
context: raw as *mut _,
|
||||
recognizeLowLevelIL: Some(cb_recognize_low_level_il::<R>),
|
||||
recognizeMediumLevelIL: Some(cb_recognize_medium_level_il::<R>),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_global_function_recognizer<R>(recognizer: R)
|
||||
where
|
||||
R: 'static + FunctionRecognizer + Send + Sync + Sized,
|
||||
{
|
||||
let mut recognizer = create_function_recognizer_registration::<R>(recognizer);
|
||||
unsafe {
|
||||
BNRegisterGlobalFunctionRecognizer(&mut recognizer as *mut _);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn register_arch_function_recognizer<R>(arch: &CoreArchitecture, recognizer: R)
|
||||
where
|
||||
R: 'static + FunctionRecognizer + Send + Sync + Sized,
|
||||
{
|
||||
let mut recognizer = create_function_recognizer_registration::<R>(recognizer);
|
||||
unsafe {
|
||||
BNRegisterArchitectureFunctionRecognizer(
|
||||
arch.handle().as_ref().0,
|
||||
&mut recognizer as *mut _,
|
||||
);
|
||||
}
|
||||
}
|
||||
158
src/headless.rs
Normal file
158
src/headless.rs
Normal file
@@ -0,0 +1,158 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
binaryview,
|
||||
metadata::Metadata,
|
||||
rc::{self, Ref},
|
||||
string::BnStrCompatible,
|
||||
};
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn binja_path() -> PathBuf {
|
||||
use std::ffi::{CStr, OsStr};
|
||||
use std::mem;
|
||||
use std::os::raw;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
#[repr(C)]
|
||||
struct DlInfo {
|
||||
dli_fname: *const raw::c_char,
|
||||
dli_fbase: *mut raw::c_void,
|
||||
dli_sname: *const raw::c_char,
|
||||
dli_saddr: *mut raw::c_void,
|
||||
}
|
||||
|
||||
if let Ok(p) = env::var("BINJA_DIR") {
|
||||
return PathBuf::from(p);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn dladdr(addr: *mut raw::c_void, info: *mut DlInfo) -> raw::c_int;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let mut info: DlInfo = mem::zeroed();
|
||||
|
||||
if dladdr(BNSetBundledPluginDirectory as *mut _, &mut info) == 0 {
|
||||
panic!("Failed to find libbinaryninjacore path!");
|
||||
}
|
||||
|
||||
if info.dli_fname.is_null() {
|
||||
panic!("Failed to find libbinaryninjacore path!");
|
||||
}
|
||||
|
||||
let path = CStr::from_ptr(info.dli_fname);
|
||||
let path = OsStr::from_bytes(path.to_bytes());
|
||||
let mut path = PathBuf::from(path);
|
||||
|
||||
path.pop();
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn binja_path() -> PathBuf {
|
||||
PathBuf::from(env::var("PROGRAMFILES").unwrap()).join("Vector35\\BinaryNinja\\")
|
||||
}
|
||||
|
||||
use binaryninjacore_sys::{BNInitPlugins, BNInitRepoPlugins, BNSetBundledPluginDirectory};
|
||||
|
||||
/// Loads plugins, core architecture, platform, etc.
|
||||
///
|
||||
/// ⚠️ Important! Must be called at the beginning of scripts. Plugins do not need to call this. ⚠️
|
||||
///
|
||||
/// You can instead call this through [`Session`] or [`script_helper`]
|
||||
pub fn init() {
|
||||
unsafe {
|
||||
let path = binja_path().join("plugins").into_os_string();
|
||||
let path = path.into_string().unwrap();
|
||||
|
||||
BNSetBundledPluginDirectory(path.as_str().into_bytes_with_nul().as_ptr() as *mut _);
|
||||
BNInitPlugins(true);
|
||||
BNInitRepoPlugins();
|
||||
}
|
||||
}
|
||||
|
||||
/// Unloads plugins, stops all worker threads, and closes open logs
|
||||
///
|
||||
/// ⚠️ Important! Must be called at the end of scripts. ⚠️
|
||||
pub fn shutdown() {
|
||||
unsafe { binaryninjacore_sys::BNShutdown() };
|
||||
}
|
||||
|
||||
/// Prelued-postlued helper function (calls [`init`] and [`shutdown`] for you)
|
||||
/// ```rust
|
||||
/// binaryninja::headless::script_helper(|| {
|
||||
/// binaryninja::load("/bin/cat")
|
||||
/// .expect("Couldn't open `/bin/cat`")
|
||||
/// .iter()
|
||||
/// .for_each(|func| println!(" `{}`", func.symbol().full_name()));
|
||||
/// });
|
||||
/// ```
|
||||
pub fn script_helper(func: fn()) {
|
||||
init();
|
||||
func();
|
||||
shutdown();
|
||||
}
|
||||
|
||||
/// Wrapper for [`init`] and [`shutdown`]. Instantiating this at the top of your script will initialize everything correctly and then clean itself up at exit as well.
|
||||
pub struct Session {}
|
||||
|
||||
impl Session {
|
||||
pub fn new() -> Self {
|
||||
init();
|
||||
Self {}
|
||||
}
|
||||
|
||||
/// ```rust
|
||||
/// let headless_session = binaryninja::headless::Session::new();
|
||||
///
|
||||
/// let bv = headless_session.load("/bin/cat").expect("Couldn't open `/bin/cat`");
|
||||
/// ```
|
||||
pub fn load(&self, filename: &str) -> Option<rc::Ref<binaryview::BinaryView>> {
|
||||
crate::load(filename)
|
||||
}
|
||||
|
||||
/// ```rust
|
||||
/// let settings = [("analysis.linearSweep.autorun", false)].into();
|
||||
/// let headless_session = binaryninja::headless::Session::new();
|
||||
///
|
||||
/// let bv = headless_session.load_with_options("/bin/cat", true, Some(settings))
|
||||
/// .expect("Couldn't open `/bin/cat`");
|
||||
/// ```
|
||||
pub fn load_with_options(
|
||||
&self,
|
||||
filename: &str,
|
||||
update_analysis_and_wait: bool,
|
||||
options: Option<Ref<Metadata>>,
|
||||
) -> Option<rc::Ref<binaryview::BinaryView>> {
|
||||
crate::load_with_options(filename, update_analysis_and_wait, options)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Session {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Session {
|
||||
fn drop(&mut self) {
|
||||
shutdown()
|
||||
}
|
||||
}
|
||||
575
src/interaction.rs
Normal file
575
src/interaction.rs
Normal file
@@ -0,0 +1,575 @@
|
||||
// Copyright 2022-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Interfaces for asking the user for information: forms, opening files, etc.
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::binaryview::BinaryView;
|
||||
use crate::rc::Ref;
|
||||
use crate::string::{BnStr, BnStrCompatible, BnString};
|
||||
|
||||
pub fn get_text_line_input(prompt: &str, title: &str) -> Option<String> {
|
||||
let mut value: *mut libc::c_char = std::ptr::null_mut();
|
||||
|
||||
let result = unsafe {
|
||||
BNGetTextLineInput(
|
||||
&mut value,
|
||||
prompt.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
title.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
)
|
||||
};
|
||||
if !result {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(unsafe { BnString::from_raw(value).to_string() })
|
||||
}
|
||||
|
||||
pub fn get_integer_input(prompt: &str, title: &str) -> Option<i64> {
|
||||
let mut value: i64 = 0;
|
||||
|
||||
let result = unsafe {
|
||||
BNGetIntegerInput(
|
||||
&mut value,
|
||||
prompt.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
title.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
)
|
||||
};
|
||||
|
||||
if !result {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(value)
|
||||
}
|
||||
|
||||
pub fn get_address_input(prompt: &str, title: &str) -> Option<u64> {
|
||||
let mut value: u64 = 0;
|
||||
|
||||
let result = unsafe {
|
||||
BNGetAddressInput(
|
||||
&mut value,
|
||||
prompt.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
title.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
std::ptr::null_mut(),
|
||||
0,
|
||||
)
|
||||
};
|
||||
|
||||
if !result {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(value)
|
||||
}
|
||||
|
||||
pub fn get_open_filename_input(prompt: &str, extension: &str) -> Option<PathBuf> {
|
||||
let mut value: *mut libc::c_char = std::ptr::null_mut();
|
||||
|
||||
let result = unsafe {
|
||||
BNGetOpenFileNameInput(
|
||||
&mut value,
|
||||
prompt.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
extension.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
)
|
||||
};
|
||||
if !result {
|
||||
return None;
|
||||
}
|
||||
|
||||
let string = unsafe { BnString::from_raw(value) };
|
||||
Some(PathBuf::from(string.as_str()))
|
||||
}
|
||||
|
||||
pub fn get_save_filename_input(prompt: &str, title: &str, default_name: &str) -> Option<PathBuf> {
|
||||
let mut value: *mut libc::c_char = std::ptr::null_mut();
|
||||
|
||||
let result = unsafe {
|
||||
BNGetSaveFileNameInput(
|
||||
&mut value,
|
||||
prompt.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
title.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
default_name.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
)
|
||||
};
|
||||
if !result {
|
||||
return None;
|
||||
}
|
||||
|
||||
let string = unsafe { BnString::from_raw(value) };
|
||||
Some(PathBuf::from(string.as_str()))
|
||||
}
|
||||
|
||||
pub fn get_directory_name_input(prompt: &str, default_name: &str) -> Option<PathBuf> {
|
||||
let mut value: *mut libc::c_char = std::ptr::null_mut();
|
||||
|
||||
let result = unsafe {
|
||||
BNGetDirectoryNameInput(
|
||||
&mut value,
|
||||
prompt.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
default_name.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
)
|
||||
};
|
||||
if !result {
|
||||
return None;
|
||||
}
|
||||
|
||||
let string = unsafe { BnString::from_raw(value) };
|
||||
Some(PathBuf::from(string.as_str()))
|
||||
}
|
||||
|
||||
pub type MessageBoxButtonSet = BNMessageBoxButtonSet;
|
||||
pub type MessageBoxIcon = BNMessageBoxIcon;
|
||||
pub type MessageBoxButtonResult = BNMessageBoxButtonResult;
|
||||
pub fn show_message_box(
|
||||
title: &str,
|
||||
text: &str,
|
||||
buttons: MessageBoxButtonSet,
|
||||
icon: MessageBoxIcon,
|
||||
) -> MessageBoxButtonResult {
|
||||
unsafe {
|
||||
BNShowMessageBox(
|
||||
title.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
text.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
buttons,
|
||||
icon,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum FormResponses {
|
||||
None,
|
||||
String(String),
|
||||
Integer(i64),
|
||||
Address(u64),
|
||||
Index(usize),
|
||||
}
|
||||
|
||||
enum FormData {
|
||||
Label {
|
||||
_text: BnString,
|
||||
},
|
||||
Text {
|
||||
_prompt: BnString,
|
||||
_default: Option<BnString>,
|
||||
},
|
||||
Choice {
|
||||
_prompt: BnString,
|
||||
_choices: Vec<BnString>,
|
||||
_raw: Vec<*const c_char>,
|
||||
},
|
||||
File {
|
||||
_prompt: BnString,
|
||||
_ext: BnString,
|
||||
_default: Option<BnString>,
|
||||
},
|
||||
FileSave {
|
||||
_prompt: BnString,
|
||||
_ext: BnString,
|
||||
_default_name: BnString,
|
||||
_default: Option<BnString>,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct FormInputBuilder {
|
||||
fields: Vec<BNFormInputField>,
|
||||
data: Vec<FormData>,
|
||||
}
|
||||
|
||||
impl FormInputBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
fields: vec![],
|
||||
data: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Form Field: Text output
|
||||
pub fn label_field(mut self, text: &str) -> Self {
|
||||
let text = BnString::new(text);
|
||||
|
||||
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
|
||||
result.type_ = BNFormInputFieldType::LabelFormField;
|
||||
result.hasDefault = false;
|
||||
result.prompt = text.as_ref().as_ptr() as *const c_char;
|
||||
self.fields.push(result);
|
||||
|
||||
self.data.push(FormData::Label { _text: text });
|
||||
self
|
||||
}
|
||||
|
||||
/// Form Field: Vertical spacing
|
||||
pub fn seperator_field(mut self) -> Self {
|
||||
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
|
||||
result.type_ = BNFormInputFieldType::SeparatorFormField;
|
||||
result.hasDefault = false;
|
||||
self.fields.push(result);
|
||||
self
|
||||
}
|
||||
|
||||
/// Form Field: Prompt for a string value
|
||||
pub fn text_field(mut self, prompt: &str, default: Option<&str>) -> Self {
|
||||
let prompt = BnString::new(prompt);
|
||||
let default = default.map(BnString::new);
|
||||
|
||||
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
|
||||
result.type_ = BNFormInputFieldType::TextLineFormField;
|
||||
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
|
||||
result.hasDefault = default.is_some();
|
||||
if let Some(ref default) = default {
|
||||
result.stringDefault = default.as_ref().as_ptr() as *const c_char;
|
||||
}
|
||||
self.fields.push(result);
|
||||
|
||||
self.data.push(FormData::Text {
|
||||
_prompt: prompt,
|
||||
_default: default,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Form Field: Prompt for multi-line string value
|
||||
pub fn multiline_field(mut self, prompt: &str, default: Option<&str>) -> Self {
|
||||
let prompt = BnString::new(prompt);
|
||||
let default = default.map(BnString::new);
|
||||
|
||||
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
|
||||
result.type_ = BNFormInputFieldType::MultilineTextFormField;
|
||||
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
|
||||
result.hasDefault = default.is_some();
|
||||
if let Some(ref default) = default {
|
||||
result.stringDefault = default.as_ref().as_ptr() as *const c_char;
|
||||
}
|
||||
self.fields.push(result);
|
||||
|
||||
self.data.push(FormData::Text {
|
||||
_prompt: prompt,
|
||||
_default: default,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Form Field: Prompt for an integer
|
||||
pub fn integer_field(mut self, prompt: &str, default: Option<i64>) -> Self {
|
||||
let prompt = BnString::new(prompt);
|
||||
|
||||
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
|
||||
result.type_ = BNFormInputFieldType::IntegerFormField;
|
||||
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
|
||||
result.hasDefault = default.is_some();
|
||||
if let Some(default) = default {
|
||||
result.intDefault = default;
|
||||
}
|
||||
self.fields.push(result);
|
||||
|
||||
self.data.push(FormData::Label { _text: prompt });
|
||||
self
|
||||
}
|
||||
|
||||
/// Form Field: Prompt for an address
|
||||
pub fn address_field(
|
||||
mut self,
|
||||
prompt: &str,
|
||||
view: Option<Ref<BinaryView>>,
|
||||
current_address: Option<u64>,
|
||||
default: Option<u64>,
|
||||
) -> Self {
|
||||
let prompt = BnString::new(prompt);
|
||||
|
||||
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
|
||||
result.type_ = BNFormInputFieldType::AddressFormField;
|
||||
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
|
||||
if let Some(view) = view {
|
||||
result.view = view.handle;
|
||||
}
|
||||
result.currentAddress = current_address.unwrap_or(0);
|
||||
result.hasDefault = default.is_some();
|
||||
if let Some(default) = default {
|
||||
result.addressDefault = default;
|
||||
}
|
||||
self.fields.push(result);
|
||||
|
||||
self.data.push(FormData::Label { _text: prompt });
|
||||
self
|
||||
}
|
||||
|
||||
/// Form Field: Prompt for a choice from provided options
|
||||
pub fn choice_field(mut self, prompt: &str, choices: &[&str], default: Option<usize>) -> Self {
|
||||
let prompt = BnString::new(prompt);
|
||||
let choices: Vec<BnString> = choices.iter().map(|&s| BnString::new(s)).collect();
|
||||
|
||||
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
|
||||
result.type_ = BNFormInputFieldType::ChoiceFormField;
|
||||
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
|
||||
let mut raw_choices: Vec<*const c_char> = choices
|
||||
.iter()
|
||||
.map(|c| c.as_ref().as_ptr() as *const c_char)
|
||||
.collect();
|
||||
result.choices = raw_choices.as_mut_ptr();
|
||||
result.count = choices.len();
|
||||
result.hasDefault = default.is_some();
|
||||
if let Some(default) = default {
|
||||
result.indexDefault = default;
|
||||
}
|
||||
self.fields.push(result);
|
||||
|
||||
self.data.push(FormData::Choice {
|
||||
_prompt: prompt,
|
||||
_choices: choices,
|
||||
_raw: raw_choices,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Form Field: Prompt for file to open
|
||||
pub fn open_file_field(
|
||||
mut self,
|
||||
prompt: &str,
|
||||
ext: Option<&str>,
|
||||
default: Option<&str>,
|
||||
) -> Self {
|
||||
let prompt = BnString::new(prompt);
|
||||
let ext = if let Some(ext) = ext {
|
||||
BnString::new(ext)
|
||||
} else {
|
||||
BnString::new("")
|
||||
};
|
||||
let default = default.map(BnString::new);
|
||||
|
||||
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
|
||||
result.type_ = BNFormInputFieldType::OpenFileNameFormField;
|
||||
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
|
||||
result.ext = ext.as_ref().as_ptr() as *const c_char;
|
||||
result.hasDefault = default.is_some();
|
||||
if let Some(ref default) = default {
|
||||
result.stringDefault = default.as_ref().as_ptr() as *const c_char;
|
||||
}
|
||||
self.fields.push(result);
|
||||
|
||||
self.data.push(FormData::File {
|
||||
_prompt: prompt,
|
||||
_ext: ext,
|
||||
_default: default,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Form Field: Prompt for file to save to
|
||||
pub fn save_file_field(
|
||||
mut self,
|
||||
prompt: &str,
|
||||
ext: Option<&str>,
|
||||
default_name: Option<&str>,
|
||||
default: Option<&str>,
|
||||
) -> Self {
|
||||
let prompt = BnString::new(prompt);
|
||||
let ext = if let Some(ext) = ext {
|
||||
BnString::new(ext)
|
||||
} else {
|
||||
BnString::new("")
|
||||
};
|
||||
let default_name = if let Some(default_name) = default_name {
|
||||
BnString::new(default_name)
|
||||
} else {
|
||||
BnString::new("")
|
||||
};
|
||||
let default = default.map(BnString::new);
|
||||
|
||||
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
|
||||
result.type_ = BNFormInputFieldType::SaveFileNameFormField;
|
||||
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
|
||||
result.ext = ext.as_ref().as_ptr() as *const c_char;
|
||||
result.defaultName = default_name.as_ref().as_ptr() as *const c_char;
|
||||
result.hasDefault = default.is_some();
|
||||
if let Some(ref default) = default {
|
||||
result.stringDefault = default.as_ref().as_ptr() as *const c_char;
|
||||
}
|
||||
self.fields.push(result);
|
||||
|
||||
self.data.push(FormData::FileSave {
|
||||
_prompt: prompt,
|
||||
_ext: ext,
|
||||
_default_name: default_name,
|
||||
_default: default,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Form Field: Prompt for directory name
|
||||
pub fn directory_name_field(
|
||||
mut self,
|
||||
prompt: &str,
|
||||
default_name: Option<&str>,
|
||||
default: Option<&str>,
|
||||
) -> Self {
|
||||
let prompt = BnString::new(prompt);
|
||||
let default_name = if let Some(default_name) = default_name {
|
||||
BnString::new(default_name)
|
||||
} else {
|
||||
BnString::new("")
|
||||
};
|
||||
let default = default.map(BnString::new);
|
||||
|
||||
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
|
||||
result.type_ = BNFormInputFieldType::DirectoryNameFormField;
|
||||
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
|
||||
result.defaultName = default_name.as_ref().as_ptr() as *const c_char;
|
||||
result.hasDefault = default.is_some();
|
||||
if let Some(ref default) = default {
|
||||
result.stringDefault = default.as_ref().as_ptr() as *const c_char;
|
||||
}
|
||||
self.fields.push(result);
|
||||
|
||||
self.data.push(FormData::File {
|
||||
_prompt: prompt,
|
||||
_ext: default_name,
|
||||
_default: default,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Prompts the user for a set of inputs specified in `fields` with given title.
|
||||
/// The fields parameter is a list which can contain the following types:
|
||||
///
|
||||
/// This API is flexible and works both in the UI via a pop-up dialog and on the command-line.
|
||||
///
|
||||
/// ```
|
||||
/// let responses = interaction::FormInputBuilder::new()
|
||||
/// .text_field("First Name", None)
|
||||
/// .text_field("Last Name", None)
|
||||
/// .choice_field(
|
||||
/// "Favorite Food",
|
||||
/// &vec![
|
||||
/// "Pizza",
|
||||
/// "Also Pizza",
|
||||
/// "Also Pizza",
|
||||
/// "Yummy Pizza",
|
||||
/// "Wrong Answer",
|
||||
/// ],
|
||||
/// Some(0),
|
||||
/// )
|
||||
/// .get_form_input("Form Title");
|
||||
///
|
||||
/// let food = match responses[2] {
|
||||
/// Index(0) => "Pizza",
|
||||
/// Index(1) => "Also Pizza",
|
||||
/// Index(2) => "Also Pizza",
|
||||
/// Index(3) => "Wrong Answer",
|
||||
/// _ => panic!("This person doesn't like pizza?!?"),
|
||||
/// };
|
||||
///
|
||||
/// let interaction::FormResponses::String(last_name) = responses[0];
|
||||
/// let interaction::FormResponses::String(first_name) = responses[1];
|
||||
///
|
||||
/// println!("{} {} likes {}", &first_name, &last_name, food);
|
||||
/// ```
|
||||
pub fn get_form_input(&mut self, title: &str) -> Vec<FormResponses> {
|
||||
if unsafe {
|
||||
BNGetFormInput(
|
||||
self.fields.as_mut_ptr(),
|
||||
self.fields.len(),
|
||||
title.into_bytes_with_nul().as_ptr() as *const _,
|
||||
)
|
||||
} {
|
||||
let result = self
|
||||
.fields
|
||||
.iter()
|
||||
.map(|form_field| match form_field.type_ {
|
||||
BNFormInputFieldType::LabelFormField
|
||||
| BNFormInputFieldType::SeparatorFormField => FormResponses::None,
|
||||
|
||||
BNFormInputFieldType::TextLineFormField
|
||||
| BNFormInputFieldType::MultilineTextFormField
|
||||
| BNFormInputFieldType::OpenFileNameFormField
|
||||
| BNFormInputFieldType::SaveFileNameFormField
|
||||
| BNFormInputFieldType::DirectoryNameFormField => {
|
||||
FormResponses::String(unsafe {
|
||||
BnStr::from_raw(form_field.stringResult).to_string()
|
||||
})
|
||||
}
|
||||
|
||||
BNFormInputFieldType::IntegerFormField => {
|
||||
FormResponses::Integer(form_field.intResult)
|
||||
}
|
||||
BNFormInputFieldType::AddressFormField => {
|
||||
FormResponses::Address(form_field.addressResult)
|
||||
}
|
||||
BNFormInputFieldType::ChoiceFormField => {
|
||||
FormResponses::Index(form_field.indexResult)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
unsafe { BNFreeFormInputResults(self.fields.as_mut_ptr(), self.fields.len()) };
|
||||
result
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FormInputBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
struct TaskContext<F: Fn(Box<dyn Fn(usize, usize) -> Result<(), ()>>)>(F);
|
||||
|
||||
pub fn run_progress_dialog<F: Fn(Box<dyn Fn(usize, usize) -> Result<(), ()>>)>(
|
||||
title: &str,
|
||||
can_cancel: bool,
|
||||
task: F,
|
||||
) -> Result<(), ()> {
|
||||
let mut ctxt = TaskContext::<F>(task);
|
||||
|
||||
unsafe extern "C" fn cb_task<F: Fn(Box<dyn Fn(usize, usize) -> Result<(), ()>>)>(
|
||||
ctxt: *mut c_void,
|
||||
progress: Option<unsafe extern "C" fn(*mut c_void, usize, usize) -> bool>,
|
||||
progress_ctxt: *mut c_void,
|
||||
) {
|
||||
ffi_wrap!("run_progress_dialog", {
|
||||
let context = ctxt as *mut TaskContext<F>;
|
||||
let progress_fn = Box::new(move |cur: usize, max: usize| -> Result<(), ()> {
|
||||
match progress {
|
||||
Some(func) => {
|
||||
if (func)(progress_ctxt, cur, max) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
None => Ok(()),
|
||||
}
|
||||
});
|
||||
((*context).0)(progress_fn);
|
||||
})
|
||||
}
|
||||
|
||||
if unsafe {
|
||||
BNRunProgressDialog(
|
||||
title.into_bytes_with_nul().as_ptr() as *mut _,
|
||||
can_cancel,
|
||||
Some(cb_task::<F>),
|
||||
&mut ctxt as *mut _ as *mut c_void,
|
||||
)
|
||||
} {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
428
src/lib.rs
Normal file
428
src/lib.rs
Normal file
@@ -0,0 +1,428 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// TODO : These clippy-allow are bad and needs to be removed
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
#![allow(clippy::result_unit_err)]
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![doc(html_root_url = "https://dev-rust.binary.ninja/binaryninja/")]
|
||||
#![doc(html_favicon_url = "/favicon.ico")]
|
||||
#![doc(html_logo_url = "/logo.png")]
|
||||
#![doc(issue_tracker_base_url = "https://github.com/Vector35/binaryninja-api/issues/")]
|
||||
|
||||
//! This crate is the official [Binary Ninja] API wrapper for Rust.
|
||||
//!
|
||||
//! [Binary Ninja] is an interactive disassembler, decompiler, and binary analysis platform for reverse engineers, malware analysts, vulnerability researchers, and software developers that runs on Windows, macOS, and Linux. Our extensive API can be used to create and customize loaders, add or augment architectures, customize the UI, or automate any workflow (types, patches, decompilation...anything!).
|
||||
//!
|
||||
//! If you're just getting started with [Binary Ninja], you may wish to check out the [Getting Started Guide]
|
||||
//!
|
||||
//! If you have questions, we'd love to answer them in [our public Slack], and if you find any issues, please [file an issue] or [submit a PR].
|
||||
//!
|
||||
//! ---
|
||||
//! # Warning
|
||||
//! <img align="right" src="../under_construction.png" width="175" height="175">
|
||||
//!
|
||||
//! > ⚠️ **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.**
|
||||
//!
|
||||
//! > ⚠️ This project runs on Rust version `stable-2022-12-15`
|
||||
//!
|
||||
//! ---
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! There are two distinct ways to use this crate:
|
||||
//! 1. [Writing a Plugin](#writing-a-plugin)
|
||||
//! 2. [Writing a Script](#writing-a-script)
|
||||
//!
|
||||
//! ## Writing a Plugin
|
||||
//!
|
||||
//! Create a new library (`cargo new --lib <plugin-name>`) and include the following in your `Cargo.toml`:
|
||||
//!
|
||||
//! ```
|
||||
//! [lib]
|
||||
//! crate-type = ["cdylib"]
|
||||
//!
|
||||
//! [dependencies]
|
||||
//! binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
|
||||
//! ```
|
||||
//!
|
||||
//! In `lib.rs` you'll need to provide a `CorePluginInit` or `UIPluginInit` function for Binary Ninja to call.
|
||||
//!
|
||||
//! See [`command`] for the different actions you can provide and how to register your plugin with [Binary Ninja].
|
||||
//!
|
||||
//! ## Writing a Script:
|
||||
//!
|
||||
//! "Scripts" are binaries (`cargo new --bin <script-name>`), and have some specific requirements:
|
||||
//!
|
||||
//! ### build.rs
|
||||
//!
|
||||
//! Because [the only official method of providing linker arguments to a crate is through that crate's `build.rs`], all scripts need to provide their own `build.rs` so they can probably link with Binary Ninja.
|
||||
//!
|
||||
//! The most up-to-date version of the suggested [`build.rs` is here].
|
||||
//!
|
||||
//! ### `main.rs`
|
||||
//! Standalone binaries need to initialize Binary Ninja before they can work. You can do this through [`headless::Session`], [`headless::script_helper`], or [`headless::init()`] at start and [`headless::shutdown()`] at shutdown.
|
||||
//! ```rust
|
||||
//! fn main() {
|
||||
//! // This loads all the core architecture, platform, etc plugins
|
||||
//! // Standalone executables need to call this, but plugins do not
|
||||
//! let headless_session = binaryninja::headless::Session::new();
|
||||
//!
|
||||
//! println!("Loading binary...");
|
||||
//! let bv = headless_session.load("/bin/cat").expect("Couldn't open `/bin/cat`");
|
||||
//!
|
||||
//! // Your code here...
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### `Cargo.toml`
|
||||
//! ```
|
||||
//! [dependencies]
|
||||
//! binaryninja = { git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
|
||||
//! ```
|
||||
//!
|
||||
//! See the [examples] on GitHub for more comprehensive examples.
|
||||
//!
|
||||
//! [Binary Ninja]: https://binary.ninja/
|
||||
//! [Getting Started Guide]: https://docs.binary.ninja/
|
||||
//! [our public Slack]: https://join.slack.com/t/binaryninja/shared_invite/zt-3u4vu3ja-IGUF4ZWNlD7ER2ulvICvuQ
|
||||
//! [file an issue]: https://github.com/Vector35/binaryninja-api/issues
|
||||
//! [submit a PR]: https://github.com/Vector35/binaryninja-api/pulls
|
||||
//! [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
|
||||
//! [`build.rs` is here]: https://github.com/Vector35/binaryninja-api/blob/dev/rust/examples/template/build.rs
|
||||
//! [examples]: https://github.com/Vector35/binaryninja-api/tree/dev/rust/examples
|
||||
//!
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[doc(hidden)]
|
||||
pub extern crate binaryninjacore_sys;
|
||||
extern crate libc;
|
||||
#[cfg(feature = "rayon")]
|
||||
extern crate rayon;
|
||||
|
||||
// TODO
|
||||
// move some options to results
|
||||
// replace `fn handle` with `AsRef` bounds
|
||||
// possible values
|
||||
// arch rework
|
||||
// cc possible values
|
||||
// bv reorg
|
||||
// core fileaccessor (for bv saving)
|
||||
// platform cc
|
||||
|
||||
#[macro_use]
|
||||
mod ffi;
|
||||
|
||||
pub mod architecture;
|
||||
pub mod backgroundtask;
|
||||
pub mod basicblock;
|
||||
pub mod binaryreader;
|
||||
pub mod binaryview;
|
||||
pub mod binarywriter;
|
||||
pub mod callingconvention;
|
||||
pub mod command;
|
||||
pub mod custombinaryview;
|
||||
pub mod databuffer;
|
||||
pub mod debuginfo;
|
||||
pub mod demangle;
|
||||
pub mod disassembly;
|
||||
pub mod downloadprovider;
|
||||
pub mod fileaccessor;
|
||||
pub mod filemetadata;
|
||||
pub mod flowgraph;
|
||||
pub mod function;
|
||||
pub mod functionrecognizer;
|
||||
pub mod headless;
|
||||
pub mod interaction;
|
||||
pub mod linearview;
|
||||
pub mod llil;
|
||||
pub mod logger;
|
||||
pub mod metadata;
|
||||
pub mod mlil;
|
||||
pub mod platform;
|
||||
pub mod rc;
|
||||
pub mod references;
|
||||
pub mod relocation;
|
||||
pub mod section;
|
||||
pub mod segment;
|
||||
pub mod settings;
|
||||
pub mod string;
|
||||
pub mod symbol;
|
||||
pub mod tags;
|
||||
pub mod templatesimplifier;
|
||||
pub mod types;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub use binaryninjacore_sys::BNBranchType as BranchType;
|
||||
pub use binaryninjacore_sys::BNEndianness as Endianness;
|
||||
use binaryview::BinaryView;
|
||||
use metadata::Metadata;
|
||||
use metadata::MetadataType;
|
||||
use rc::Ref;
|
||||
use string::BnStrCompatible;
|
||||
|
||||
// Commented out to suppress unused warnings
|
||||
// const BN_MAX_INSTRUCTION_LENGTH: u64 = 256;
|
||||
// const BN_DEFAULT_INSTRUCTION_LENGTH: u64 = 16;
|
||||
// const BN_DEFAULT_OPCODE_DISPLAY: u64 = 8;
|
||||
// const BN_MAX_INSTRUCTION_BRANCHES: u64 = 3;
|
||||
// const BN_MAX_STORED_DATA_LENGTH: u64 = 0x3fffffff;
|
||||
// const BN_NULL_ID: i64 = -1;
|
||||
// const BN_INVALID_REGISTER: usize = 0xffffffff;
|
||||
// const BN_AUTOCOERCE_EXTERN_PTR: u64 = 0xfffffffd;
|
||||
// const BN_NOCOERCE_EXTERN_PTR: u64 = 0xfffffffe;
|
||||
// const BN_INVALID_OPERAND: u64 = 0xffffffff;
|
||||
// const BN_MAX_STRING_LENGTH: u64 = 128;
|
||||
// const BN_MAX_VARIABLE_OFFSET: u64 = 0x7fffffffff;
|
||||
// const BN_MAX_VARIABLE_INDEX: u64 = 0xfffff;
|
||||
// const BN_MINIMUM_CONFIDENCE: u8 = 1;
|
||||
// const BN_HEURISTIC_CONFIDENCE: u8 = 192;
|
||||
|
||||
const BN_FULL_CONFIDENCE: u8 = 255;
|
||||
const BN_INVALID_EXPR: usize = usize::MAX;
|
||||
|
||||
/// The main way to open and load files into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`]
|
||||
pub fn load<S: BnStrCompatible>(filename: S) -> Option<rc::Ref<binaryview::BinaryView>> {
|
||||
let filename = filename.into_bytes_with_nul();
|
||||
let metadata = Metadata::new_of_type(MetadataType::KeyValueDataType);
|
||||
|
||||
let handle = unsafe {
|
||||
binaryninjacore_sys::BNLoadFilename(
|
||||
filename.as_ref().as_ptr() as *mut _,
|
||||
true,
|
||||
None,
|
||||
metadata.handle,
|
||||
)
|
||||
};
|
||||
|
||||
if handle.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { BinaryView::from_raw(handle) })
|
||||
}
|
||||
}
|
||||
|
||||
/// The main way to open and load files (with options) into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`]
|
||||
///
|
||||
/// ```rust
|
||||
/// let settings = [("analysis.linearSweep.autorun", false)].into();
|
||||
///
|
||||
/// let bv = binaryninja::load_with_options("/bin/cat", true, Some(settings))
|
||||
/// .expect("Couldn't open `/bin/cat`");
|
||||
/// ```
|
||||
pub fn load_with_options<S: BnStrCompatible>(
|
||||
filename: S,
|
||||
update_analysis_and_wait: bool,
|
||||
options: Option<Ref<Metadata>>,
|
||||
) -> Option<rc::Ref<binaryview::BinaryView>> {
|
||||
let filename = filename.into_bytes_with_nul();
|
||||
|
||||
let handle = unsafe {
|
||||
binaryninjacore_sys::BNLoadFilename(
|
||||
filename.as_ref().as_ptr() as *mut _,
|
||||
update_analysis_and_wait,
|
||||
None,
|
||||
if let Some(options) = options {
|
||||
options.as_ref().handle
|
||||
} else {
|
||||
Metadata::new_of_type(MetadataType::KeyValueDataType).handle
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
if handle.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { BinaryView::from_raw(handle) })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn install_directory() -> Result<PathBuf, ()> {
|
||||
let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetInstallDirectory() };
|
||||
if s.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
Ok(PathBuf::from(
|
||||
unsafe { string::BnString::from_raw(s) }.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn bundled_plugin_directory() -> Result<PathBuf, ()> {
|
||||
let s: *mut std::os::raw::c_char =
|
||||
unsafe { binaryninjacore_sys::BNGetBundledPluginDirectory() };
|
||||
if s.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
Ok(PathBuf::from(
|
||||
unsafe { string::BnString::from_raw(s) }.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn set_bundled_plugin_directory<S: string::BnStrCompatible>(new_dir: S) {
|
||||
unsafe {
|
||||
binaryninjacore_sys::BNSetBundledPluginDirectory(
|
||||
new_dir.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn user_directory() -> Result<PathBuf, ()> {
|
||||
let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetUserDirectory() };
|
||||
if s.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
Ok(PathBuf::from(
|
||||
unsafe { string::BnString::from_raw(s) }.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn user_plugin_directory() -> Result<PathBuf, ()> {
|
||||
let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetUserPluginDirectory() };
|
||||
if s.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
Ok(PathBuf::from(
|
||||
unsafe { string::BnString::from_raw(s) }.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn repositories_directory() -> Result<PathBuf, ()> {
|
||||
let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetRepositoriesDirectory() };
|
||||
if s.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
Ok(PathBuf::from(
|
||||
unsafe { string::BnString::from_raw(s) }.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn settings_file_name() -> Result<PathBuf, ()> {
|
||||
let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetSettingsFileName() };
|
||||
if s.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
Ok(PathBuf::from(
|
||||
unsafe { string::BnString::from_raw(s) }.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn save_last_run() {
|
||||
unsafe { binaryninjacore_sys::BNSaveLastRun() };
|
||||
}
|
||||
|
||||
pub fn path_relative_to_bundled_plugin_directory<S: string::BnStrCompatible>(
|
||||
path: S,
|
||||
) -> Result<PathBuf, ()> {
|
||||
let s: *mut std::os::raw::c_char = unsafe {
|
||||
binaryninjacore_sys::BNGetPathRelativeToBundledPluginDirectory(
|
||||
path.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char,
|
||||
)
|
||||
};
|
||||
if s.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
Ok(PathBuf::from(
|
||||
unsafe { string::BnString::from_raw(s) }.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn path_relative_to_user_plugin_directory<S: string::BnStrCompatible>(
|
||||
path: S,
|
||||
) -> Result<PathBuf, ()> {
|
||||
let s: *mut std::os::raw::c_char = unsafe {
|
||||
binaryninjacore_sys::BNGetPathRelativeToUserPluginDirectory(
|
||||
path.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char,
|
||||
)
|
||||
};
|
||||
if s.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
Ok(PathBuf::from(
|
||||
unsafe { string::BnString::from_raw(s) }.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn path_relative_to_user_directory<S: string::BnStrCompatible>(path: S) -> Result<PathBuf, ()> {
|
||||
let s: *mut std::os::raw::c_char = unsafe {
|
||||
binaryninjacore_sys::BNGetPathRelativeToUserDirectory(
|
||||
path.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char,
|
||||
)
|
||||
};
|
||||
if s.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
Ok(PathBuf::from(
|
||||
unsafe { string::BnString::from_raw(s) }.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn version() -> string::BnString {
|
||||
unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetVersionString()) }
|
||||
}
|
||||
|
||||
pub fn plugin_abi_version() -> u32 {
|
||||
binaryninjacore_sys::BN_CURRENT_CORE_ABI_VERSION
|
||||
}
|
||||
|
||||
pub fn plugin_abi_minimum_version() -> u32 {
|
||||
binaryninjacore_sys::BN_MINIMUM_CORE_ABI_VERSION
|
||||
}
|
||||
|
||||
pub fn core_abi_version() -> u32 {
|
||||
unsafe { binaryninjacore_sys::BNGetCurrentCoreABIVersion() }
|
||||
}
|
||||
|
||||
pub fn core_abi_minimum_version() -> u32 {
|
||||
unsafe { binaryninjacore_sys::BNGetMinimumCoreABIVersion() }
|
||||
}
|
||||
|
||||
pub fn plugin_ui_abi_version() -> u32 {
|
||||
binaryninjacore_sys::BN_CURRENT_UI_ABI_VERSION
|
||||
}
|
||||
|
||||
pub fn plugin_ui_abi_minimum_version() -> u32 {
|
||||
binaryninjacore_sys::BN_MINIMUM_UI_ABI_VERSION
|
||||
}
|
||||
|
||||
pub fn add_required_plugin_dependency<S: string::BnStrCompatible>(name: S) {
|
||||
unsafe {
|
||||
binaryninjacore_sys::BNAddRequiredPluginDependency(
|
||||
name.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add_optional_plugin_dependency<S: string::BnStrCompatible>(name: S) {
|
||||
unsafe {
|
||||
binaryninjacore_sys::BNAddOptionalPluginDependency(
|
||||
name.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// Provide ABI version automatically so that the core can verify binary compatibility
|
||||
#[cfg(not(feature = "noexports"))]
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
pub extern "C" fn CorePluginABIVersion() -> u32 {
|
||||
plugin_abi_version()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "noexports"))]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn UIPluginABIVersion() -> u32 {
|
||||
plugin_ui_abi_version()
|
||||
}
|
||||
436
src/linearview.rs
Normal file
436
src/linearview.rs
Normal file
@@ -0,0 +1,436 @@
|
||||
// Copyright 2022-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! APIs for accessing Binary Ninja's linear view
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::binaryview::BinaryView;
|
||||
use crate::disassembly::{DisassemblySettings, DisassemblyTextLine};
|
||||
use crate::function::Function;
|
||||
|
||||
use crate::rc::*;
|
||||
use std::ops::Deref;
|
||||
|
||||
use std::mem;
|
||||
|
||||
pub struct LinearViewObject {
|
||||
pub(crate) handle: *mut BNLinearViewObject,
|
||||
}
|
||||
|
||||
impl LinearViewObject {
|
||||
pub(crate) unsafe fn from_raw(handle: *mut BNLinearViewObject) -> Ref<Self> {
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Ref::new(Self { handle })
|
||||
}
|
||||
|
||||
pub fn data_only(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle =
|
||||
binaryninjacore_sys::BNCreateLinearViewDataOnly(view.handle, settings.handle);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disassembly(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle =
|
||||
binaryninjacore_sys::BNCreateLinearViewDisassembly(view.handle, settings.handle);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lifted_il(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle =
|
||||
binaryninjacore_sys::BNCreateLinearViewLiftedIL(view.handle, settings.handle);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mlil(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle =
|
||||
binaryninjacore_sys::BNCreateLinearViewMediumLevelIL(view.handle, settings.handle);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mlil_ssa(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle = binaryninjacore_sys::BNCreateLinearViewMediumLevelILSSAForm(
|
||||
view.handle,
|
||||
settings.handle,
|
||||
);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hlil(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle =
|
||||
binaryninjacore_sys::BNCreateLinearViewHighLevelIL(view.handle, settings.handle);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hlil_ssa(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle = binaryninjacore_sys::BNCreateLinearViewHighLevelILSSAForm(
|
||||
view.handle,
|
||||
settings.handle,
|
||||
);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn language_representation(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle = binaryninjacore_sys::BNCreateLinearViewLanguageRepresentation(
|
||||
view.handle,
|
||||
settings.handle,
|
||||
);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn single_function_disassembly(
|
||||
function: &Function,
|
||||
settings: &DisassemblySettings,
|
||||
) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle = binaryninjacore_sys::BNCreateLinearViewSingleFunctionDisassembly(
|
||||
function.handle,
|
||||
settings.handle,
|
||||
);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn single_function_lifted_il(
|
||||
function: &Function,
|
||||
settings: &DisassemblySettings,
|
||||
) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle = binaryninjacore_sys::BNCreateLinearViewSingleFunctionLiftedIL(
|
||||
function.handle,
|
||||
settings.handle,
|
||||
);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn single_function_mlil(function: &Function, settings: &DisassemblySettings) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle = binaryninjacore_sys::BNCreateLinearViewSingleFunctionMediumLevelIL(
|
||||
function.handle,
|
||||
settings.handle,
|
||||
);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn single_function_mlil_ssa(
|
||||
function: &Function,
|
||||
settings: &DisassemblySettings,
|
||||
) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle = binaryninjacore_sys::BNCreateLinearViewSingleFunctionMediumLevelILSSAForm(
|
||||
function.handle,
|
||||
settings.handle,
|
||||
);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn single_function_hlil(function: &Function, settings: &DisassemblySettings) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle = binaryninjacore_sys::BNCreateLinearViewSingleFunctionHighLevelIL(
|
||||
function.handle,
|
||||
settings.handle,
|
||||
);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn single_function_hlil_ssa(
|
||||
function: &Function,
|
||||
settings: &DisassemblySettings,
|
||||
) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle = binaryninjacore_sys::BNCreateLinearViewSingleFunctionHighLevelILSSAForm(
|
||||
function.handle,
|
||||
settings.handle,
|
||||
);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn single_function_language_representation(
|
||||
function: &Function,
|
||||
settings: &DisassemblySettings,
|
||||
) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle =
|
||||
binaryninjacore_sys::BNCreateLinearViewSingleFunctionLanguageRepresentation(
|
||||
function.handle,
|
||||
settings.handle,
|
||||
);
|
||||
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for LinearViewObject {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewLinearViewObjectReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeLinearViewObject(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for LinearViewObject {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for LinearViewObject {}
|
||||
unsafe impl Sync for LinearViewObject {}
|
||||
|
||||
#[derive(Eq)]
|
||||
pub struct LinearViewCursor {
|
||||
pub(crate) handle: *mut binaryninjacore_sys::BNLinearViewCursor,
|
||||
}
|
||||
|
||||
impl LinearViewCursor {
|
||||
pub(crate) unsafe fn from_raw(handle: *mut BNLinearViewCursor) -> Ref<Self> {
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Ref::new(Self { handle })
|
||||
}
|
||||
|
||||
pub fn new(root: &LinearViewObject) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle = BNCreateLinearViewCursor(root.handle);
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the current [LinearViewObject] associated with this cursor.
|
||||
pub fn current_object(&self) -> Ref<LinearViewObject> {
|
||||
unsafe {
|
||||
let handle = BNGetLinearViewCursorCurrentObject(self.handle);
|
||||
LinearViewObject::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: can we implement clone without shadowing ToOwned?
|
||||
pub fn duplicate(&self) -> Ref<Self> {
|
||||
unsafe {
|
||||
let handle = BNDuplicateLinearViewCursor(self.handle);
|
||||
Self::from_raw(handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn before_begin(&self) -> bool {
|
||||
unsafe { BNIsLinearViewCursorBeforeBegin(self.handle) }
|
||||
}
|
||||
|
||||
pub fn after_end(&self) -> bool {
|
||||
unsafe { BNIsLinearViewCursorAfterEnd(self.handle) }
|
||||
}
|
||||
|
||||
pub fn valid(&self) -> bool {
|
||||
!(self.before_begin() || self.after_end())
|
||||
}
|
||||
|
||||
pub fn seek_to_start(&self) {
|
||||
unsafe { BNSeekLinearViewCursorToBegin(self.handle) }
|
||||
}
|
||||
|
||||
pub fn seek_to_end(&self) {
|
||||
unsafe { BNSeekLinearViewCursorToEnd(self.handle) }
|
||||
}
|
||||
|
||||
pub fn seek_to_address(&self, address: u64) {
|
||||
unsafe { BNSeekLinearViewCursorToAddress(self.handle, address) }
|
||||
}
|
||||
|
||||
pub fn ordering_index(&self) -> std::ops::Range<u64> {
|
||||
unsafe {
|
||||
let range = BNGetLinearViewCursorOrderingIndex(self.handle);
|
||||
range.start..range.end
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ordering_index_total(&self) -> u64 {
|
||||
unsafe { BNGetLinearViewCursorOrderingIndexTotal(self.handle) }
|
||||
}
|
||||
|
||||
pub fn seek_to_ordering_index(&self, idx: u64) {
|
||||
unsafe { BNSeekLinearViewCursorToAddress(self.handle, idx) }
|
||||
}
|
||||
|
||||
pub fn previous(&self) -> bool {
|
||||
unsafe { BNLinearViewCursorPrevious(self.handle) }
|
||||
}
|
||||
|
||||
pub fn next(&self) -> bool {
|
||||
unsafe { BNLinearViewCursorNext(self.handle) }
|
||||
}
|
||||
|
||||
pub fn lines(&self) -> Array<LinearDisassemblyLine> {
|
||||
let mut count: usize = 0;
|
||||
unsafe {
|
||||
let handles = BNGetLinearViewCursorLines(self.handle, &mut count);
|
||||
Array::new(handles, count, ())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for LinearViewCursor {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
unsafe { BNCompareLinearViewCursors(self.handle, other.handle) == 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for LinearViewCursor {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
match unsafe { BNCompareLinearViewCursors(self.handle, other.handle) } {
|
||||
i if i < 0 => Some(std::cmp::Ordering::Less),
|
||||
i if i > 0 => Some(std::cmp::Ordering::Greater),
|
||||
_ => Some(std::cmp::Ordering::Equal),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for LinearViewCursor {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
match unsafe { BNCompareLinearViewCursors(self.handle, other.handle) } {
|
||||
i if i < 0 => std::cmp::Ordering::Less,
|
||||
i if i > 0 => std::cmp::Ordering::Greater,
|
||||
_ => std::cmp::Ordering::Equal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for LinearViewCursor {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewLinearViewCursorReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeLinearViewCursor(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for LinearViewCursor {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for LinearViewCursor {}
|
||||
unsafe impl Sync for LinearViewCursor {}
|
||||
|
||||
pub type LinearDisassemblyLineType = BNLinearDisassemblyLineType;
|
||||
|
||||
pub struct LinearDisassemblyLine {
|
||||
t: LinearDisassemblyLineType,
|
||||
|
||||
// These will be cleaned up by BNFreeLinearDisassemblyLines, so we
|
||||
// don't drop them in the relevant deconstructors.
|
||||
function: mem::ManuallyDrop<Ref<Function>>,
|
||||
contents: mem::ManuallyDrop<DisassemblyTextLine>,
|
||||
}
|
||||
|
||||
impl LinearDisassemblyLine {
|
||||
pub(crate) unsafe fn from_raw(raw: &BNLinearDisassemblyLine) -> Self {
|
||||
let linetype = raw.type_;
|
||||
let function = mem::ManuallyDrop::new(Function::from_raw(raw.function));
|
||||
let contents = mem::ManuallyDrop::new(DisassemblyTextLine(raw.contents));
|
||||
Self {
|
||||
t: linetype,
|
||||
function,
|
||||
contents,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn function(&self) -> &Function {
|
||||
self.function.as_ref()
|
||||
}
|
||||
|
||||
pub fn line_type(&self) -> LinearDisassemblyLineType {
|
||||
self.t
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for LinearDisassemblyLine {
|
||||
type Target = DisassemblyTextLine;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.contents.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LinearDisassemblyLine {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.deref())
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for LinearDisassemblyLine {
|
||||
type Raw = BNLinearDisassemblyLine;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for LinearDisassemblyLine {
|
||||
unsafe fn free(raw: *mut BNLinearDisassemblyLine, count: usize, _context: &()) {
|
||||
BNFreeLinearDisassemblyLines(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for LinearDisassemblyLine {
|
||||
type Wrapped = Guard<'a, LinearDisassemblyLine>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
Guard::new(LinearDisassemblyLine::from_raw(raw), _context)
|
||||
}
|
||||
}
|
||||
103
src/llil/block.rs
Normal file
103
src/llil/block.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::architecture::Architecture;
|
||||
use crate::basicblock::{BasicBlock, BlockContext};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct BlockIter<'func, A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
function: &'func Function<A, M, F>,
|
||||
range: Range<u64>,
|
||||
}
|
||||
|
||||
impl<'func, A, M, F> Iterator for BlockIter<'func, A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
type Item = Instruction<'func, A, M, F>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.range.next().map(|i| Instruction {
|
||||
function: self.function,
|
||||
instr_idx: i as usize,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Block<'func, A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub(crate) function: &'func Function<A, M, F>,
|
||||
}
|
||||
|
||||
impl<'func, A, M, F> fmt::Debug for Block<'func, A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "llil_bb {:?}", self.function)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'func, A, M, F> BlockContext for Block<'func, A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
type Iter = BlockIter<'func, A, M, F>;
|
||||
type Instruction = Instruction<'func, A, M, F>;
|
||||
|
||||
fn start(&self, block: &BasicBlock<Self>) -> Instruction<'func, A, M, F> {
|
||||
Instruction {
|
||||
function: self.function,
|
||||
instr_idx: block.raw_start() as usize,
|
||||
}
|
||||
}
|
||||
|
||||
fn iter(&self, block: &BasicBlock<Self>) -> BlockIter<'func, A, M, F> {
|
||||
BlockIter {
|
||||
function: self.function,
|
||||
range: block.raw_start()..block.raw_end(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'func, A, M, F> Clone for Block<'func, A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Block {
|
||||
function: self.function,
|
||||
}
|
||||
}
|
||||
}
|
||||
808
src/llil/expression.rs
Normal file
808
src/llil/expression.rs
Normal file
@@ -0,0 +1,808 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use binaryninjacore_sys::BNGetLowLevelILByIndex;
|
||||
use binaryninjacore_sys::BNLowLevelILInstruction;
|
||||
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::operation;
|
||||
use super::operation::Operation;
|
||||
use super::*;
|
||||
|
||||
use crate::architecture::Architecture;
|
||||
use crate::architecture::RegisterInfo;
|
||||
|
||||
// used as a marker for Expressions that can produce a value
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct ValueExpr;
|
||||
|
||||
// used as a marker for Expressions that can not produce a value
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct VoidExpr;
|
||||
|
||||
pub trait ExpressionResultType: 'static {}
|
||||
impl ExpressionResultType for ValueExpr {}
|
||||
impl ExpressionResultType for VoidExpr {}
|
||||
|
||||
pub struct Expression<'func, A, M, F, R>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
R: ExpressionResultType,
|
||||
{
|
||||
pub(crate) function: &'func Function<A, M, F>,
|
||||
pub(crate) expr_idx: usize,
|
||||
|
||||
// tag the 'return' type of this expression
|
||||
pub(crate) _ty: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<'func, A, M, F, R> Expression<'func, A, M, F, R>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
R: ExpressionResultType,
|
||||
{
|
||||
pub(crate) fn new(function: &'func Function<A, M, F>, expr_idx: usize) -> Self {
|
||||
Self {
|
||||
function,
|
||||
expr_idx,
|
||||
_ty: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index(&self) -> usize {
|
||||
self.expr_idx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'func, A, M, V> fmt::Debug for Expression<'func, A, M, NonSSA<V>, ValueExpr>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let op_info = self.info();
|
||||
write!(f, "<expr {}: {:?}>", self.expr_idx, op_info)
|
||||
}
|
||||
}
|
||||
|
||||
fn common_info<'func, A, M, F>(
|
||||
function: &'func Function<A, M, F>,
|
||||
op: BNLowLevelILInstruction,
|
||||
) -> ExprInfo<'func, A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
use binaryninjacore_sys::BNLowLevelILOperation::*;
|
||||
|
||||
match op.operation {
|
||||
LLIL_CONST => ExprInfo::Const(Operation::new(function, op)),
|
||||
LLIL_CONST_PTR => ExprInfo::ConstPtr(Operation::new(function, op)),
|
||||
|
||||
LLIL_ADD => ExprInfo::Add(Operation::new(function, op)),
|
||||
LLIL_ADC => ExprInfo::Adc(Operation::new(function, op)),
|
||||
LLIL_SUB => ExprInfo::Sub(Operation::new(function, op)),
|
||||
LLIL_SBB => ExprInfo::Sbb(Operation::new(function, op)),
|
||||
LLIL_AND => ExprInfo::And(Operation::new(function, op)),
|
||||
LLIL_OR => ExprInfo::Or(Operation::new(function, op)),
|
||||
LLIL_XOR => ExprInfo::Xor(Operation::new(function, op)),
|
||||
LLIL_LSL => ExprInfo::Lsl(Operation::new(function, op)),
|
||||
LLIL_LSR => ExprInfo::Lsr(Operation::new(function, op)),
|
||||
LLIL_ASR => ExprInfo::Asr(Operation::new(function, op)),
|
||||
LLIL_ROL => ExprInfo::Rol(Operation::new(function, op)),
|
||||
LLIL_RLC => ExprInfo::Rlc(Operation::new(function, op)),
|
||||
LLIL_ROR => ExprInfo::Ror(Operation::new(function, op)),
|
||||
LLIL_RRC => ExprInfo::Rrc(Operation::new(function, op)),
|
||||
LLIL_MUL => ExprInfo::Mul(Operation::new(function, op)),
|
||||
|
||||
LLIL_MULU_DP => ExprInfo::MuluDp(Operation::new(function, op)),
|
||||
LLIL_MULS_DP => ExprInfo::MulsDp(Operation::new(function, op)),
|
||||
|
||||
LLIL_DIVU => ExprInfo::Divu(Operation::new(function, op)),
|
||||
LLIL_DIVS => ExprInfo::Divs(Operation::new(function, op)),
|
||||
|
||||
LLIL_DIVU_DP => ExprInfo::DivuDp(Operation::new(function, op)),
|
||||
LLIL_DIVS_DP => ExprInfo::DivsDp(Operation::new(function, op)),
|
||||
|
||||
LLIL_MODU => ExprInfo::Modu(Operation::new(function, op)),
|
||||
LLIL_MODS => ExprInfo::Mods(Operation::new(function, op)),
|
||||
|
||||
LLIL_MODU_DP => ExprInfo::ModuDp(Operation::new(function, op)),
|
||||
LLIL_MODS_DP => ExprInfo::ModsDp(Operation::new(function, op)),
|
||||
|
||||
LLIL_NEG => ExprInfo::Neg(Operation::new(function, op)),
|
||||
LLIL_NOT => ExprInfo::Not(Operation::new(function, op)),
|
||||
|
||||
LLIL_SX => ExprInfo::Sx(Operation::new(function, op)),
|
||||
LLIL_ZX => ExprInfo::Zx(Operation::new(function, op)),
|
||||
LLIL_LOW_PART => ExprInfo::LowPart(Operation::new(function, op)),
|
||||
|
||||
LLIL_CMP_E => ExprInfo::CmpE(Operation::new(function, op)),
|
||||
LLIL_CMP_NE => ExprInfo::CmpNe(Operation::new(function, op)),
|
||||
LLIL_CMP_SLT => ExprInfo::CmpSlt(Operation::new(function, op)),
|
||||
LLIL_CMP_ULT => ExprInfo::CmpUlt(Operation::new(function, op)),
|
||||
LLIL_CMP_SLE => ExprInfo::CmpSle(Operation::new(function, op)),
|
||||
LLIL_CMP_ULE => ExprInfo::CmpUle(Operation::new(function, op)),
|
||||
LLIL_CMP_SGE => ExprInfo::CmpSge(Operation::new(function, op)),
|
||||
LLIL_CMP_UGE => ExprInfo::CmpUge(Operation::new(function, op)),
|
||||
LLIL_CMP_SGT => ExprInfo::CmpSgt(Operation::new(function, op)),
|
||||
LLIL_CMP_UGT => ExprInfo::CmpUgt(Operation::new(function, op)),
|
||||
|
||||
LLIL_BOOL_TO_INT => ExprInfo::BoolToInt(Operation::new(function, op)),
|
||||
|
||||
LLIL_FADD => ExprInfo::Fadd(Operation::new(function, op)),
|
||||
LLIL_FSUB => ExprInfo::Fsub(Operation::new(function, op)),
|
||||
LLIL_FMUL => ExprInfo::Fmul(Operation::new(function, op)),
|
||||
LLIL_FDIV => ExprInfo::Fdiv(Operation::new(function, op)),
|
||||
|
||||
LLIL_FSQRT => ExprInfo::Fsqrt(Operation::new(function, op)),
|
||||
LLIL_FNEG => ExprInfo::Fneg(Operation::new(function, op)),
|
||||
LLIL_FABS => ExprInfo::Fabs(Operation::new(function, op)),
|
||||
LLIL_FLOAT_TO_INT => ExprInfo::FloatToInt(Operation::new(function, op)),
|
||||
LLIL_INT_TO_FLOAT => ExprInfo::IntToFloat(Operation::new(function, op)),
|
||||
LLIL_FLOAT_CONV => ExprInfo::FloatConv(Operation::new(function, op)),
|
||||
LLIL_ROUND_TO_INT => ExprInfo::RoundToInt(Operation::new(function, op)),
|
||||
LLIL_FLOOR => ExprInfo::Floor(Operation::new(function, op)),
|
||||
LLIL_CEIL => ExprInfo::Ceil(Operation::new(function, op)),
|
||||
LLIL_FTRUNC => ExprInfo::Ftrunc(Operation::new(function, op)),
|
||||
|
||||
LLIL_FCMP_E => ExprInfo::FcmpE(Operation::new(function, op)),
|
||||
LLIL_FCMP_NE => ExprInfo::FcmpNE(Operation::new(function, op)),
|
||||
LLIL_FCMP_LT => ExprInfo::FcmpLT(Operation::new(function, op)),
|
||||
LLIL_FCMP_LE => ExprInfo::FcmpLE(Operation::new(function, op)),
|
||||
LLIL_FCMP_GT => ExprInfo::FcmpGT(Operation::new(function, op)),
|
||||
LLIL_FCMP_GE => ExprInfo::FcmpGE(Operation::new(function, op)),
|
||||
LLIL_FCMP_O => ExprInfo::FcmpO(Operation::new(function, op)),
|
||||
LLIL_FCMP_UO => ExprInfo::FcmpUO(Operation::new(function, op)),
|
||||
|
||||
LLIL_UNIMPL => ExprInfo::Unimpl(Operation::new(function, op)),
|
||||
LLIL_UNIMPL_MEM => ExprInfo::UnimplMem(Operation::new(function, op)),
|
||||
|
||||
// TODO TEST_BIT ADD_OVERFLOW
|
||||
_ => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
error!(
|
||||
"Got unexpected operation {:?} in value expr at 0x{:x}",
|
||||
op.operation, op.address
|
||||
);
|
||||
}
|
||||
|
||||
ExprInfo::Undef(Operation::new(function, op))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use super::VisitorAction;
|
||||
|
||||
macro_rules! visit {
|
||||
($f:expr, $($e:expr),*) => {
|
||||
if let VisitorAction::Halt = $f($($e,)*) {
|
||||
return VisitorAction::Halt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn common_visit<'func, A, M, F, CB>(info: &ExprInfo<'func, A, M, F>, f: &mut CB) -> VisitorAction
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
CB: FnMut(&Expression<'func, A, M, F, ValueExpr>) -> VisitorAction,
|
||||
{
|
||||
use self::ExprInfo::*;
|
||||
|
||||
match *info {
|
||||
CmpE(ref op) | CmpNe(ref op) | CmpSlt(ref op) | CmpUlt(ref op) | CmpSle(ref op)
|
||||
| CmpUle(ref op) | CmpSge(ref op) | CmpUge(ref op) | CmpSgt(ref op) | CmpUgt(ref op)
|
||||
| FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op) | FcmpGE(ref op)
|
||||
| FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => {
|
||||
visit!(f, &op.left());
|
||||
visit!(f, &op.right());
|
||||
}
|
||||
|
||||
Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => {
|
||||
visit!(f, &op.left());
|
||||
visit!(f, &op.right());
|
||||
visit!(f, &op.carry());
|
||||
}
|
||||
|
||||
Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
|
||||
| Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op) | MulsDp(ref op)
|
||||
| MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op) | Mods(ref op)
|
||||
| Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => {
|
||||
visit!(f, &op.left());
|
||||
visit!(f, &op.right());
|
||||
}
|
||||
|
||||
DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => {
|
||||
visit!(f, &op.high());
|
||||
visit!(f, &op.low());
|
||||
visit!(f, &op.right());
|
||||
}
|
||||
|
||||
Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
|
||||
| BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op) | FloatToInt(ref op)
|
||||
| IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op) | Floor(ref op)
|
||||
| Ceil(ref op) | Ftrunc(ref op) => {
|
||||
visit!(f, &op.operand());
|
||||
}
|
||||
|
||||
UnimplMem(ref op) => {
|
||||
visit!(f, &op.mem_expr());
|
||||
}
|
||||
|
||||
_ => {}
|
||||
};
|
||||
|
||||
VisitorAction::Sibling
|
||||
}
|
||||
|
||||
impl<'func, A, M, V> Expression<'func, A, M, NonSSA<V>, ValueExpr>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
pub(crate) unsafe fn info_from_op(
|
||||
&self,
|
||||
op: BNLowLevelILInstruction,
|
||||
) -> ExprInfo<'func, A, M, NonSSA<V>> {
|
||||
use binaryninjacore_sys::BNLowLevelILOperation::*;
|
||||
|
||||
match op.operation {
|
||||
LLIL_LOAD => ExprInfo::Load(Operation::new(self.function, op)),
|
||||
LLIL_POP => ExprInfo::Pop(Operation::new(self.function, op)),
|
||||
LLIL_REG => ExprInfo::Reg(Operation::new(self.function, op)),
|
||||
LLIL_FLAG => ExprInfo::Flag(Operation::new(self.function, op)),
|
||||
LLIL_FLAG_BIT => ExprInfo::FlagBit(Operation::new(self.function, op)),
|
||||
LLIL_FLAG_COND => ExprInfo::FlagCond(Operation::new(self.function, op)), // TODO lifted only
|
||||
LLIL_FLAG_GROUP => ExprInfo::FlagGroup(Operation::new(self.function, op)), // TODO lifted only
|
||||
_ => common_info(self.function, op),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn info(&self) -> ExprInfo<'func, A, M, NonSSA<V>> {
|
||||
unsafe {
|
||||
let op = BNGetLowLevelILByIndex(self.function.handle, self.expr_idx);
|
||||
self.info_from_op(op)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction
|
||||
where
|
||||
F: FnMut(&Self, &ExprInfo<'func, A, M, NonSSA<V>>) -> VisitorAction,
|
||||
{
|
||||
use self::ExprInfo::*;
|
||||
|
||||
let info = self.info();
|
||||
|
||||
match f(self, &info) {
|
||||
VisitorAction::Descend => {}
|
||||
action => return action,
|
||||
};
|
||||
|
||||
match info {
|
||||
Load(ref op) => visit!(Self::visit_tree, &op.source_mem_expr(), f),
|
||||
_ => {
|
||||
let mut fb = |e: &Self| e.visit_tree(f);
|
||||
visit!(common_visit, &info, &mut fb);
|
||||
}
|
||||
};
|
||||
|
||||
VisitorAction::Sibling
|
||||
}
|
||||
}
|
||||
|
||||
impl<'func, A, M> Expression<'func, A, M, SSA, ValueExpr>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
{
|
||||
pub(crate) unsafe fn info_from_op(
|
||||
&self,
|
||||
op: BNLowLevelILInstruction,
|
||||
) -> ExprInfo<'func, A, M, SSA> {
|
||||
use binaryninjacore_sys::BNLowLevelILOperation::*;
|
||||
|
||||
match op.operation {
|
||||
LLIL_LOAD_SSA => ExprInfo::Load(Operation::new(self.function, op)),
|
||||
LLIL_REG_SSA | LLIL_REG_SSA_PARTIAL => ExprInfo::Reg(Operation::new(self.function, op)),
|
||||
LLIL_FLAG_SSA => ExprInfo::Flag(Operation::new(self.function, op)),
|
||||
LLIL_FLAG_BIT_SSA => ExprInfo::FlagBit(Operation::new(self.function, op)),
|
||||
_ => common_info(self.function, op),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn info(&self) -> ExprInfo<'func, A, M, SSA> {
|
||||
unsafe {
|
||||
let op = BNGetLowLevelILByIndex(self.function.handle, self.expr_idx);
|
||||
self.info_from_op(op)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction
|
||||
where
|
||||
F: FnMut(&Self, &ExprInfo<'func, A, M, SSA>) -> VisitorAction,
|
||||
{
|
||||
use self::ExprInfo::*;
|
||||
|
||||
let info = self.info();
|
||||
|
||||
match f(self, &info) {
|
||||
VisitorAction::Descend => {}
|
||||
action => return action,
|
||||
};
|
||||
|
||||
match info {
|
||||
// TODO ssa
|
||||
Load(ref _op) => {} //visit!(Self::visit_tree, &op.source_mem_expr(), f),
|
||||
_ => {
|
||||
let mut fb = |e: &Self| e.visit_tree(f);
|
||||
visit!(common_visit, &info, &mut fb);
|
||||
}
|
||||
};
|
||||
|
||||
VisitorAction::Sibling
|
||||
}
|
||||
}
|
||||
|
||||
impl<'func, A, F> Expression<'func, A, Finalized, F, ValueExpr>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
F: FunctionForm,
|
||||
{
|
||||
// TODO possible values
|
||||
}
|
||||
|
||||
pub enum ExprInfo<'func, A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
Load(Operation<'func, A, M, F, operation::Load>),
|
||||
Pop(Operation<'func, A, M, F, operation::Pop>),
|
||||
Reg(Operation<'func, A, M, F, operation::Reg>),
|
||||
Const(Operation<'func, A, M, F, operation::Const>),
|
||||
ConstPtr(Operation<'func, A, M, F, operation::Const>),
|
||||
Flag(Operation<'func, A, M, F, operation::Flag>),
|
||||
FlagBit(Operation<'func, A, M, F, operation::FlagBit>),
|
||||
|
||||
Add(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Adc(Operation<'func, A, M, F, operation::BinaryOpCarry>),
|
||||
Sub(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Sbb(Operation<'func, A, M, F, operation::BinaryOpCarry>),
|
||||
And(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Or(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Xor(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Lsl(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Lsr(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Asr(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Rol(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Rlc(Operation<'func, A, M, F, operation::BinaryOpCarry>),
|
||||
Ror(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Rrc(Operation<'func, A, M, F, operation::BinaryOpCarry>),
|
||||
Mul(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
|
||||
MulsDp(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
MuluDp(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
|
||||
Divu(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Divs(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
|
||||
DivuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
|
||||
DivsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
|
||||
|
||||
Modu(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Mods(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
|
||||
ModuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
|
||||
ModsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
|
||||
|
||||
Neg(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
Not(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
Sx(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
Zx(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
LowPart(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
|
||||
FlagCond(Operation<'func, A, M, F, operation::FlagCond>),
|
||||
FlagGroup(Operation<'func, A, M, F, operation::FlagGroup>),
|
||||
|
||||
CmpE(Operation<'func, A, M, F, operation::Condition>),
|
||||
CmpNe(Operation<'func, A, M, F, operation::Condition>),
|
||||
CmpSlt(Operation<'func, A, M, F, operation::Condition>),
|
||||
CmpUlt(Operation<'func, A, M, F, operation::Condition>),
|
||||
CmpSle(Operation<'func, A, M, F, operation::Condition>),
|
||||
CmpUle(Operation<'func, A, M, F, operation::Condition>),
|
||||
CmpSge(Operation<'func, A, M, F, operation::Condition>),
|
||||
CmpUge(Operation<'func, A, M, F, operation::Condition>),
|
||||
CmpSgt(Operation<'func, A, M, F, operation::Condition>),
|
||||
CmpUgt(Operation<'func, A, M, F, operation::Condition>),
|
||||
|
||||
//TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
|
||||
BoolToInt(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
|
||||
Fadd(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Fsub(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Fmul(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Fdiv(Operation<'func, A, M, F, operation::BinaryOp>),
|
||||
Fsqrt(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
Fneg(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
Fabs(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
FloatToInt(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
IntToFloat(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
FloatConv(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
RoundToInt(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
Floor(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
Ceil(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
Ftrunc(Operation<'func, A, M, F, operation::UnaryOp>),
|
||||
|
||||
FcmpE(Operation<'func, A, M, F, operation::Condition>),
|
||||
FcmpNE(Operation<'func, A, M, F, operation::Condition>),
|
||||
FcmpLT(Operation<'func, A, M, F, operation::Condition>),
|
||||
FcmpLE(Operation<'func, A, M, F, operation::Condition>),
|
||||
FcmpGE(Operation<'func, A, M, F, operation::Condition>),
|
||||
FcmpGT(Operation<'func, A, M, F, operation::Condition>),
|
||||
FcmpO(Operation<'func, A, M, F, operation::Condition>),
|
||||
FcmpUO(Operation<'func, A, M, F, operation::Condition>),
|
||||
|
||||
// TODO ADD_OVERFLOW
|
||||
Unimpl(Operation<'func, A, M, F, operation::NoArgs>),
|
||||
UnimplMem(Operation<'func, A, M, F, operation::UnimplMem>),
|
||||
|
||||
Undef(Operation<'func, A, M, F, operation::NoArgs>),
|
||||
}
|
||||
|
||||
impl<'func, A, M, F> ExprInfo<'func, A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
/// Returns the size of the result of this expression
|
||||
///
|
||||
/// If the expression is malformed or is `Unimpl` there
|
||||
/// is no meaningful size associated with the result.
|
||||
pub fn size(&self) -> Option<usize> {
|
||||
use self::ExprInfo::*;
|
||||
|
||||
match *self {
|
||||
Undef(..) | Unimpl(..) => None,
|
||||
|
||||
FlagCond(..) | FlagGroup(..) | CmpE(..) | CmpNe(..) | CmpSlt(..) | CmpUlt(..)
|
||||
| CmpSle(..) | CmpUle(..) | CmpSge(..) | CmpUge(..) | CmpSgt(..) | CmpUgt(..) => {
|
||||
Some(0)
|
||||
}
|
||||
|
||||
_ => Some(self.raw_struct().size),
|
||||
//TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
|
||||
}
|
||||
}
|
||||
|
||||
pub fn address(&self) -> u64 {
|
||||
self.raw_struct().address
|
||||
}
|
||||
|
||||
/// Determines if the expressions represent the same operation
|
||||
///
|
||||
/// It does not examine the operands for equality.
|
||||
pub fn is_same_op_as(&self, other: &Self) -> bool {
|
||||
use self::ExprInfo::*;
|
||||
|
||||
match (self, other) {
|
||||
(&Reg(..), &Reg(..)) => true,
|
||||
_ => self.raw_struct().operation == other.raw_struct().operation,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_cmp_op(&self) -> Option<&Operation<'func, A, M, F, operation::Condition>> {
|
||||
use self::ExprInfo::*;
|
||||
|
||||
match *self {
|
||||
CmpE(ref op) | CmpNe(ref op) | CmpSlt(ref op) | CmpUlt(ref op) | CmpSle(ref op)
|
||||
| CmpUle(ref op) | CmpSge(ref op) | CmpUge(ref op) | CmpSgt(ref op)
|
||||
| CmpUgt(ref op) | FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op)
|
||||
| FcmpGE(ref op) | FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => Some(op),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_binary_op(&self) -> Option<&Operation<'func, A, M, F, operation::BinaryOp>> {
|
||||
use self::ExprInfo::*;
|
||||
|
||||
match *self {
|
||||
Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
|
||||
| Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op)
|
||||
| MulsDp(ref op) | MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op)
|
||||
| Mods(ref op) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => Some(op),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_binary_op_carry(
|
||||
&self,
|
||||
) -> Option<&Operation<'func, A, M, F, operation::BinaryOpCarry>> {
|
||||
use self::ExprInfo::*;
|
||||
|
||||
match *self {
|
||||
Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => Some(op),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_double_prec_div_op(
|
||||
&self,
|
||||
) -> Option<&Operation<'func, A, M, F, operation::DoublePrecDivOp>> {
|
||||
use self::ExprInfo::*;
|
||||
|
||||
match *self {
|
||||
DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => Some(op),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_unary_op(&self) -> Option<&Operation<'func, A, M, F, operation::UnaryOp>> {
|
||||
use self::ExprInfo::*;
|
||||
|
||||
match *self {
|
||||
Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
|
||||
| BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
|
||||
| FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
|
||||
| Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => Some(op),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn raw_struct(&self) -> &BNLowLevelILInstruction {
|
||||
use self::ExprInfo::*;
|
||||
|
||||
match *self {
|
||||
Undef(ref op) => &op.op,
|
||||
|
||||
Unimpl(ref op) => &op.op,
|
||||
|
||||
FlagCond(ref op) => &op.op,
|
||||
FlagGroup(ref op) => &op.op,
|
||||
|
||||
CmpE(ref op) | CmpNe(ref op) | CmpSlt(ref op) | CmpUlt(ref op) | CmpSle(ref op)
|
||||
| CmpUle(ref op) | CmpSge(ref op) | CmpUge(ref op) | CmpSgt(ref op)
|
||||
| CmpUgt(ref op) | FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op)
|
||||
| FcmpGE(ref op) | FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => &op.op,
|
||||
|
||||
Load(ref op) => &op.op,
|
||||
|
||||
Pop(ref op) => &op.op,
|
||||
|
||||
Reg(ref op) => &op.op,
|
||||
|
||||
Flag(ref op) => &op.op,
|
||||
|
||||
FlagBit(ref op) => &op.op,
|
||||
|
||||
Const(ref op) | ConstPtr(ref op) => &op.op,
|
||||
|
||||
Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => &op.op,
|
||||
|
||||
Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
|
||||
| Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op)
|
||||
| MulsDp(ref op) | MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op)
|
||||
| Mods(ref op) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => &op.op,
|
||||
|
||||
DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => &op.op,
|
||||
|
||||
Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
|
||||
| BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
|
||||
| FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
|
||||
| Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => &op.op,
|
||||
|
||||
UnimplMem(ref op) => &op.op,
|
||||
//TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'func, A> ExprInfo<'func, A, Mutable, NonSSA<LiftedNonSSA>>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
{
|
||||
pub fn flag_write(&self) -> Option<A::FlagWrite> {
|
||||
use self::ExprInfo::*;
|
||||
|
||||
match *self {
|
||||
Undef(ref _op) => None,
|
||||
|
||||
Unimpl(ref _op) => None,
|
||||
|
||||
FlagCond(ref _op) => None,
|
||||
FlagGroup(ref _op) => None,
|
||||
|
||||
CmpE(ref _op) | CmpNe(ref _op) | CmpSlt(ref _op) | CmpUlt(ref _op)
|
||||
| CmpSle(ref _op) | CmpUle(ref _op) | CmpSge(ref _op) | CmpUge(ref _op)
|
||||
| CmpSgt(ref _op) | CmpUgt(ref _op) | FcmpE(ref _op) | FcmpNE(ref _op)
|
||||
| FcmpLT(ref _op) | FcmpLE(ref _op) | FcmpGE(ref _op) | FcmpGT(ref _op)
|
||||
| FcmpO(ref _op) | FcmpUO(ref _op) => None,
|
||||
|
||||
Load(ref op) => op.flag_write(),
|
||||
|
||||
Pop(ref op) => op.flag_write(),
|
||||
|
||||
Reg(ref op) => op.flag_write(),
|
||||
|
||||
Flag(ref op) => op.flag_write(),
|
||||
|
||||
FlagBit(ref op) => op.flag_write(),
|
||||
|
||||
Const(ref op) | ConstPtr(ref op) => op.flag_write(),
|
||||
|
||||
Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => op.flag_write(),
|
||||
|
||||
Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
|
||||
| Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op)
|
||||
| MulsDp(ref op) | MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op)
|
||||
| Mods(ref op) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => {
|
||||
op.flag_write()
|
||||
}
|
||||
|
||||
DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => op.flag_write(),
|
||||
|
||||
Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
|
||||
| BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
|
||||
| FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
|
||||
| Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => op.flag_write(),
|
||||
|
||||
UnimplMem(ref op) => op.flag_write(),
|
||||
//TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'func, A, M, V> fmt::Debug for ExprInfo<'func, A, M, NonSSA<V>>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::ExprInfo::*;
|
||||
|
||||
match *self {
|
||||
Undef(..) => f.write_str("undefined"),
|
||||
|
||||
Unimpl(..) => f.write_str("unimplemented"),
|
||||
|
||||
FlagCond(..) => f.write_str("some_flag_cond"),
|
||||
FlagGroup(..) => f.write_str("some_flag_group"),
|
||||
|
||||
CmpE(ref op) | CmpNe(ref op) | CmpSlt(ref op) | CmpUlt(ref op) | CmpSle(ref op)
|
||||
| CmpUle(ref op) | CmpSge(ref op) | CmpUge(ref op) | CmpSgt(ref op)
|
||||
| CmpUgt(ref op) | FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op)
|
||||
| FcmpGE(ref op) | FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => {
|
||||
let left = op.left();
|
||||
let right = op.right();
|
||||
|
||||
write!(
|
||||
f,
|
||||
"{:?}({}, {:?}, {:?})",
|
||||
op.op.operation,
|
||||
op.size(),
|
||||
left,
|
||||
right
|
||||
)
|
||||
}
|
||||
|
||||
Load(ref op) => {
|
||||
let source = op.source_mem_expr();
|
||||
let size = op.size();
|
||||
|
||||
write!(f, "[{:?}].{}", source, size)
|
||||
}
|
||||
|
||||
Pop(ref op) => write!(f, "pop.{}", op.size()),
|
||||
|
||||
Reg(ref op) => {
|
||||
let reg = op.source_reg();
|
||||
let size = op.size();
|
||||
|
||||
let size = match reg {
|
||||
Register::Temp(_) => Some(size),
|
||||
Register::ArchReg(ref r) if r.info().size() != size => Some(size),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
match size {
|
||||
Some(s) => write!(f, "{:?}.{}", reg, s),
|
||||
_ => write!(f, "{:?}", reg),
|
||||
}
|
||||
}
|
||||
|
||||
Flag(ref _op) => write!(f, "flag"), // TODO
|
||||
|
||||
FlagBit(ref _op) => write!(f, "flag_bit"), // TODO
|
||||
|
||||
Const(ref op) | ConstPtr(ref op) => write!(f, "0x{:x}", op.value()),
|
||||
|
||||
Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => {
|
||||
let left = op.left();
|
||||
let right = op.right();
|
||||
let carry = op.carry();
|
||||
|
||||
write!(
|
||||
f,
|
||||
"{:?}({}, {:?}, {:?}, carry: {:?})",
|
||||
op.op.operation,
|
||||
op.size(),
|
||||
left,
|
||||
right,
|
||||
carry
|
||||
)
|
||||
}
|
||||
|
||||
Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
|
||||
| Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op)
|
||||
| MulsDp(ref op) | MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op)
|
||||
| Mods(ref op) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => {
|
||||
let left = op.left();
|
||||
let right = op.right();
|
||||
|
||||
write!(
|
||||
f,
|
||||
"{:?}({}, {:?}, {:?})",
|
||||
op.op.operation,
|
||||
op.size(),
|
||||
left,
|
||||
right
|
||||
)
|
||||
}
|
||||
|
||||
DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => {
|
||||
let high = op.high();
|
||||
let low = op.low();
|
||||
let right = op.right();
|
||||
|
||||
write!(
|
||||
f,
|
||||
"{:?}({}, {:?}:{:?},{:?})",
|
||||
op.op.operation,
|
||||
op.size(),
|
||||
high,
|
||||
low,
|
||||
right
|
||||
)
|
||||
}
|
||||
|
||||
Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
|
||||
| BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
|
||||
| FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
|
||||
| Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => write!(
|
||||
f,
|
||||
"{:?}({}, {:?})",
|
||||
op.op.operation,
|
||||
op.size(),
|
||||
op.operand()
|
||||
),
|
||||
|
||||
UnimplMem(ref op) => write!(f, "unimplemented_mem({:?})", op.mem_expr()),
|
||||
//TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
208
src/llil/function.rs
Normal file
208
src/llil/function.rs
Normal file
@@ -0,0 +1,208 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use binaryninjacore_sys::BNFreeLowLevelILFunction;
|
||||
use binaryninjacore_sys::BNLowLevelILFunction;
|
||||
use binaryninjacore_sys::BNNewLowLevelILFunctionReference;
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::basicblock::BasicBlock;
|
||||
use crate::rc::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Mutable;
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Finalized;
|
||||
|
||||
pub trait FunctionMutability: 'static {}
|
||||
impl FunctionMutability for Mutable {}
|
||||
impl FunctionMutability for Finalized {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct LiftedNonSSA;
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct RegularNonSSA;
|
||||
|
||||
pub trait NonSSAVariant: 'static {}
|
||||
impl NonSSAVariant for LiftedNonSSA {}
|
||||
impl NonSSAVariant for RegularNonSSA {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct SSA;
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct NonSSA<V: NonSSAVariant>(V);
|
||||
|
||||
pub trait FunctionForm: 'static {}
|
||||
impl FunctionForm for SSA {}
|
||||
impl<V: NonSSAVariant> FunctionForm for NonSSA<V> {}
|
||||
|
||||
pub struct Function<A: Architecture, M: FunctionMutability, F: FunctionForm> {
|
||||
pub(crate) borrower: A::Handle,
|
||||
pub(crate) handle: *mut BNLowLevelILFunction,
|
||||
_arch: PhantomData<*mut A>,
|
||||
_mutability: PhantomData<M>,
|
||||
_form: PhantomData<F>,
|
||||
}
|
||||
|
||||
unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Send for Function<A, M, F> {}
|
||||
unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Sync for Function<A, M, F> {}
|
||||
|
||||
impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Eq for Function<A, M, F> {}
|
||||
impl<A: Architecture, M: FunctionMutability, F: FunctionForm> PartialEq for Function<A, M, F> {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
self.handle == rhs.handle
|
||||
}
|
||||
}
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Hash for Function<A, M, F> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.handle.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'func, A, M, F> Function<A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub(crate) unsafe fn from_raw(borrower: A::Handle, handle: *mut BNLowLevelILFunction) -> Self {
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Self {
|
||||
borrower,
|
||||
handle,
|
||||
_arch: PhantomData,
|
||||
_mutability: PhantomData,
|
||||
_form: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn arch(&self) -> &A {
|
||||
self.borrower.borrow()
|
||||
}
|
||||
|
||||
pub fn instruction_at<L: Into<Location>>(&self, loc: L) -> Option<Instruction<A, M, F>> {
|
||||
use binaryninjacore_sys::BNGetLowLevelILInstructionCount;
|
||||
use binaryninjacore_sys::BNLowLevelILGetInstructionStart;
|
||||
|
||||
let loc: Location = loc.into();
|
||||
let arch_handle = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
|
||||
|
||||
unsafe {
|
||||
let instr_idx = BNLowLevelILGetInstructionStart(self.handle, arch_handle.0, loc.addr);
|
||||
|
||||
if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) {
|
||||
None
|
||||
} else {
|
||||
Some(Instruction {
|
||||
function: self,
|
||||
instr_idx,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instruction_from_idx(&self, instr_idx: usize) -> Instruction<A, M, F> {
|
||||
unsafe {
|
||||
use binaryninjacore_sys::BNGetLowLevelILInstructionCount;
|
||||
if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) {
|
||||
panic!("instruction index {} out of bounds", instr_idx);
|
||||
}
|
||||
|
||||
Instruction {
|
||||
function: self,
|
||||
instr_idx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instruction_count(&self) -> usize {
|
||||
unsafe {
|
||||
use binaryninjacore_sys::BNGetLowLevelILInstructionCount;
|
||||
BNGetLowLevelILInstructionCount(self.handle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL basic blocks are not available until the function object
|
||||
// is finalized, so ensure we can't try requesting basic blocks
|
||||
// during lifting
|
||||
impl<'func, A, F> Function<A, Finalized, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn basic_blocks(&self) -> Array<BasicBlock<LowLevelBlock<A, Finalized, F>>> {
|
||||
use binaryninjacore_sys::BNGetLowLevelILBasicBlockList;
|
||||
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let blocks = BNGetLowLevelILBasicBlockList(self.handle, &mut count);
|
||||
let context = LowLevelBlock { function: self };
|
||||
|
||||
Array::new(blocks, count, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'func, A, M, F> ToOwned for Function<A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'func, A, M, F> RefCountable for Function<A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
borrower: handle.borrower.clone(),
|
||||
handle: BNNewLowLevelILFunctionReference(handle.handle),
|
||||
_arch: PhantomData,
|
||||
_mutability: PhantomData,
|
||||
_form: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeLowLevelILFunction(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'func, A, M, F> fmt::Debug for Function<A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "<llil func handle {:p}>", self.handle)
|
||||
}
|
||||
}
|
||||
202
src/llil/instruction.rs
Normal file
202
src/llil/instruction.rs
Normal file
@@ -0,0 +1,202 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use binaryninjacore_sys::BNGetLowLevelILByIndex;
|
||||
use binaryninjacore_sys::BNGetLowLevelILIndexForInstruction;
|
||||
use binaryninjacore_sys::BNLowLevelILInstruction;
|
||||
|
||||
use super::operation;
|
||||
use super::operation::Operation;
|
||||
use super::*;
|
||||
|
||||
use crate::architecture::Architecture;
|
||||
|
||||
pub struct Instruction<'func, A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub(crate) function: &'func Function<A, M, F>,
|
||||
pub(crate) instr_idx: usize,
|
||||
}
|
||||
|
||||
fn common_info<'func, A, M, F>(
|
||||
function: &'func Function<A, M, F>,
|
||||
op: BNLowLevelILInstruction,
|
||||
) -> Option<InstrInfo<'func, A, M, F>>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
use binaryninjacore_sys::BNLowLevelILOperation::*;
|
||||
|
||||
match op.operation {
|
||||
LLIL_NOP => InstrInfo::Nop(Operation::new(function, op)).into(),
|
||||
LLIL_JUMP => InstrInfo::Jump(Operation::new(function, op)).into(),
|
||||
LLIL_JUMP_TO => InstrInfo::JumpTo(Operation::new(function, op)).into(),
|
||||
LLIL_RET => InstrInfo::Ret(Operation::new(function, op)).into(),
|
||||
LLIL_NORET => InstrInfo::NoRet(Operation::new(function, op)).into(),
|
||||
LLIL_IF => InstrInfo::If(Operation::new(function, op)).into(),
|
||||
LLIL_GOTO => InstrInfo::Goto(Operation::new(function, op)).into(),
|
||||
LLIL_BP => InstrInfo::Bp(Operation::new(function, op)).into(),
|
||||
LLIL_TRAP => InstrInfo::Trap(Operation::new(function, op)).into(),
|
||||
LLIL_UNDEF => InstrInfo::Undef(Operation::new(function, op)).into(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
use super::VisitorAction;
|
||||
|
||||
macro_rules! visit {
|
||||
($f:expr, $($e:expr),*) => {
|
||||
if let VisitorAction::Halt = $f($($e,)*) {
|
||||
return VisitorAction::Halt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn common_visit<'func, A, M, F, CB>(info: &InstrInfo<'func, A, M, F>, f: &mut CB) -> VisitorAction
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
CB: FnMut(&Expression<'func, A, M, F, ValueExpr>) -> VisitorAction,
|
||||
{
|
||||
use self::InstrInfo::*;
|
||||
|
||||
match *info {
|
||||
Jump(ref op) => visit!(f, &op.target()),
|
||||
JumpTo(ref op) => visit!(f, &op.target()),
|
||||
Ret(ref op) => visit!(f, &op.target()),
|
||||
If(ref op) => visit!(f, &op.condition()),
|
||||
Value(ref e, _) => visit!(f, e),
|
||||
_ => {}
|
||||
};
|
||||
|
||||
VisitorAction::Sibling
|
||||
}
|
||||
|
||||
impl<'func, A, M, V> Instruction<'func, A, M, NonSSA<V>>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
pub fn info(&self) -> InstrInfo<'func, A, M, NonSSA<V>> {
|
||||
use binaryninjacore_sys::BNLowLevelILOperation::*;
|
||||
|
||||
let expr_idx =
|
||||
unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.instr_idx) };
|
||||
let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, expr_idx) };
|
||||
|
||||
match op.operation {
|
||||
LLIL_SET_REG => InstrInfo::SetReg(Operation::new(self.function, op)),
|
||||
LLIL_SET_REG_SPLIT => InstrInfo::SetRegSplit(Operation::new(self.function, op)),
|
||||
LLIL_SET_FLAG => InstrInfo::SetFlag(Operation::new(self.function, op)),
|
||||
LLIL_STORE => InstrInfo::Store(Operation::new(self.function, op)),
|
||||
LLIL_PUSH => InstrInfo::Push(Operation::new(self.function, op)),
|
||||
LLIL_CALL | LLIL_CALL_STACK_ADJUST => {
|
||||
InstrInfo::Call(Operation::new(self.function, op))
|
||||
}
|
||||
LLIL_TAILCALL => InstrInfo::TailCall(Operation::new(self.function, op)),
|
||||
LLIL_SYSCALL => InstrInfo::Syscall(Operation::new(self.function, op)),
|
||||
LLIL_INTRINSIC => InstrInfo::Intrinsic(Operation::new(self.function, op)),
|
||||
_ => {
|
||||
common_info(self.function, op).unwrap_or_else(|| {
|
||||
// Hopefully this is a bare value. If it isn't (expression
|
||||
// from wrong function form or similar) it won't really cause
|
||||
// any problems as it'll come back as undefined when queried.
|
||||
let expr = Expression::new(self.function, expr_idx);
|
||||
|
||||
let info = unsafe { expr.info_from_op(op) };
|
||||
|
||||
InstrInfo::Value(expr, info)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction
|
||||
where
|
||||
F: FnMut(
|
||||
&Expression<'func, A, M, NonSSA<V>, ValueExpr>,
|
||||
&ExprInfo<'func, A, M, NonSSA<V>>,
|
||||
) -> VisitorAction,
|
||||
{
|
||||
use self::InstrInfo::*;
|
||||
let info = self.info();
|
||||
|
||||
let fb = &mut |e: &Expression<'func, A, M, NonSSA<V>, ValueExpr>| e.visit_tree(f);
|
||||
|
||||
match info {
|
||||
SetReg(ref op) => visit!(fb, &op.source_expr()),
|
||||
SetRegSplit(ref op) => visit!(fb, &op.source_expr()),
|
||||
SetFlag(ref op) => visit!(fb, &op.source_expr()),
|
||||
Store(ref op) => {
|
||||
visit!(fb, &op.dest_mem_expr());
|
||||
visit!(fb, &op.source_expr());
|
||||
}
|
||||
Push(ref op) => visit!(fb, &op.operand()),
|
||||
Call(ref op) | TailCall(ref op) => visit!(fb, &op.target()),
|
||||
Intrinsic(ref _op) => {
|
||||
// TODO: Use this when we support expression lists
|
||||
// for expr in op.source_exprs() {
|
||||
// visit!(fb, expr);
|
||||
// }
|
||||
}
|
||||
_ => visit!(common_visit, &info, fb),
|
||||
}
|
||||
|
||||
VisitorAction::Sibling
|
||||
}
|
||||
}
|
||||
|
||||
pub enum InstrInfo<'func, A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
Nop(Operation<'func, A, M, F, operation::NoArgs>),
|
||||
SetReg(Operation<'func, A, M, F, operation::SetReg>),
|
||||
SetRegSplit(Operation<'func, A, M, F, operation::SetRegSplit>),
|
||||
SetFlag(Operation<'func, A, M, F, operation::SetFlag>),
|
||||
Store(Operation<'func, A, M, F, operation::Store>),
|
||||
Push(Operation<'func, A, M, F, operation::UnaryOp>), // TODO needs a real op
|
||||
|
||||
Jump(Operation<'func, A, M, F, operation::Jump>),
|
||||
JumpTo(Operation<'func, A, M, F, operation::JumpTo>),
|
||||
|
||||
Call(Operation<'func, A, M, F, operation::Call>),
|
||||
TailCall(Operation<'func, A, M, F, operation::Call>),
|
||||
|
||||
Ret(Operation<'func, A, M, F, operation::Ret>),
|
||||
NoRet(Operation<'func, A, M, F, operation::NoArgs>),
|
||||
|
||||
If(Operation<'func, A, M, F, operation::If>),
|
||||
Goto(Operation<'func, A, M, F, operation::Goto>),
|
||||
|
||||
Syscall(Operation<'func, A, M, F, operation::Syscall>),
|
||||
Intrinsic(Operation<'func, A, M, F, operation::Intrinsic>),
|
||||
Bp(Operation<'func, A, M, F, operation::NoArgs>),
|
||||
Trap(Operation<'func, A, M, F, operation::Trap>),
|
||||
Undef(Operation<'func, A, M, F, operation::NoArgs>),
|
||||
|
||||
Value(
|
||||
Expression<'func, A, M, F, ValueExpr>,
|
||||
ExprInfo<'func, A, M, F>,
|
||||
),
|
||||
}
|
||||
1435
src/llil/lifting.rs
Normal file
1435
src/llil/lifting.rs
Normal file
File diff suppressed because it is too large
Load Diff
95
src/llil/mod.rs
Normal file
95
src/llil/mod.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
// TODO : provide some way to forbid emitting register reads for certain registers
|
||||
// also writing for certain registers (e.g. zero register must prohibit il.set_reg and il.reg
|
||||
// (replace with nop or const(0) respectively)
|
||||
// requirements on load/store memory address sizes?
|
||||
// can reg/set_reg be used with sizes that differ from what is in BNRegisterInfo?
|
||||
|
||||
use crate::architecture::Architecture;
|
||||
use crate::architecture::Register as ArchReg;
|
||||
use crate::function::Location;
|
||||
|
||||
mod block;
|
||||
mod expression;
|
||||
mod function;
|
||||
mod instruction;
|
||||
mod lifting;
|
||||
pub mod operation;
|
||||
|
||||
pub use self::expression::*;
|
||||
pub use self::function::*;
|
||||
pub use self::instruction::*;
|
||||
pub use self::lifting::get_default_flag_cond_llil;
|
||||
pub use self::lifting::get_default_flag_write_llil;
|
||||
pub use self::lifting::{
|
||||
ExpressionBuilder, FlagWriteOp, Label, Liftable, LiftableWithSize, RegisterOrConstant,
|
||||
};
|
||||
|
||||
pub use self::block::Block as LowLevelBlock;
|
||||
pub use self::block::BlockIter as LowLevelBlockIter;
|
||||
|
||||
pub type Lifter<Arch> = Function<Arch, Mutable, NonSSA<LiftedNonSSA>>;
|
||||
pub type LiftedFunction<Arch> = Function<Arch, Finalized, NonSSA<LiftedNonSSA>>;
|
||||
pub type LiftedExpr<'a, Arch> = Expression<'a, Arch, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>;
|
||||
pub type RegularFunction<Arch> = Function<Arch, Finalized, NonSSA<RegularNonSSA>>;
|
||||
pub type SSAFunction<Arch> = Function<Arch, Finalized, SSA>;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Register<R: ArchReg> {
|
||||
ArchReg(R),
|
||||
Temp(u32),
|
||||
}
|
||||
|
||||
impl<R: ArchReg> Register<R> {
|
||||
fn id(&self) -> u32 {
|
||||
match *self {
|
||||
Register::ArchReg(ref r) => r.id(),
|
||||
Register::Temp(id) => 0x8000_0000 | id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ArchReg> fmt::Debug for Register<R> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Register::ArchReg(ref r) => write!(f, "{}", r.name().as_ref()),
|
||||
Register::Temp(id) => write!(f, "temp{}", id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum SSARegister<R: ArchReg> {
|
||||
Full(Register<R>, u32), // no such thing as partial access to a temp register, I think
|
||||
Partial(R, u32, R), // partial accesses only possible for arch registers, I think
|
||||
}
|
||||
|
||||
impl<R: ArchReg> SSARegister<R> {
|
||||
pub fn version(&self) -> u32 {
|
||||
match *self {
|
||||
SSARegister::Full(_, ver) | SSARegister::Partial(_, ver, _) => ver,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum VisitorAction {
|
||||
Descend,
|
||||
Sibling,
|
||||
Halt,
|
||||
}
|
||||
663
src/llil/operation.rs
Normal file
663
src/llil/operation.rs
Normal file
@@ -0,0 +1,663 @@
|
||||
// Copyright 2021-2024 Vector 35 Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use binaryninjacore_sys::BNLowLevelILInstruction;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct Operation<'func, A, M, F, O>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
O: OperationArguments,
|
||||
{
|
||||
pub(crate) function: &'func Function<A, M, F>,
|
||||
pub(crate) op: BNLowLevelILInstruction,
|
||||
_args: PhantomData<O>,
|
||||
}
|
||||
|
||||
impl<'func, A, M, F, O> Operation<'func, A, M, F, O>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
O: OperationArguments,
|
||||
{
|
||||
pub(crate) fn new(function: &'func Function<A, M, F>, op: BNLowLevelILInstruction) -> Self {
|
||||
Self {
|
||||
function,
|
||||
op,
|
||||
_args: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn address(&self) -> u64 {
|
||||
self.op.address
|
||||
}
|
||||
}
|
||||
|
||||
impl<'func, A, M, O> Operation<'func, A, M, NonSSA<LiftedNonSSA>, O>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
O: OperationArguments,
|
||||
{
|
||||
pub fn flag_write(&self) -> Option<A::FlagWrite> {
|
||||
match self.op.flags {
|
||||
0 => None,
|
||||
id => self.function.arch().flag_write_from_id(id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_NOP, LLIL_NORET, LLIL_BP, LLIL_UNDEF, LLIL_UNIMPL
|
||||
pub struct NoArgs;
|
||||
|
||||
// LLIL_POP
|
||||
pub struct Pop;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, Pop>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_SYSCALL, LLIL_SYSCALL_SSA
|
||||
pub struct Syscall;
|
||||
|
||||
// LLIL_INTRINSIC, LLIL_INTRINSIC_SSA
|
||||
pub struct Intrinsic;
|
||||
|
||||
impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Intrinsic>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
// TODO: Support register and expression lists
|
||||
pub fn intrinsic(&self) -> Option<A::Intrinsic> {
|
||||
let raw_id = self.op.operands[2] as u32;
|
||||
self.function.arch().intrinsic_from_id(raw_id)
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_SET_REG, LLIL_SET_REG_SSA, LLIL_SET_REG_PARTIAL_SSA
|
||||
pub struct SetReg;
|
||||
|
||||
impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetReg>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
|
||||
pub fn dest_reg(&self) -> Register<A::Register> {
|
||||
let raw_id = self.op.operands[0] as u32;
|
||||
|
||||
if raw_id >= 0x8000_0000 {
|
||||
Register::Temp(raw_id & 0x7fff_ffff)
|
||||
} else {
|
||||
self.function
|
||||
.arch()
|
||||
.register_from_id(raw_id)
|
||||
.map(Register::ArchReg)
|
||||
.unwrap_or_else(|| {
|
||||
error!(
|
||||
"got garbage register from LLIL_SET_REG @ 0x{:x}",
|
||||
self.op.address
|
||||
);
|
||||
|
||||
Register::Temp(0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[1] as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_SET_REG_SPLIT, LLIL_SET_REG_SPLIT_SSA
|
||||
pub struct SetRegSplit;
|
||||
|
||||
impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetRegSplit>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
|
||||
pub fn dest_reg_high(&self) -> Register<A::Register> {
|
||||
let raw_id = self.op.operands[0] as u32;
|
||||
|
||||
if raw_id >= 0x8000_0000 {
|
||||
Register::Temp(raw_id & 0x7fff_ffff)
|
||||
} else {
|
||||
self.function
|
||||
.arch()
|
||||
.register_from_id(raw_id)
|
||||
.map(Register::ArchReg)
|
||||
.unwrap_or_else(|| {
|
||||
error!(
|
||||
"got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}",
|
||||
self.op.address
|
||||
);
|
||||
|
||||
Register::Temp(0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dest_reg_low(&self) -> Register<A::Register> {
|
||||
let raw_id = self.op.operands[1] as u32;
|
||||
|
||||
if raw_id >= 0x8000_0000 {
|
||||
Register::Temp(raw_id & 0x7fff_ffff)
|
||||
} else {
|
||||
self.function
|
||||
.arch()
|
||||
.register_from_id(raw_id)
|
||||
.map(Register::ArchReg)
|
||||
.unwrap_or_else(|| {
|
||||
error!(
|
||||
"got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}",
|
||||
self.op.address
|
||||
);
|
||||
|
||||
Register::Temp(0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[2] as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_SET_FLAG, LLIL_SET_FLAG_SSA
|
||||
pub struct SetFlag;
|
||||
|
||||
impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetFlag>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[1] as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_LOAD, LLIL_LOAD_SSA
|
||||
pub struct Load;
|
||||
|
||||
impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Load>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
|
||||
pub fn source_mem_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_STORE, LLIL_STORE_SSA
|
||||
pub struct Store;
|
||||
|
||||
impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Store>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
|
||||
pub fn dest_mem_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
|
||||
pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[1] as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_REG, LLIL_REG_SSA, LLIL_REG_SSA_PARTIAL
|
||||
pub struct Reg;
|
||||
|
||||
impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Reg>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
|
||||
pub fn source_reg(&self) -> Register<A::Register> {
|
||||
let raw_id = self.op.operands[0] as u32;
|
||||
|
||||
if raw_id >= 0x8000_0000 {
|
||||
Register::Temp(raw_id & 0x7fff_ffff)
|
||||
} else {
|
||||
self.function
|
||||
.arch()
|
||||
.register_from_id(raw_id)
|
||||
.map(Register::ArchReg)
|
||||
.unwrap_or_else(|| {
|
||||
error!(
|
||||
"got garbage register from LLIL_REG @ 0x{:x}",
|
||||
self.op.address
|
||||
);
|
||||
|
||||
Register::Temp(0)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_FLAG, LLIL_FLAG_SSA
|
||||
pub struct Flag;
|
||||
|
||||
// LLIL_FLAG_BIT, LLIL_FLAG_BIT_SSA
|
||||
pub struct FlagBit;
|
||||
|
||||
// LLIL_JUMP
|
||||
pub struct Jump;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, Jump>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_JUMP_TO
|
||||
pub struct JumpTo;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, JumpTo>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
// TODO target list
|
||||
}
|
||||
|
||||
// LLIL_CALL, LLIL_CALL_SSA
|
||||
pub struct Call;
|
||||
|
||||
impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Call>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
pub fn target(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
|
||||
pub fn stack_adjust(&self) -> Option<u64> {
|
||||
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CALL_STACK_ADJUST;
|
||||
|
||||
if self.op.operation == LLIL_CALL_STACK_ADJUST {
|
||||
Some(self.op.operands[1])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_RET
|
||||
pub struct Ret;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, Ret>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_IF
|
||||
pub struct If;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, If>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn condition(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
|
||||
pub fn true_target(&self) -> Instruction<'func, A, M, F> {
|
||||
Instruction {
|
||||
function: self.function,
|
||||
instr_idx: self.op.operands[1] as usize,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn false_target(&self) -> Instruction<'func, A, M, F> {
|
||||
Instruction {
|
||||
function: self.function,
|
||||
instr_idx: self.op.operands[2] as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_GOTO
|
||||
pub struct Goto;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, Goto>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn target(&self) -> Instruction<'func, A, M, F> {
|
||||
Instruction {
|
||||
function: self.function,
|
||||
instr_idx: self.op.operands[0] as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_FLAG_COND
|
||||
pub struct FlagCond;
|
||||
|
||||
// LLIL_FLAG_GROUP
|
||||
pub struct FlagGroup;
|
||||
|
||||
impl<'func, A, M> Operation<'func, A, M, NonSSA<LiftedNonSSA>, FlagGroup>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
{
|
||||
pub fn flag_group(&self) -> A::FlagGroup {
|
||||
let id = self.op.operands[0] as u32;
|
||||
self.function.arch().flag_group_from_id(id).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_TRAP
|
||||
pub struct Trap;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, Trap>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn vector(&self) -> u64 {
|
||||
self.op.operands[0]
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_REG_PHI
|
||||
pub struct RegPhi;
|
||||
|
||||
// LLIL_FLAG_PHI
|
||||
pub struct FlagPhi;
|
||||
|
||||
// LLIL_MEM_PHI
|
||||
pub struct MemPhi;
|
||||
|
||||
// LLIL_CONST, LLIL_CONST_PTR
|
||||
pub struct Const;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, Const>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
|
||||
pub fn value(&self) -> u64 {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let raw = self.op.operands[0] as i64;
|
||||
|
||||
let is_safe = match raw.overflowing_shr(self.op.size as u32 * 8) {
|
||||
(_, true) => true,
|
||||
(res, false) => [-1, 0].contains(&res),
|
||||
};
|
||||
|
||||
if !is_safe {
|
||||
error!(
|
||||
"il expr @ {:x} contains constant 0x{:x} as {} byte value (doesn't fit!)",
|
||||
self.op.address, self.op.operands[0], self.op.size
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut mask = -1i64 as u64;
|
||||
|
||||
if self.op.size < mem::size_of::<u64>() {
|
||||
mask <<= self.op.size * 8;
|
||||
mask = !mask;
|
||||
}
|
||||
|
||||
self.op.operands[0] & mask
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_ADD, LLIL_SUB, LLIL_AND, LLIL_OR
|
||||
// LLIL_XOR, LLIL_LSL, LLIL_LSR, LLIL_ASR
|
||||
// LLIL_ROL, LLIL_ROR, LLIL_MUL, LLIL_MULU_DP,
|
||||
// LLIL_MULS_DP, LLIL_DIVU, LLIL_DIVS, LLIL_MODU,
|
||||
// LLIL_MODS
|
||||
pub struct BinaryOp;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOp>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
|
||||
pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
|
||||
pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[1] as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_ADC, LLIL_SBB, LLIL_RLC, LLIL_RRC
|
||||
pub struct BinaryOpCarry;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOpCarry>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
|
||||
pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
|
||||
pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[1] as usize)
|
||||
}
|
||||
|
||||
pub fn carry(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[2] as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_DIVS_DP, LLIL_DIVU_DP, LLIL_MODU_DP, LLIL_MODS_DP
|
||||
pub struct DoublePrecDivOp;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, DoublePrecDivOp>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
|
||||
pub fn high(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
|
||||
pub fn low(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[1] as usize)
|
||||
}
|
||||
|
||||
pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[2] as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_PUSH, LLIL_NEG, LLIL_NOT, LLIL_SX,
|
||||
// LLIL_ZX, LLIL_LOW_PART, LLIL_BOOL_TO_INT, LLIL_UNIMPL_MEM
|
||||
pub struct UnaryOp;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, UnaryOp>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
|
||||
pub fn operand(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_CMP_X
|
||||
pub struct Condition;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, Condition>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
|
||||
pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
|
||||
pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[1] as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_UNIMPL_MEM
|
||||
pub struct UnimplMem;
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, UnimplMem>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
|
||||
pub fn mem_expr(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO TEST_BIT
|
||||
|
||||
pub trait OperationArguments: 'static {}
|
||||
|
||||
impl OperationArguments for NoArgs {}
|
||||
impl OperationArguments for Pop {}
|
||||
impl OperationArguments for Syscall {}
|
||||
impl OperationArguments for Intrinsic {}
|
||||
impl OperationArguments for SetReg {}
|
||||
impl OperationArguments for SetRegSplit {}
|
||||
impl OperationArguments for SetFlag {}
|
||||
impl OperationArguments for Load {}
|
||||
impl OperationArguments for Store {}
|
||||
impl OperationArguments for Reg {}
|
||||
impl OperationArguments for Flag {}
|
||||
impl OperationArguments for FlagBit {}
|
||||
impl OperationArguments for Jump {}
|
||||
impl OperationArguments for JumpTo {}
|
||||
impl OperationArguments for Call {}
|
||||
impl OperationArguments for Ret {}
|
||||
impl OperationArguments for If {}
|
||||
impl OperationArguments for Goto {}
|
||||
impl OperationArguments for FlagCond {}
|
||||
impl OperationArguments for FlagGroup {}
|
||||
impl OperationArguments for Trap {}
|
||||
impl OperationArguments for RegPhi {}
|
||||
impl OperationArguments for FlagPhi {}
|
||||
impl OperationArguments for MemPhi {}
|
||||
impl OperationArguments for Const {}
|
||||
impl OperationArguments for BinaryOp {}
|
||||
impl OperationArguments for BinaryOpCarry {}
|
||||
impl OperationArguments for DoublePrecDivOp {}
|
||||
impl OperationArguments for UnaryOp {}
|
||||
impl OperationArguments for Condition {}
|
||||
impl OperationArguments for UnimplMem {}
|
||||
175
src/logger.rs
Normal file
175
src/logger.rs
Normal file
@@ -0,0 +1,175 @@
|
||||
#![allow(clippy::needless_doctest_main)]
|
||||
|
||||
//! To use logging in your script, do something like:
|
||||
//!
|
||||
//! ```
|
||||
//! use binaryninja::logger;
|
||||
//! use log::{info, LevelFilter};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! logger::init(LevelFilter::Warn).expect("Unable to initialize logger");
|
||||
//! info!("The logger has been initialized!");
|
||||
//! // Your code here...
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! or
|
||||
//!
|
||||
//!```
|
||||
//! use binaryninja::logger;
|
||||
//! use log::{info, LevelFilter};
|
||||
//!
|
||||
//! #[no_mangle]
|
||||
//! pub extern "C" fn CorePluginInit() -> bool {
|
||||
//! logger::init(LevelFilter::Warn).expect("Unable to initialize logger");
|
||||
//! info!("The logger has been initialized!");
|
||||
//! // Your code here...
|
||||
//! true
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
|
||||
use crate::string::BnStr;
|
||||
|
||||
pub use binaryninjacore_sys::BNLogLevel as Level;
|
||||
use binaryninjacore_sys::{BNLogListener, BNUpdateLogListeners};
|
||||
|
||||
use log;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
|
||||
struct Logger;
|
||||
static LOGGER: Logger = Logger;
|
||||
|
||||
impl log::Log for Logger {
|
||||
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
use self::Level::*;
|
||||
use binaryninjacore_sys::BNLog;
|
||||
use log::Level;
|
||||
use std::ffi::CString;
|
||||
|
||||
let level = match record.level() {
|
||||
Level::Error => ErrorLog,
|
||||
Level::Warn => WarningLog,
|
||||
Level::Info => InfoLog,
|
||||
Level::Debug | Level::Trace => DebugLog,
|
||||
};
|
||||
|
||||
if let Ok(msg) = CString::new(format!("{}", record.args())) {
|
||||
let percent_s = CString::new("%s").expect("'%s' has no null bytes");
|
||||
unsafe {
|
||||
BNLog(
|
||||
0,
|
||||
level,
|
||||
std::ptr::null(),
|
||||
0,
|
||||
percent_s.as_ptr(),
|
||||
msg.as_ptr(),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
/// Uses BinaryNinja's logging functionality as the sink for
|
||||
/// Rust's `log` crate.
|
||||
pub fn init(filter: log::LevelFilter) -> Result<(), log::SetLoggerError> {
|
||||
log::set_max_level(filter);
|
||||
log::set_logger(&LOGGER)
|
||||
}
|
||||
|
||||
pub trait LogListener: 'static + Sync {
|
||||
fn log(&self, session: usize, level: Level, msg: &BnStr, logger_name: &BnStr, tid: usize);
|
||||
fn level(&self) -> Level;
|
||||
fn close(&self) {}
|
||||
}
|
||||
|
||||
pub struct LogGuard<L: LogListener> {
|
||||
ctxt: *mut L,
|
||||
}
|
||||
|
||||
impl<L: LogListener> Drop for LogGuard<L> {
|
||||
fn drop(&mut self) {
|
||||
use binaryninjacore_sys::BNUnregisterLogListener;
|
||||
|
||||
let mut bn_obj = BNLogListener {
|
||||
context: self.ctxt as *mut _,
|
||||
log: Some(cb_log::<L>),
|
||||
close: Some(cb_close::<L>),
|
||||
getLogLevel: Some(cb_level::<L>),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
BNUnregisterLogListener(&mut bn_obj);
|
||||
BNUpdateLogListeners();
|
||||
|
||||
let _listener = Box::from_raw(self.ctxt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_listener<L: LogListener>(listener: L) -> LogGuard<L> {
|
||||
use binaryninjacore_sys::BNRegisterLogListener;
|
||||
|
||||
let raw = Box::into_raw(Box::new(listener));
|
||||
let mut bn_obj = BNLogListener {
|
||||
context: raw as *mut _,
|
||||
log: Some(cb_log::<L>),
|
||||
close: Some(cb_close::<L>),
|
||||
getLogLevel: Some(cb_level::<L>),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
BNRegisterLogListener(&mut bn_obj);
|
||||
BNUpdateLogListeners();
|
||||
}
|
||||
|
||||
LogGuard { ctxt: raw }
|
||||
}
|
||||
|
||||
extern "C" fn cb_log<L>(
|
||||
ctxt: *mut c_void,
|
||||
session: usize,
|
||||
level: Level,
|
||||
msg: *const c_char,
|
||||
logger_name: *const c_char,
|
||||
tid: usize,
|
||||
) where
|
||||
L: LogListener,
|
||||
{
|
||||
ffi_wrap!("LogListener::log", unsafe {
|
||||
let listener = &*(ctxt as *const L);
|
||||
listener.log(
|
||||
session,
|
||||
level,
|
||||
BnStr::from_raw(msg),
|
||||
BnStr::from_raw(logger_name),
|
||||
tid,
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_close<L>(ctxt: *mut c_void)
|
||||
where
|
||||
L: LogListener,
|
||||
{
|
||||
ffi_wrap!("LogListener::close", unsafe {
|
||||
let listener = &*(ctxt as *const L);
|
||||
listener.close();
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn cb_level<L>(ctxt: *mut c_void) -> Level
|
||||
where
|
||||
L: LogListener,
|
||||
{
|
||||
ffi_wrap!("LogListener::log", unsafe {
|
||||
let listener = &*(ctxt as *const L);
|
||||
listener.level()
|
||||
})
|
||||
}
|
||||
716
src/metadata.rs
Normal file
716
src/metadata.rs
Normal file
@@ -0,0 +1,716 @@
|
||||
use crate::rc::{
|
||||
Array, CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Guard, Ref, RefCountable,
|
||||
};
|
||||
use crate::string::{BnStrCompatible, BnString};
|
||||
use binaryninjacore_sys::*;
|
||||
use std::collections::HashMap;
|
||||
use std::os::raw::c_char;
|
||||
use std::slice;
|
||||
|
||||
pub type MetadataType = BNMetadataType;
|
||||
|
||||
pub struct Metadata {
|
||||
pub(crate) handle: *mut BNMetadata,
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
pub(crate) unsafe fn from_raw(handle: *mut BNMetadata) -> Self {
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn ref_from_raw(handle: *mut BNMetadata) -> Ref<Self> {
|
||||
Ref::new(Self::from_raw(handle))
|
||||
}
|
||||
|
||||
pub fn new_of_type(metadata_type: MetadataType) -> Ref<Self> {
|
||||
unsafe { Self::ref_from_raw(BNCreateMetadataOfType(metadata_type)) }
|
||||
}
|
||||
|
||||
pub fn get_type(&self) -> MetadataType {
|
||||
unsafe { BNMetadataGetType(self.handle) }
|
||||
}
|
||||
|
||||
pub fn get_boolean(&self) -> Result<bool, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::BooleanDataType => Ok(unsafe { BNMetadataGetBoolean(self.handle) }),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_unsigned_integer(&self) -> Result<u64, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::UnsignedIntegerDataType => {
|
||||
Ok(unsafe { BNMetadataGetUnsignedInteger(self.handle) })
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_signed_integer(&self) -> Result<i64, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::SignedIntegerDataType => {
|
||||
Ok(unsafe { BNMetadataGetSignedInteger(self.handle) })
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_double(&self) -> Result<f64, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::DoubleDataType => Ok(unsafe { BNMetadataGetDouble(self.handle) }),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_string(&self) -> Result<BnString, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::StringDataType => {
|
||||
let ptr: *mut c_char = unsafe { BNMetadataGetString(self.handle) };
|
||||
if ptr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
Ok(unsafe { BnString::from_raw(ptr) })
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_boolean_list(&self) -> Result<Vec<bool>, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::ArrayDataType => {
|
||||
let mut size: usize = 0;
|
||||
let ptr: *mut bool = unsafe { BNMetadataGetBooleanList(self.handle, &mut size) };
|
||||
if ptr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
let list = unsafe { slice::from_raw_parts(ptr, size) };
|
||||
let vec = Vec::from(list);
|
||||
unsafe { BNFreeMetadataBooleanList(ptr, size) };
|
||||
Ok(vec)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_unsigned_integer_list(&self) -> Result<Vec<u64>, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::ArrayDataType => {
|
||||
let mut size: usize = 0;
|
||||
let ptr: *mut u64 =
|
||||
unsafe { BNMetadataGetUnsignedIntegerList(self.handle, &mut size) };
|
||||
if ptr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
let list = unsafe { slice::from_raw_parts(ptr, size) };
|
||||
let vec = Vec::from(list);
|
||||
unsafe { BNFreeMetadataUnsignedIntegerList(ptr, size) };
|
||||
Ok(vec)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_signed_integer_list(&self) -> Result<Vec<i64>, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::ArrayDataType => {
|
||||
let mut size: usize = 0;
|
||||
let ptr: *mut i64 =
|
||||
unsafe { BNMetadataGetSignedIntegerList(self.handle, &mut size) };
|
||||
if ptr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
let list = unsafe { slice::from_raw_parts(ptr, size) };
|
||||
let vec = Vec::from(list);
|
||||
unsafe { BNFreeMetadataSignedIntegerList(ptr, size) };
|
||||
Ok(vec)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_double_list(&self) -> Result<Vec<f64>, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::ArrayDataType => {
|
||||
let mut size: usize = 0;
|
||||
let ptr: *mut f64 = unsafe { BNMetadataGetDoubleList(self.handle, &mut size) };
|
||||
if ptr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
let list = unsafe { slice::from_raw_parts(ptr, size) };
|
||||
let vec = Vec::from(list);
|
||||
unsafe { BNFreeMetadataDoubleList(ptr, size) };
|
||||
Ok(vec)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_string_list(&self) -> Result<Vec<BnString>, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::ArrayDataType => {
|
||||
let mut size: usize = 0;
|
||||
let ptr: *mut *mut c_char =
|
||||
unsafe { BNMetadataGetStringList(self.handle, &mut size) };
|
||||
if ptr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
let list = unsafe { slice::from_raw_parts(ptr, size) };
|
||||
let vec = list
|
||||
.iter()
|
||||
.map(|ptr| unsafe { BnString::from_raw(*ptr) })
|
||||
.collect::<Vec<_>>();
|
||||
unsafe { BNFreeMetadataStringList(ptr, size) };
|
||||
Ok(vec)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_raw(&self) -> Result<Vec<u8>, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::RawDataType => {
|
||||
let mut size: usize = 0;
|
||||
let ptr: *mut u8 = unsafe { BNMetadataGetRaw(self.handle, &mut size) };
|
||||
if ptr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let list = unsafe { slice::from_raw_parts(ptr, size) };
|
||||
let vec = Vec::from(list);
|
||||
unsafe { BNFreeMetadataRaw(ptr) };
|
||||
Ok(vec)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_array(&self) -> Result<Array<Metadata>, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::ArrayDataType => {
|
||||
let mut size: usize = 0;
|
||||
let ptr: *mut *mut BNMetadata =
|
||||
unsafe { BNMetadataGetArray(self.handle, &mut size) };
|
||||
if ptr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(unsafe { Array::new(ptr, size, ()) })
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_value_store(&self) -> Result<HashMap<BnString, Ref<Metadata>>, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::KeyValueDataType => {
|
||||
let ptr: *mut BNMetadataValueStore =
|
||||
unsafe { BNMetadataGetValueStore(self.handle) };
|
||||
if ptr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let size = unsafe { (*ptr).size };
|
||||
let keys_ptr: *mut *mut c_char = unsafe { (*ptr).keys };
|
||||
let keys = unsafe { slice::from_raw_parts(keys_ptr, size) };
|
||||
let values_ptr: *mut *mut BNMetadata = unsafe { (*ptr).values };
|
||||
let values: &[*mut BNMetadata] = unsafe { slice::from_raw_parts(values_ptr, size) };
|
||||
|
||||
let mut map = HashMap::new();
|
||||
for i in 0..size {
|
||||
let key = unsafe { BnString::from_raw(keys[i]) };
|
||||
|
||||
let value = unsafe {
|
||||
Ref::<Metadata>::new(Self {
|
||||
handle: BNNewMetadataReference(values[i]),
|
||||
})
|
||||
};
|
||||
map.insert(key, value);
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { BNMetadataSize(self.handle) }
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
unsafe { BNMetadataSize(self.handle) == 0 }
|
||||
}
|
||||
|
||||
pub fn index(&self, index: usize) -> Result<Option<Ref<Metadata>>, ()> {
|
||||
if self.get_type() != MetadataType::ArrayDataType {
|
||||
return Err(());
|
||||
}
|
||||
let ptr: *mut BNMetadata = unsafe { BNMetadataGetForIndex(self.handle, index) };
|
||||
if ptr.is_null() {
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(Some(unsafe { Self::ref_from_raw(ptr) }))
|
||||
}
|
||||
|
||||
pub fn get<S: BnStrCompatible>(&self, key: S) -> Result<Option<Ref<Metadata>>, ()> {
|
||||
if self.get_type() != MetadataType::KeyValueDataType {
|
||||
return Err(());
|
||||
}
|
||||
let ptr: *mut BNMetadata = unsafe {
|
||||
BNMetadataGetForKey(
|
||||
self.handle,
|
||||
key.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
|
||||
)
|
||||
};
|
||||
if ptr.is_null() {
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(Some(unsafe { Self::ref_from_raw(ptr) }))
|
||||
}
|
||||
|
||||
pub fn push(&self, value: &Metadata) -> Result<(), ()> {
|
||||
if self.get_type() != MetadataType::ArrayDataType {
|
||||
return Err(());
|
||||
}
|
||||
unsafe { BNMetadataArrayAppend(self.handle, value.handle) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert<S: BnStrCompatible>(&self, key: S, value: &Metadata) -> Result<(), ()> {
|
||||
if self.get_type() != MetadataType::KeyValueDataType {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
BNMetadataSetValueForKey(
|
||||
self.handle,
|
||||
key.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
|
||||
value.handle,
|
||||
)
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_index(&self, index: usize) -> Result<(), ()> {
|
||||
if self.get_type() != MetadataType::ArrayDataType {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
unsafe { BNMetadataRemoveIndex(self.handle, index) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_key<S: BnStrCompatible>(&self, key: S) -> Result<(), ()> {
|
||||
if self.get_type() != MetadataType::KeyValueDataType {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
BNMetadataRemoveKey(
|
||||
self.handle,
|
||||
key.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
|
||||
)
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for Metadata {}
|
||||
unsafe impl Send for Metadata {}
|
||||
|
||||
unsafe impl RefCountable for Metadata {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewMetadataReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeMetadata(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for Metadata {
|
||||
type Raw = *mut BNMetadata;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for Metadata {
|
||||
unsafe fn free(raw: *mut *mut BNMetadata, _count: usize, _context: &()) {
|
||||
BNFreeMetadataArray(raw);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for Metadata {
|
||||
type Wrapped = Guard<'a, Metadata>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a *mut BNMetadata, context: &'a ()) -> Guard<'a, Metadata> {
|
||||
Guard::new(Metadata::from_raw(*raw), context)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for Metadata {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Ref<Metadata> {
|
||||
fn from(value: bool) -> Self {
|
||||
unsafe { Metadata::ref_from_raw(BNCreateMetadataBooleanData(value)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Ref<Metadata> {
|
||||
fn from(value: u64) -> Self {
|
||||
unsafe { Metadata::ref_from_raw(BNCreateMetadataUnsignedIntegerData(value)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Ref<Metadata> {
|
||||
fn from(value: i64) -> Self {
|
||||
unsafe { Metadata::ref_from_raw(BNCreateMetadataSignedIntegerData(value)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Ref<Metadata> {
|
||||
fn from(value: f64) -> Self {
|
||||
unsafe { Metadata::ref_from_raw(BNCreateMetadataDoubleData(value)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Ref<Metadata> {
|
||||
fn from(value: String) -> Self {
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataStringData(
|
||||
value.into_bytes_with_nul().as_ptr() as *const c_char,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Ref<Metadata> {
|
||||
fn from(value: &str) -> Self {
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataStringData(
|
||||
value.into_bytes_with_nul().as_ptr() as *const c_char,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<Ref<Metadata>>> From<&T> for Ref<Metadata> {
|
||||
fn from(value: &T) -> Self {
|
||||
value.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Vec<u8>> for Ref<Metadata> {
|
||||
fn from(value: &Vec<u8>) -> Self {
|
||||
unsafe { Metadata::ref_from_raw(BNCreateMetadataRawData(value.as_ptr(), value.len())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Vec<Ref<Metadata>>> for Ref<Metadata> {
|
||||
fn from(value: &Vec<Ref<Metadata>>) -> Self {
|
||||
let mut pointers: Vec<*mut BNMetadata> = vec![];
|
||||
for v in value.iter() {
|
||||
pointers.push(v.as_ref().handle);
|
||||
}
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataArray(pointers.as_mut_ptr(), pointers.len()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Array<Metadata>> for Ref<Metadata> {
|
||||
fn from(value: &Array<Metadata>) -> Self {
|
||||
let mut pointers: Vec<*mut BNMetadata> = vec![];
|
||||
for v in value.iter() {
|
||||
pointers.push(v.as_ref().handle);
|
||||
}
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataArray(pointers.as_mut_ptr(), pointers.len()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BnStrCompatible> From<HashMap<S, Ref<Metadata>>> for Ref<Metadata> {
|
||||
fn from(value: HashMap<S, Ref<Metadata>>) -> Self {
|
||||
let mut key_refs: Vec<S::Result> = vec![];
|
||||
let mut keys: Vec<*const c_char> = vec![];
|
||||
let mut values: Vec<*mut BNMetadata> = vec![];
|
||||
for (k, v) in value.into_iter() {
|
||||
key_refs.push(k.into_bytes_with_nul());
|
||||
values.push(v.as_ref().handle);
|
||||
}
|
||||
for k in &key_refs {
|
||||
keys.push(k.as_ref().as_ptr() as *const c_char);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataValueStore(
|
||||
keys.as_mut_ptr(),
|
||||
values.as_mut_ptr(),
|
||||
keys.len(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BnStrCompatible + Copy, T: Into<Ref<Metadata>>> From<&[(S, T)]> for Ref<Metadata> {
|
||||
fn from(value: &[(S, T)]) -> Self {
|
||||
let mut key_refs: Vec<S::Result> = vec![];
|
||||
let mut keys: Vec<*const c_char> = vec![];
|
||||
let mut values: Vec<*mut BNMetadata> = vec![];
|
||||
for (k, v) in value.iter() {
|
||||
key_refs.push(k.into_bytes_with_nul());
|
||||
let value_metadata: Ref<Metadata> = v.into();
|
||||
values.push(value_metadata.handle);
|
||||
}
|
||||
for k in &key_refs {
|
||||
keys.push(k.as_ref().as_ptr() as *const c_char);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataValueStore(
|
||||
keys.as_mut_ptr(),
|
||||
values.as_mut_ptr(),
|
||||
keys.len(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BnStrCompatible + Copy, T: Into<Ref<Metadata>>, const N: usize> From<[(S, T); N]>
|
||||
for Ref<Metadata>
|
||||
{
|
||||
fn from(value: [(S, T); N]) -> Self {
|
||||
let mut key_refs: Vec<S::Result> = vec![];
|
||||
let mut keys: Vec<*const c_char> = vec![];
|
||||
let mut values: Vec<*mut BNMetadata> = vec![];
|
||||
for (k, v) in value.into_iter() {
|
||||
key_refs.push(k.into_bytes_with_nul());
|
||||
let value_metadata: Ref<Metadata> = v.into();
|
||||
values.push(value_metadata.handle);
|
||||
}
|
||||
for k in &key_refs {
|
||||
keys.push(k.as_ref().as_ptr() as *const c_char);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataValueStore(
|
||||
keys.as_mut_ptr(),
|
||||
values.as_mut_ptr(),
|
||||
keys.len(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Vec<bool>> for Ref<Metadata> {
|
||||
fn from(value: &Vec<bool>) -> Self {
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataBooleanListData(
|
||||
value.as_ptr() as *mut bool,
|
||||
value.len(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Vec<u64>> for Ref<Metadata> {
|
||||
fn from(value: &Vec<u64>) -> Self {
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataUnsignedIntegerListData(
|
||||
value.as_ptr() as *mut u64,
|
||||
value.len(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Vec<i64>> for Ref<Metadata> {
|
||||
fn from(value: &Vec<i64>) -> Self {
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataSignedIntegerListData(
|
||||
value.as_ptr() as *mut i64,
|
||||
value.len(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Vec<f64>> for Ref<Metadata> {
|
||||
fn from(value: &Vec<f64>) -> Self {
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataDoubleListData(
|
||||
value.as_ptr() as *mut f64,
|
||||
value.len(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BnStrCompatible> From<Vec<S>> for Ref<Metadata> {
|
||||
fn from(value: Vec<S>) -> Self {
|
||||
let mut refs = vec![];
|
||||
for v in value {
|
||||
refs.push(v.into_bytes_with_nul());
|
||||
}
|
||||
let mut pointers = vec![];
|
||||
for r in &refs {
|
||||
pointers.push(r.as_ref().as_ptr() as *const c_char);
|
||||
}
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataStringListData(
|
||||
pointers.as_ptr() as *mut *const c_char,
|
||||
pointers.len(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Metadata {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
unsafe { BNMetadataIsEqual(self.handle, other.handle) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Ref<Metadata> {}
|
||||
|
||||
impl TryFrom<&Metadata> for bool {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_boolean()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for u64 {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_unsigned_integer()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for i64 {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_signed_integer()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for f64 {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_double()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for BnString {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for String {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_string().map(|s| s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for Vec<bool> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_boolean_list()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for Vec<u64> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_unsigned_integer_list()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for Vec<i64> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_signed_integer_list()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for Vec<f64> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_double_list()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for Vec<BnString> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_string_list()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for Vec<String> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value
|
||||
.get_string_list()
|
||||
.map(|v| v.into_iter().map(|s| s.to_string()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for Vec<u8> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for Array<Metadata> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for HashMap<BnString, Ref<Metadata>> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value.get_value_store()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Metadata> for HashMap<String, Ref<Metadata>> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Metadata) -> Result<Self, Self::Error> {
|
||||
value
|
||||
.get_value_store()
|
||||
.map(|m| m.into_iter().map(|(k, v)| (k.to_string(), v)).collect())
|
||||
}
|
||||
}
|
||||
63
src/mlil/block.rs
Normal file
63
src/mlil/block.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use binaryninjacore_sys::BNGetMediumLevelILIndexForInstruction;
|
||||
|
||||
use crate::basicblock::{BasicBlock, BlockContext};
|
||||
use crate::rc::Ref;
|
||||
|
||||
use super::{MediumLevelILFunction, MediumLevelILInstruction};
|
||||
|
||||
pub struct MediumLevelILBlockIter {
|
||||
function: Ref<MediumLevelILFunction>,
|
||||
range: Range<u64>,
|
||||
}
|
||||
|
||||
impl Iterator for MediumLevelILBlockIter {
|
||||
type Item = MediumLevelILInstruction;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.range
|
||||
.next()
|
||||
.map(|i| unsafe {
|
||||
BNGetMediumLevelILIndexForInstruction(self.function.handle, i as usize)
|
||||
})
|
||||
.map(|i| MediumLevelILInstruction::new(&self.function, i))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MediumLevelILBlock {
|
||||
pub(crate) function: Ref<MediumLevelILFunction>,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for MediumLevelILBlock {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "mlil_bb {:?}", self.function)
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockContext for MediumLevelILBlock {
|
||||
type Iter = MediumLevelILBlockIter;
|
||||
type Instruction = MediumLevelILInstruction;
|
||||
|
||||
fn start(&self, block: &BasicBlock<Self>) -> MediumLevelILInstruction {
|
||||
let expr_idx = unsafe {
|
||||
BNGetMediumLevelILIndexForInstruction(self.function.handle, block.raw_start() as usize)
|
||||
};
|
||||
MediumLevelILInstruction::new(&self.function, expr_idx)
|
||||
}
|
||||
|
||||
fn iter(&self, block: &BasicBlock<Self>) -> MediumLevelILBlockIter {
|
||||
MediumLevelILBlockIter {
|
||||
function: self.function.to_owned(),
|
||||
range: block.raw_start()..block.raw_end(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for MediumLevelILBlock {
|
||||
fn clone(&self) -> Self {
|
||||
MediumLevelILBlock {
|
||||
function: self.function.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
116
src/mlil/function.rs
Normal file
116
src/mlil/function.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
use binaryninjacore_sys::BNFreeMediumLevelILFunction;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILBasicBlockList;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILInstructionCount;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILOwnerFunction;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILSSAForm;
|
||||
use binaryninjacore_sys::BNMediumLevelILFunction;
|
||||
use binaryninjacore_sys::BNMediumLevelILGetInstructionStart;
|
||||
use binaryninjacore_sys::BNNewMediumLevelILFunctionReference;
|
||||
|
||||
use crate::basicblock::BasicBlock;
|
||||
use crate::function::Function;
|
||||
use crate::function::Location;
|
||||
use crate::rc::{Array, Ref, RefCountable};
|
||||
|
||||
use super::{MediumLevelILBlock, MediumLevelILInstruction};
|
||||
|
||||
pub struct MediumLevelILFunction {
|
||||
pub(crate) handle: *mut BNMediumLevelILFunction,
|
||||
}
|
||||
|
||||
unsafe impl Send for MediumLevelILFunction {}
|
||||
unsafe impl Sync for MediumLevelILFunction {}
|
||||
|
||||
impl Eq for MediumLevelILFunction {}
|
||||
impl PartialEq for MediumLevelILFunction {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
self.handle == rhs.handle
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for MediumLevelILFunction {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.handle.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl MediumLevelILFunction {
|
||||
pub(crate) unsafe fn from_raw(handle: *mut BNMediumLevelILFunction) -> Self {
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub fn instruction_at<L: Into<Location>>(&self, loc: L) -> Option<MediumLevelILInstruction> {
|
||||
let loc: Location = loc.into();
|
||||
let arch_handle = loc.arch.unwrap();
|
||||
|
||||
let expr_idx =
|
||||
unsafe { BNMediumLevelILGetInstructionStart(self.handle, arch_handle.0, loc.addr) };
|
||||
|
||||
if expr_idx >= self.instruction_count() {
|
||||
None
|
||||
} else {
|
||||
Some(MediumLevelILInstruction::new(self, expr_idx))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instruction_from_idx(&self, expr_idx: usize) -> MediumLevelILInstruction {
|
||||
MediumLevelILInstruction::new(self, expr_idx)
|
||||
}
|
||||
|
||||
pub fn instruction_count(&self) -> usize {
|
||||
unsafe { BNGetMediumLevelILInstructionCount(self.handle) }
|
||||
}
|
||||
|
||||
pub fn ssa_form(&self) -> MediumLevelILFunction {
|
||||
let ssa = unsafe { BNGetMediumLevelILSSAForm(self.handle) };
|
||||
assert!(!ssa.is_null());
|
||||
MediumLevelILFunction { handle: ssa }
|
||||
}
|
||||
|
||||
pub fn get_function(&self) -> Ref<Function> {
|
||||
unsafe {
|
||||
let func = BNGetMediumLevelILOwnerFunction(self.handle);
|
||||
Function::from_raw(func)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn basic_blocks(&self) -> Array<BasicBlock<MediumLevelILBlock>> {
|
||||
let mut count = 0;
|
||||
let blocks = unsafe { BNGetMediumLevelILBasicBlockList(self.handle, &mut count) };
|
||||
let context = MediumLevelILBlock {
|
||||
function: self.to_owned(),
|
||||
};
|
||||
|
||||
unsafe { Array::new(blocks, count, context) }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for MediumLevelILFunction {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for MediumLevelILFunction {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewMediumLevelILFunctionReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeMediumLevelILFunction(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for MediumLevelILFunction {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "<mlil func handle {:p}>", self.handle)
|
||||
}
|
||||
}
|
||||
824
src/mlil/instruction.rs
Normal file
824
src/mlil/instruction.rs
Normal file
@@ -0,0 +1,824 @@
|
||||
use binaryninjacore_sys::BNGetMediumLevelILByIndex;
|
||||
use binaryninjacore_sys::BNMediumLevelILOperation;
|
||||
|
||||
use crate::mlil::MediumLevelILLiftedOperation;
|
||||
use crate::rc::Ref;
|
||||
|
||||
use super::operation::*;
|
||||
use super::{MediumLevelILFunction, MediumLevelILLiftedInstruction};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MediumLevelILInstruction {
|
||||
pub(crate) function: Ref<MediumLevelILFunction>,
|
||||
pub(crate) address: u64,
|
||||
pub(crate) operation: MediumLevelILOperation,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum MediumLevelILOperation {
|
||||
Nop(NoArgs),
|
||||
Noret(NoArgs),
|
||||
Bp(NoArgs),
|
||||
Undef(NoArgs),
|
||||
Unimpl(NoArgs),
|
||||
If(MediumLevelILOperationIf),
|
||||
FloatConst(FloatConst),
|
||||
Const(Constant),
|
||||
ConstPtr(Constant),
|
||||
Import(Constant),
|
||||
ExternPtr(ExternPtr),
|
||||
ConstData(ConstData),
|
||||
Jump(Jump),
|
||||
RetHint(Jump),
|
||||
StoreSsa(StoreSsa),
|
||||
StoreStructSsa(StoreStructSsa),
|
||||
StoreStruct(StoreStruct),
|
||||
Store(Store),
|
||||
JumpTo(JumpTo),
|
||||
Goto(Goto),
|
||||
FreeVarSlot(FreeVarSlot),
|
||||
SetVarField(SetVarField),
|
||||
SetVar(SetVar),
|
||||
FreeVarSlotSsa(FreeVarSlotSsa),
|
||||
SetVarSsaField(SetVarSsaField),
|
||||
SetVarAliasedField(SetVarSsaField),
|
||||
SetVarAliased(SetVarAliased),
|
||||
SetVarSsa(SetVarSsa),
|
||||
VarPhi(VarPhi),
|
||||
MemPhi(MemPhi),
|
||||
VarSplit(VarSplit),
|
||||
SetVarSplit(SetVarSplit),
|
||||
VarSplitSsa(VarSplitSsa),
|
||||
SetVarSplitSsa(SetVarSplitSsa),
|
||||
Add(BinaryOp),
|
||||
Sub(BinaryOp),
|
||||
And(BinaryOp),
|
||||
Or(BinaryOp),
|
||||
Xor(BinaryOp),
|
||||
Lsl(BinaryOp),
|
||||
Lsr(BinaryOp),
|
||||
Asr(BinaryOp),
|
||||
Rol(BinaryOp),
|
||||
Ror(BinaryOp),
|
||||
Mul(BinaryOp),
|
||||
MuluDp(BinaryOp),
|
||||
MulsDp(BinaryOp),
|
||||
Divu(BinaryOp),
|
||||
DivuDp(BinaryOp),
|
||||
Divs(BinaryOp),
|
||||
DivsDp(BinaryOp),
|
||||
Modu(BinaryOp),
|
||||
ModuDp(BinaryOp),
|
||||
Mods(BinaryOp),
|
||||
ModsDp(BinaryOp),
|
||||
CmpE(BinaryOp),
|
||||
CmpNe(BinaryOp),
|
||||
CmpSlt(BinaryOp),
|
||||
CmpUlt(BinaryOp),
|
||||
CmpSle(BinaryOp),
|
||||
CmpUle(BinaryOp),
|
||||
CmpSge(BinaryOp),
|
||||
CmpUge(BinaryOp),
|
||||
CmpSgt(BinaryOp),
|
||||
CmpUgt(BinaryOp),
|
||||
TestBit(BinaryOp),
|
||||
AddOverflow(BinaryOp),
|
||||
FcmpE(BinaryOp),
|
||||
FcmpNe(BinaryOp),
|
||||
FcmpLt(BinaryOp),
|
||||
FcmpLe(BinaryOp),
|
||||
FcmpGe(BinaryOp),
|
||||
FcmpGt(BinaryOp),
|
||||
FcmpO(BinaryOp),
|
||||
FcmpUo(BinaryOp),
|
||||
Fadd(BinaryOp),
|
||||
Fsub(BinaryOp),
|
||||
Fmul(BinaryOp),
|
||||
Fdiv(BinaryOp),
|
||||
Adc(BinaryOpCarry),
|
||||
Sbb(BinaryOpCarry),
|
||||
Rlc(BinaryOpCarry),
|
||||
Rrc(BinaryOpCarry),
|
||||
Call(Call),
|
||||
Tailcall(Call),
|
||||
Syscall(Syscall),
|
||||
Intrinsic(Intrinsic),
|
||||
IntrinsicSsa(IntrinsicSsa),
|
||||
CallSsa(CallSsa),
|
||||
TailcallSsa(CallSsa),
|
||||
CallUntypedSsa(CallUntypedSsa),
|
||||
TailcallUntypedSsa(CallUntypedSsa),
|
||||
SyscallSsa(SyscallSsa),
|
||||
SyscallUntypedSsa(SyscallUntypedSsa),
|
||||
CallUntyped(CallUntyped),
|
||||
TailcallUntyped(CallUntyped),
|
||||
SyscallUntyped(SyscallUntyped),
|
||||
SeparateParamList(SeparateParamList),
|
||||
SharedParamSlot(SharedParamSlot),
|
||||
Neg(UnaryOp),
|
||||
Not(UnaryOp),
|
||||
Sx(UnaryOp),
|
||||
Zx(UnaryOp),
|
||||
LowPart(UnaryOp),
|
||||
BoolToInt(UnaryOp),
|
||||
UnimplMem(UnaryOp),
|
||||
Fsqrt(UnaryOp),
|
||||
Fneg(UnaryOp),
|
||||
Fabs(UnaryOp),
|
||||
FloatToInt(UnaryOp),
|
||||
IntToFloat(UnaryOp),
|
||||
FloatConv(UnaryOp),
|
||||
RoundToInt(UnaryOp),
|
||||
Floor(UnaryOp),
|
||||
Ceil(UnaryOp),
|
||||
Ftrunc(UnaryOp),
|
||||
Load(UnaryOp),
|
||||
LoadStruct(LoadStruct),
|
||||
LoadStructSsa(LoadStructSsa),
|
||||
LoadSsa(LoadSsa),
|
||||
Ret(Ret),
|
||||
Var(Var),
|
||||
AddressOf(Var),
|
||||
VarField(Field),
|
||||
AddressOfField(Field),
|
||||
VarSsa(VarSsa),
|
||||
VarAliased(VarSsa),
|
||||
VarSsaField(VarSsaField),
|
||||
VarAliasedField(VarSsaField),
|
||||
Trap(Trap),
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for MediumLevelILInstruction {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"<{} at 0x{:08}>",
|
||||
core::any::type_name::<Self>(),
|
||||
self.address,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl MediumLevelILInstruction {
|
||||
pub(crate) fn new(function: &MediumLevelILFunction, idx: usize) -> Self {
|
||||
let op = unsafe { BNGetMediumLevelILByIndex(function.handle, idx) };
|
||||
use BNMediumLevelILOperation::*;
|
||||
use MediumLevelILOperation as Op;
|
||||
let info = match op.operation {
|
||||
MLIL_NOP => Op::Nop(NoArgs::default()),
|
||||
MLIL_NORET => Op::Noret(NoArgs::default()),
|
||||
MLIL_BP => Op::Bp(NoArgs::default()),
|
||||
MLIL_UNDEF => Op::Undef(NoArgs::default()),
|
||||
MLIL_UNIMPL => Op::Unimpl(NoArgs::default()),
|
||||
MLIL_IF => Op::If(MediumLevelILOperationIf::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1],
|
||||
op.operands[2],
|
||||
)),
|
||||
MLIL_FLOAT_CONST => Op::FloatConst(FloatConst::new(op.operands[0], op.size)),
|
||||
MLIL_CONST => Op::Const(Constant::new(op.operands[0])),
|
||||
MLIL_CONST_PTR => Op::ConstPtr(Constant::new(op.operands[0])),
|
||||
MLIL_IMPORT => Op::Import(Constant::new(op.operands[0])),
|
||||
MLIL_EXTERN_PTR => Op::ExternPtr(ExternPtr::new(op.operands[0], op.operands[1])),
|
||||
MLIL_CONST_DATA => Op::ConstData(ConstData::new((op.operands[0], op.operands[1]))),
|
||||
MLIL_JUMP => Op::Jump(Jump::new(op.operands[0] as usize)),
|
||||
MLIL_RET_HINT => Op::RetHint(Jump::new(op.operands[0] as usize)),
|
||||
MLIL_STORE_SSA => Op::StoreSsa(StoreSsa::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1],
|
||||
op.operands[2],
|
||||
op.operands[3] as usize,
|
||||
)),
|
||||
MLIL_STORE_STRUCT_SSA => Op::StoreStructSsa(StoreStructSsa::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1],
|
||||
op.operands[2],
|
||||
op.operands[3],
|
||||
op.operands[4] as usize,
|
||||
)),
|
||||
MLIL_STORE_STRUCT => Op::StoreStruct(StoreStruct::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1],
|
||||
op.operands[2] as usize,
|
||||
)),
|
||||
MLIL_STORE => Op::Store(Store::new(op.operands[0] as usize, op.operands[1] as usize)),
|
||||
MLIL_JUMP_TO => Op::JumpTo(JumpTo::new(
|
||||
op.operands[0] as usize,
|
||||
(op.operands[1] as usize, op.operands[2] as usize),
|
||||
)),
|
||||
MLIL_GOTO => Op::Goto(Goto::new(op.operands[0])),
|
||||
MLIL_FREE_VAR_SLOT => Op::FreeVarSlot(FreeVarSlot::new(op.operands[0])),
|
||||
MLIL_SET_VAR_FIELD => Op::SetVarField(SetVarField::new(
|
||||
op.operands[0],
|
||||
op.operands[1],
|
||||
op.operands[2] as usize,
|
||||
)),
|
||||
MLIL_SET_VAR => Op::SetVar(SetVar::new(op.operands[0], op.operands[1] as usize)),
|
||||
MLIL_FREE_VAR_SLOT_SSA => Op::FreeVarSlotSsa(FreeVarSlotSsa::new(
|
||||
(op.operands[0], op.operands[1] as usize),
|
||||
(op.operands[0], op.operands[2] as usize),
|
||||
)),
|
||||
MLIL_SET_VAR_SSA_FIELD => Op::SetVarSsaField(SetVarSsaField::new(
|
||||
(op.operands[0], op.operands[1] as usize),
|
||||
(op.operands[0], op.operands[2] as usize),
|
||||
op.operands[3],
|
||||
op.operands[4] as usize,
|
||||
)),
|
||||
MLIL_SET_VAR_ALIASED_FIELD => Op::SetVarAliasedField(SetVarSsaField::new(
|
||||
(op.operands[0], op.operands[1] as usize),
|
||||
(op.operands[0], op.operands[2] as usize),
|
||||
op.operands[3],
|
||||
op.operands[4] as usize,
|
||||
)),
|
||||
MLIL_SET_VAR_ALIASED => Op::SetVarAliased(SetVarAliased::new(
|
||||
(op.operands[0], op.operands[1] as usize),
|
||||
(op.operands[0], op.operands[2] as usize),
|
||||
op.operands[3] as usize,
|
||||
)),
|
||||
MLIL_SET_VAR_SSA => Op::SetVarSsa(SetVarSsa::new(
|
||||
(op.operands[0], op.operands[1] as usize),
|
||||
op.operands[2] as usize,
|
||||
)),
|
||||
MLIL_VAR_PHI => Op::VarPhi(VarPhi::new(
|
||||
(op.operands[0], op.operands[1] as usize),
|
||||
(op.operands[2] as usize, op.operands[3] as usize),
|
||||
)),
|
||||
MLIL_MEM_PHI => Op::MemPhi(MemPhi::new(
|
||||
op.operands[0],
|
||||
(op.operands[1] as usize, op.operands[2] as usize),
|
||||
)),
|
||||
MLIL_VAR_SPLIT => Op::VarSplit(VarSplit::new(op.operands[0], op.operands[1])),
|
||||
MLIL_SET_VAR_SPLIT => Op::SetVarSplit(SetVarSplit::new(
|
||||
op.operands[0],
|
||||
op.operands[1],
|
||||
op.operands[2] as usize,
|
||||
)),
|
||||
MLIL_VAR_SPLIT_SSA => Op::VarSplitSsa(VarSplitSsa::new(
|
||||
(op.operands[0], op.operands[1] as usize),
|
||||
(op.operands[2], op.operands[3] as usize),
|
||||
)),
|
||||
MLIL_SET_VAR_SPLIT_SSA => Op::SetVarSplitSsa(SetVarSplitSsa::new(
|
||||
(op.operands[0], op.operands[1] as usize),
|
||||
(op.operands[2], op.operands[3] as usize),
|
||||
op.operands[4] as usize,
|
||||
)),
|
||||
MLIL_ADD => Op::Add(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_SUB => Op::Sub(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_AND => Op::And(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_OR => Op::Or(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_XOR => Op::Xor(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_LSL => Op::Lsl(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_LSR => Op::Lsr(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_ASR => Op::Asr(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_ROL => Op::Rol(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_ROR => Op::Ror(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_MUL => Op::Mul(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_MULU_DP => Op::MuluDp(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_MULS_DP => Op::MulsDp(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_DIVU => Op::Divu(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_DIVU_DP => Op::DivuDp(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_DIVS => Op::Divs(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_DIVS_DP => Op::DivsDp(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_MODU => Op::Modu(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_MODU_DP => Op::ModuDp(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_MODS => Op::Mods(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_MODS_DP => Op::ModsDp(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_CMP_E => Op::CmpE(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_CMP_NE => Op::CmpNe(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_CMP_SLT => Op::CmpSlt(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_CMP_ULT => Op::CmpUlt(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_CMP_SLE => Op::CmpSle(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_CMP_ULE => Op::CmpUle(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_CMP_SGE => Op::CmpSge(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_CMP_UGE => Op::CmpUge(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_CMP_SGT => Op::CmpSgt(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_CMP_UGT => Op::CmpUgt(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_TEST_BIT => Op::TestBit(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_ADD_OVERFLOW => Op::AddOverflow(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_FCMP_E => Op::FcmpE(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_FCMP_NE => Op::FcmpNe(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_FCMP_LT => Op::FcmpLt(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_FCMP_LE => Op::FcmpLe(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_FCMP_GE => Op::FcmpGe(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_FCMP_GT => Op::FcmpGt(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_FCMP_O => Op::FcmpO(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_FCMP_UO => Op::FcmpUo(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_FADD => Op::Fadd(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_FSUB => Op::Fsub(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_FMUL => Op::Fmul(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_FDIV => Op::Fdiv(BinaryOp::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
)),
|
||||
MLIL_ADC => Op::Adc(BinaryOpCarry::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
op.operands[2] as usize,
|
||||
)),
|
||||
MLIL_SBB => Op::Sbb(BinaryOpCarry::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
op.operands[2] as usize,
|
||||
)),
|
||||
MLIL_RLC => Op::Rlc(BinaryOpCarry::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
op.operands[2] as usize,
|
||||
)),
|
||||
MLIL_RRC => Op::Rrc(BinaryOpCarry::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
op.operands[2] as usize,
|
||||
)),
|
||||
MLIL_CALL => Op::Call(Call::new(
|
||||
(op.operands[0] as usize, op.operands[1] as usize),
|
||||
op.operands[2] as usize,
|
||||
(op.operands[3] as usize, op.operands[4] as usize),
|
||||
)),
|
||||
MLIL_TAILCALL => Op::Tailcall(Call::new(
|
||||
(op.operands[0] as usize, op.operands[1] as usize),
|
||||
op.operands[2] as usize,
|
||||
(op.operands[3] as usize, op.operands[4] as usize),
|
||||
)),
|
||||
MLIL_SYSCALL => Op::Syscall(Syscall::new(
|
||||
(op.operands[0] as usize, op.operands[1] as usize),
|
||||
(op.operands[2] as usize, op.operands[3] as usize),
|
||||
)),
|
||||
MLIL_INTRINSIC => Op::Intrinsic(Intrinsic::new(
|
||||
(op.operands[0] as usize, op.operands[1] as usize),
|
||||
op.operands[2] as usize,
|
||||
(op.operands[3] as usize, op.operands[4] as usize),
|
||||
)),
|
||||
MLIL_INTRINSIC_SSA => Op::IntrinsicSsa(IntrinsicSsa::new(
|
||||
(op.operands[0] as usize, op.operands[1] as usize),
|
||||
op.operands[2] as usize,
|
||||
(op.operands[3] as usize, op.operands[4] as usize),
|
||||
)),
|
||||
MLIL_CALL_SSA => Op::CallSsa(CallSsa::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
(op.operands[2] as usize, op.operands[3] as usize),
|
||||
op.operands[4],
|
||||
)),
|
||||
MLIL_TAILCALL_SSA => Op::TailcallSsa(CallSsa::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
(op.operands[2] as usize, op.operands[3] as usize),
|
||||
op.operands[4],
|
||||
)),
|
||||
MLIL_CALL_UNTYPED_SSA => Op::CallUntypedSsa(CallUntypedSsa::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
op.operands[2] as usize,
|
||||
op.operands[3] as usize,
|
||||
)),
|
||||
MLIL_TAILCALL_UNTYPED_SSA => Op::TailcallUntypedSsa(CallUntypedSsa::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
op.operands[2] as usize,
|
||||
op.operands[3] as usize,
|
||||
)),
|
||||
MLIL_SYSCALL_SSA => Op::SyscallSsa(SyscallSsa::new(
|
||||
op.operands[0] as usize,
|
||||
(op.operands[1] as usize, op.operands[2] as usize),
|
||||
op.operands[3],
|
||||
)),
|
||||
MLIL_SYSCALL_UNTYPED_SSA => Op::SyscallUntypedSsa(SyscallUntypedSsa::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
op.operands[2] as usize,
|
||||
)),
|
||||
MLIL_CALL_UNTYPED => Op::CallUntyped(CallUntyped::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
op.operands[2] as usize,
|
||||
op.operands[3] as usize,
|
||||
)),
|
||||
MLIL_TAILCALL_UNTYPED => Op::TailcallUntyped(CallUntyped::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
op.operands[2] as usize,
|
||||
op.operands[3] as usize,
|
||||
)),
|
||||
MLIL_SYSCALL_UNTYPED => Op::SyscallUntyped(SyscallUntyped::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1] as usize,
|
||||
op.operands[2] as usize,
|
||||
)),
|
||||
MLIL_SEPARATE_PARAM_LIST => Op::SeparateParamList(SeparateParamList::new(
|
||||
(op.operands[0] as usize, op.operands[1] as usize)
|
||||
)),
|
||||
MLIL_SHARED_PARAM_SLOT => Op::SharedParamSlot(SharedParamSlot::new(
|
||||
(op.operands[0] as usize, op.operands[1] as usize)
|
||||
)),
|
||||
MLIL_NEG => Op::Neg(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_NOT => Op::Not(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_SX => Op::Sx(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_ZX => Op::Zx(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_LOW_PART => Op::LowPart(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_BOOL_TO_INT => Op::BoolToInt(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_UNIMPL_MEM => Op::UnimplMem(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_FSQRT => Op::Fsqrt(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_FNEG => Op::Fneg(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_FABS => Op::Fabs(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_FLOAT_TO_INT => Op::FloatToInt(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_INT_TO_FLOAT => Op::IntToFloat(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_FLOAT_CONV => Op::FloatConv(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_ROUND_TO_INT => Op::RoundToInt(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_FLOOR => Op::Floor(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_CEIL => Op::Ceil(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_FTRUNC => Op::Ftrunc(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_LOAD => Op::Load(UnaryOp::new(op.operands[0] as usize)),
|
||||
MLIL_LOAD_STRUCT => {
|
||||
Op::LoadStruct(LoadStruct::new(op.operands[0] as usize, op.operands[1]))
|
||||
}
|
||||
MLIL_LOAD_STRUCT_SSA => Op::LoadStructSsa(LoadStructSsa::new(
|
||||
op.operands[0] as usize,
|
||||
op.operands[1],
|
||||
op.operands[2],
|
||||
)),
|
||||
MLIL_LOAD_SSA => Op::LoadSsa(LoadSsa::new(op.operands[0] as usize, op.operands[1])),
|
||||
MLIL_RET => Op::Ret(Ret::new((op.operands[0] as usize, op.operands[1] as usize))),
|
||||
MLIL_VAR => Op::Var(Var::new(op.operands[0])),
|
||||
MLIL_ADDRESS_OF => Op::AddressOf(Var::new(op.operands[0])),
|
||||
MLIL_VAR_FIELD => Op::VarField(Field::new(op.operands[0], op.operands[1])),
|
||||
MLIL_ADDRESS_OF_FIELD => Op::AddressOfField(Field::new(op.operands[0], op.operands[1])),
|
||||
MLIL_VAR_SSA => Op::VarSsa(VarSsa::new((op.operands[0], op.operands[1] as usize))),
|
||||
MLIL_VAR_ALIASED => {
|
||||
Op::VarAliased(VarSsa::new((op.operands[0], op.operands[1] as usize)))
|
||||
}
|
||||
MLIL_VAR_SSA_FIELD => Op::VarSsaField(VarSsaField::new(
|
||||
(op.operands[0], op.operands[1] as usize),
|
||||
op.operands[2],
|
||||
)),
|
||||
MLIL_VAR_ALIASED_FIELD => Op::VarAliasedField(VarSsaField::new(
|
||||
(op.operands[0], op.operands[1] as usize),
|
||||
op.operands[2],
|
||||
)),
|
||||
MLIL_TRAP => Op::Trap(Trap::new(op.operands[0])),
|
||||
// translated directly into a list for Expression or Variables
|
||||
MLIL_CALL_OUTPUT | MLIL_CALL_PARAM | MLIL_CALL_PARAM_SSA | MLIL_CALL_OUTPUT_SSA => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
Self {
|
||||
function: function.to_owned(),
|
||||
address: op.address,
|
||||
operation: info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn function(&self) -> &MediumLevelILFunction {
|
||||
&self.function
|
||||
}
|
||||
|
||||
pub fn address(&self) -> u64 {
|
||||
self.address
|
||||
}
|
||||
|
||||
pub fn operation(&self) -> &MediumLevelILOperation {
|
||||
&self.operation
|
||||
}
|
||||
|
||||
pub fn lift(&self) -> MediumLevelILLiftedInstruction {
|
||||
use MediumLevelILLiftedOperation as Lifted;
|
||||
use MediumLevelILOperation::*;
|
||||
|
||||
let operation = match self.operation {
|
||||
Nop(op) => Lifted::Nop(op),
|
||||
Noret(op) => Lifted::Noret(op),
|
||||
Bp(op) => Lifted::Bp(op),
|
||||
Undef(op) => Lifted::Undef(op),
|
||||
Unimpl(op) => Lifted::Unimpl(op),
|
||||
If(op) => Lifted::If(op.lift(&self.function)),
|
||||
FloatConst(op) => Lifted::FloatConst(op),
|
||||
Const(op) => Lifted::Const(op),
|
||||
ConstPtr(op) => Lifted::ConstPtr(op),
|
||||
Import(op) => Lifted::Import(op),
|
||||
ExternPtr(op) => Lifted::ExternPtr(op),
|
||||
ConstData(op) => Lifted::ConstData(op.lift(&self.function)),
|
||||
Jump(op) => Lifted::Jump(op.lift(&self.function)),
|
||||
RetHint(op) => Lifted::RetHint(op.lift(&self.function)),
|
||||
StoreSsa(op) => Lifted::StoreSsa(op.lift(&self.function)),
|
||||
StoreStructSsa(op) => Lifted::StoreStructSsa(op.lift(&self.function)),
|
||||
StoreStruct(op) => Lifted::StoreStruct(op.lift(&self.function)),
|
||||
Store(op) => Lifted::Store(op.lift(&self.function)),
|
||||
JumpTo(op) => Lifted::JumpTo(op.lift(&self.function)),
|
||||
Goto(op) => Lifted::Goto(op),
|
||||
FreeVarSlot(op) => Lifted::FreeVarSlot(op),
|
||||
SetVarField(op) => Lifted::SetVarField(op.lift(&self.function)),
|
||||
SetVar(op) => Lifted::SetVar(op.lift(&self.function)),
|
||||
FreeVarSlotSsa(op) => Lifted::FreeVarSlotSsa(op.lift()),
|
||||
SetVarSsaField(op) => Lifted::SetVarSsaField(op.lift(&self.function)),
|
||||
SetVarAliasedField(op) => Lifted::SetVarAliasedField(op.lift(&self.function)),
|
||||
SetVarAliased(op) => Lifted::SetVarAliased(op.lift(&self.function)),
|
||||
SetVarSsa(op) => Lifted::SetVarSsa(op.lift(&self.function)),
|
||||
VarPhi(op) => Lifted::VarPhi(op.lift(&self.function)),
|
||||
MemPhi(op) => Lifted::MemPhi(op.lift(&self.function)),
|
||||
VarSplit(op) => Lifted::VarSplit(op.lift()),
|
||||
SetVarSplit(op) => Lifted::SetVarSplit(op.lift(&self.function)),
|
||||
VarSplitSsa(op) => Lifted::VarSplitSsa(op.lift()),
|
||||
SetVarSplitSsa(op) => Lifted::SetVarSplitSsa(op.lift(&self.function)),
|
||||
Add(op) => Lifted::Add(op.lift(&self.function)),
|
||||
Sub(op) => Lifted::Sub(op.lift(&self.function)),
|
||||
And(op) => Lifted::And(op.lift(&self.function)),
|
||||
Or(op) => Lifted::Or(op.lift(&self.function)),
|
||||
Xor(op) => Lifted::Xor(op.lift(&self.function)),
|
||||
Lsl(op) => Lifted::Lsl(op.lift(&self.function)),
|
||||
Lsr(op) => Lifted::Lsr(op.lift(&self.function)),
|
||||
Asr(op) => Lifted::Asr(op.lift(&self.function)),
|
||||
Rol(op) => Lifted::Rol(op.lift(&self.function)),
|
||||
Ror(op) => Lifted::Ror(op.lift(&self.function)),
|
||||
Mul(op) => Lifted::Mul(op.lift(&self.function)),
|
||||
MuluDp(op) => Lifted::MuluDp(op.lift(&self.function)),
|
||||
MulsDp(op) => Lifted::MulsDp(op.lift(&self.function)),
|
||||
Divu(op) => Lifted::Divu(op.lift(&self.function)),
|
||||
DivuDp(op) => Lifted::DivuDp(op.lift(&self.function)),
|
||||
Divs(op) => Lifted::Divs(op.lift(&self.function)),
|
||||
DivsDp(op) => Lifted::DivsDp(op.lift(&self.function)),
|
||||
Modu(op) => Lifted::Modu(op.lift(&self.function)),
|
||||
ModuDp(op) => Lifted::ModuDp(op.lift(&self.function)),
|
||||
Mods(op) => Lifted::Mods(op.lift(&self.function)),
|
||||
ModsDp(op) => Lifted::ModsDp(op.lift(&self.function)),
|
||||
CmpE(op) => Lifted::CmpE(op.lift(&self.function)),
|
||||
CmpNe(op) => Lifted::CmpNe(op.lift(&self.function)),
|
||||
CmpSlt(op) => Lifted::CmpSlt(op.lift(&self.function)),
|
||||
CmpUlt(op) => Lifted::CmpUlt(op.lift(&self.function)),
|
||||
CmpSle(op) => Lifted::CmpSle(op.lift(&self.function)),
|
||||
CmpUle(op) => Lifted::CmpUle(op.lift(&self.function)),
|
||||
CmpSge(op) => Lifted::CmpSge(op.lift(&self.function)),
|
||||
CmpUge(op) => Lifted::CmpUge(op.lift(&self.function)),
|
||||
CmpSgt(op) => Lifted::CmpSgt(op.lift(&self.function)),
|
||||
CmpUgt(op) => Lifted::CmpUgt(op.lift(&self.function)),
|
||||
TestBit(op) => Lifted::TestBit(op.lift(&self.function)),
|
||||
AddOverflow(op) => Lifted::AddOverflow(op.lift(&self.function)),
|
||||
FcmpE(op) => Lifted::FcmpE(op.lift(&self.function)),
|
||||
FcmpNe(op) => Lifted::FcmpNe(op.lift(&self.function)),
|
||||
FcmpLt(op) => Lifted::FcmpLt(op.lift(&self.function)),
|
||||
FcmpLe(op) => Lifted::FcmpLe(op.lift(&self.function)),
|
||||
FcmpGe(op) => Lifted::FcmpGe(op.lift(&self.function)),
|
||||
FcmpGt(op) => Lifted::FcmpGt(op.lift(&self.function)),
|
||||
FcmpO(op) => Lifted::FcmpO(op.lift(&self.function)),
|
||||
FcmpUo(op) => Lifted::FcmpUo(op.lift(&self.function)),
|
||||
Fadd(op) => Lifted::Fadd(op.lift(&self.function)),
|
||||
Fsub(op) => Lifted::Fsub(op.lift(&self.function)),
|
||||
Fmul(op) => Lifted::Fmul(op.lift(&self.function)),
|
||||
Fdiv(op) => Lifted::Fdiv(op.lift(&self.function)),
|
||||
Adc(op) => Lifted::Adc(op.lift(&self.function)),
|
||||
Sbb(op) => Lifted::Sbb(op.lift(&self.function)),
|
||||
Rlc(op) => Lifted::Rlc(op.lift(&self.function)),
|
||||
Rrc(op) => Lifted::Rrc(op.lift(&self.function)),
|
||||
Call(op) => Lifted::Call(op.lift(&self.function)),
|
||||
Tailcall(op) => Lifted::Tailcall(op.lift(&self.function)),
|
||||
Intrinsic(op) => Lifted::Intrinsic(op.lift(&self.function)),
|
||||
Syscall(op) => Lifted::Syscall(op.lift(&self.function)),
|
||||
IntrinsicSsa(op) => Lifted::IntrinsicSsa(op.lift(&self.function)),
|
||||
CallSsa(op) => Lifted::CallSsa(op.lift(&self.function)),
|
||||
TailcallSsa(op) => Lifted::TailcallSsa(op.lift(&self.function)),
|
||||
CallUntypedSsa(op) => Lifted::CallUntypedSsa(op.lift(&self.function)),
|
||||
TailcallUntypedSsa(op) => Lifted::TailcallUntypedSsa(op.lift(&self.function)),
|
||||
SyscallSsa(op) => Lifted::SyscallSsa(op.lift(&self.function)),
|
||||
SyscallUntypedSsa(op) => Lifted::SyscallUntypedSsa(op.lift(&self.function)),
|
||||
CallUntyped(op) => Lifted::CallUntyped(op.lift(&self.function)),
|
||||
TailcallUntyped(op) => Lifted::TailcallUntyped(op.lift(&self.function)),
|
||||
SyscallUntyped(op) => Lifted::SyscallUntyped(op.lift(&self.function)),
|
||||
SeparateParamList(op) => Lifted::SeparateParamList(op.lift(&self.function)),
|
||||
SharedParamSlot(op) => Lifted::SharedParamSlot(op.lift(&self.function)),
|
||||
Neg(op) => Lifted::Neg(op.lift(&self.function)),
|
||||
Not(op) => Lifted::Not(op.lift(&self.function)),
|
||||
Sx(op) => Lifted::Sx(op.lift(&self.function)),
|
||||
Zx(op) => Lifted::Zx(op.lift(&self.function)),
|
||||
LowPart(op) => Lifted::LowPart(op.lift(&self.function)),
|
||||
BoolToInt(op) => Lifted::BoolToInt(op.lift(&self.function)),
|
||||
UnimplMem(op) => Lifted::UnimplMem(op.lift(&self.function)),
|
||||
Fsqrt(op) => Lifted::Fsqrt(op.lift(&self.function)),
|
||||
Fneg(op) => Lifted::Fneg(op.lift(&self.function)),
|
||||
Fabs(op) => Lifted::Fabs(op.lift(&self.function)),
|
||||
FloatToInt(op) => Lifted::FloatToInt(op.lift(&self.function)),
|
||||
IntToFloat(op) => Lifted::IntToFloat(op.lift(&self.function)),
|
||||
FloatConv(op) => Lifted::FloatConv(op.lift(&self.function)),
|
||||
RoundToInt(op) => Lifted::RoundToInt(op.lift(&self.function)),
|
||||
Floor(op) => Lifted::Floor(op.lift(&self.function)),
|
||||
Ceil(op) => Lifted::Ceil(op.lift(&self.function)),
|
||||
Ftrunc(op) => Lifted::Ftrunc(op.lift(&self.function)),
|
||||
Load(op) => Lifted::Load(op.lift(&self.function)),
|
||||
LoadStruct(op) => Lifted::LoadStruct(op.lift(&self.function)),
|
||||
LoadStructSsa(op) => Lifted::LoadStructSsa(op.lift(&self.function)),
|
||||
LoadSsa(op) => Lifted::LoadSsa(op.lift(&self.function)),
|
||||
Ret(op) => Lifted::Ret(op.lift(&self.function)),
|
||||
Var(op) => Lifted::Var(op),
|
||||
AddressOf(op) => Lifted::AddressOf(op),
|
||||
VarField(op) => Lifted::VarField(op),
|
||||
AddressOfField(op) => Lifted::AddressOfField(op),
|
||||
VarSsa(op) => Lifted::VarSsa(op),
|
||||
VarAliased(op) => Lifted::VarAliased(op),
|
||||
VarSsaField(op) => Lifted::VarSsaField(op),
|
||||
VarAliasedField(op) => Lifted::VarAliasedField(op),
|
||||
Trap(op) => Lifted::Trap(op),
|
||||
};
|
||||
MediumLevelILLiftedInstruction {
|
||||
address: self.address,
|
||||
operation,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn operands(&self) -> Box<dyn Iterator<Item = (&'static str, MediumLevelILOperand)>> {
|
||||
use MediumLevelILOperation::*;
|
||||
match &self.operation {
|
||||
Nop(_op) | Noret(_op) | Bp(_op) | Undef(_op) | Unimpl(_op) => Box::new([].into_iter()),
|
||||
If(op) => Box::new(op.operands(&self.function)),
|
||||
FloatConst(op) => Box::new(op.operands()),
|
||||
Const(op) | ConstPtr(op) | Import(op) => Box::new(op.operands()),
|
||||
ExternPtr(op) => Box::new(op.operands(&self.function)),
|
||||
ConstData(op) => Box::new(op.operands(&self.function)),
|
||||
Jump(op) | RetHint(op) => Box::new(op.operands(&self.function)),
|
||||
StoreSsa(op) => Box::new(op.operands(&self.function)),
|
||||
StoreStructSsa(op) => Box::new(op.operands(&self.function)),
|
||||
StoreStruct(op) => Box::new(op.operands(&self.function)),
|
||||
Store(op) => Box::new(op.operands(&self.function)),
|
||||
JumpTo(op) => Box::new(op.operands(&self.function)),
|
||||
Goto(op) => Box::new(op.operands()),
|
||||
FreeVarSlot(op) => Box::new(op.operands()),
|
||||
SetVarField(op) => Box::new(op.operands(&self.function)),
|
||||
SetVar(op) => Box::new(op.operands(&self.function)),
|
||||
FreeVarSlotSsa(op) => Box::new(op.operands()),
|
||||
SetVarSsaField(op) | SetVarAliasedField(op) => Box::new(op.operands(&self.function)),
|
||||
SetVarAliased(op) => Box::new(op.operands(&self.function)),
|
||||
SetVarSsa(op) => Box::new(op.operands(&self.function)),
|
||||
VarPhi(op) => Box::new(op.operands(&self.function)),
|
||||
MemPhi(op) => Box::new(op.operands(&self.function)),
|
||||
VarSplit(op) => Box::new(op.operands()),
|
||||
SetVarSplit(op) => Box::new(op.operands(&self.function)),
|
||||
VarSplitSsa(op) => Box::new(op.operands()),
|
||||
SetVarSplitSsa(op) => Box::new(op.operands(&self.function)),
|
||||
Add(op) | Sub(op) | And(op) | Or(op) | Xor(op) | Lsl(op) | Lsr(op) | Asr(op)
|
||||
| Rol(op) | Ror(op) | Mul(op) | MuluDp(op) | MulsDp(op) | Divu(op) | DivuDp(op)
|
||||
| Divs(op) | DivsDp(op) | Modu(op) | ModuDp(op) | Mods(op) | ModsDp(op) | CmpE(op)
|
||||
| CmpNe(op) | CmpSlt(op) | CmpUlt(op) | CmpSle(op) | CmpUle(op) | CmpSge(op)
|
||||
| CmpUge(op) | CmpSgt(op) | CmpUgt(op) | TestBit(op) | AddOverflow(op) | FcmpE(op)
|
||||
| FcmpNe(op) | FcmpLt(op) | FcmpLe(op) | FcmpGe(op) | FcmpGt(op) | FcmpO(op)
|
||||
| FcmpUo(op) | Fadd(op) | Fsub(op) | Fmul(op) | Fdiv(op) => {
|
||||
Box::new(op.operands(&self.function))
|
||||
}
|
||||
Adc(op) | Sbb(op) | Rlc(op) | Rrc(op) => Box::new(op.operands(&self.function)),
|
||||
Call(op) | Tailcall(op) => Box::new(op.operands(&self.function)),
|
||||
Syscall(op) => Box::new(op.operands(&self.function)),
|
||||
Intrinsic(op) => Box::new(op.operands(&self.function)),
|
||||
IntrinsicSsa(op) => Box::new(op.operands(&self.function)),
|
||||
CallSsa(op) | TailcallSsa(op) => Box::new(op.operands(&self.function)),
|
||||
CallUntypedSsa(op) | TailcallUntypedSsa(op) => Box::new(op.operands(&self.function)),
|
||||
SyscallSsa(op) => Box::new(op.operands(&self.function)),
|
||||
SyscallUntypedSsa(op) => Box::new(op.operands(&self.function)),
|
||||
CallUntyped(op) | TailcallUntyped(op) => Box::new(op.operands(&self.function)),
|
||||
SyscallUntyped(op) => Box::new(op.operands(&self.function)),
|
||||
SeparateParamList(op) => Box::new(op.operands(&self.function)),
|
||||
SharedParamSlot(op) => Box::new(op.operands(&self.function)),
|
||||
Neg(op) | Not(op) | Sx(op) | Zx(op) | LowPart(op) | BoolToInt(op) | UnimplMem(op)
|
||||
| Fsqrt(op) | Fneg(op) | Fabs(op) | FloatToInt(op) | IntToFloat(op) | FloatConv(op)
|
||||
| RoundToInt(op) | Floor(op) | Ceil(op) | Ftrunc(op) | Load(op) => {
|
||||
Box::new(op.operands(&self.function))
|
||||
}
|
||||
LoadStruct(op) => Box::new(op.operands(&self.function)),
|
||||
LoadStructSsa(op) => Box::new(op.operands(&self.function)),
|
||||
LoadSsa(op) => Box::new(op.operands(&self.function)),
|
||||
Ret(op) => Box::new(op.operands(&self.function)),
|
||||
Var(op) | AddressOf(op) => Box::new(op.operands()),
|
||||
VarField(op) | AddressOfField(op) => Box::new(op.operands()),
|
||||
VarSsa(op) | VarAliased(op) => Box::new(op.operands()),
|
||||
VarSsaField(op) | VarAliasedField(op) => Box::new(op.operands()),
|
||||
Trap(op) => Box::new(op.operands()),
|
||||
}
|
||||
}
|
||||
}
|
||||
141
src/mlil/lift.rs
Normal file
141
src/mlil/lift.rs
Normal file
@@ -0,0 +1,141 @@
|
||||
use super::operation::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct MediumLevelILLiftedInstruction {
|
||||
pub address: u64,
|
||||
pub operation: MediumLevelILLiftedOperation,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum MediumLevelILLiftedOperation {
|
||||
Nop(NoArgs),
|
||||
Noret(NoArgs),
|
||||
Bp(NoArgs),
|
||||
Undef(NoArgs),
|
||||
Unimpl(NoArgs),
|
||||
If(LiftedIf),
|
||||
FloatConst(FloatConst),
|
||||
Const(Constant),
|
||||
ConstPtr(Constant),
|
||||
Import(Constant),
|
||||
ExternPtr(ExternPtr),
|
||||
ConstData(ConstantData),
|
||||
Jump(LiftedJump),
|
||||
RetHint(LiftedJump),
|
||||
StoreSsa(LiftedStoreSsa),
|
||||
StoreStructSsa(LiftedStoreStructSsa),
|
||||
StoreStruct(LiftedStoreStruct),
|
||||
Store(LiftedStore),
|
||||
JumpTo(LiftedJumpTo),
|
||||
Goto(Goto),
|
||||
FreeVarSlot(FreeVarSlot),
|
||||
SetVarField(LiftedSetVarField),
|
||||
SetVar(LiftedSetVar),
|
||||
FreeVarSlotSsa(FreeVarSlotSsa),
|
||||
SetVarSsaField(LiftedSetVarSsaField),
|
||||
SetVarAliasedField(LiftedSetVarSsaField),
|
||||
SetVarAliased(LiftedSetVarAliased),
|
||||
SetVarSsa(LiftedSetVarSsa),
|
||||
VarPhi(LiftedVarPhi),
|
||||
MemPhi(LiftedMemPhi),
|
||||
VarSplit(VarSplit),
|
||||
SetVarSplit(LiftedSetVarSplit),
|
||||
VarSplitSsa(VarSplitSsa),
|
||||
SetVarSplitSsa(LiftedSetVarSplitSsa),
|
||||
Add(LiftedBinaryOp),
|
||||
Sub(LiftedBinaryOp),
|
||||
And(LiftedBinaryOp),
|
||||
Or(LiftedBinaryOp),
|
||||
Xor(LiftedBinaryOp),
|
||||
Lsl(LiftedBinaryOp),
|
||||
Lsr(LiftedBinaryOp),
|
||||
Asr(LiftedBinaryOp),
|
||||
Rol(LiftedBinaryOp),
|
||||
Ror(LiftedBinaryOp),
|
||||
Mul(LiftedBinaryOp),
|
||||
MuluDp(LiftedBinaryOp),
|
||||
MulsDp(LiftedBinaryOp),
|
||||
Divu(LiftedBinaryOp),
|
||||
DivuDp(LiftedBinaryOp),
|
||||
Divs(LiftedBinaryOp),
|
||||
DivsDp(LiftedBinaryOp),
|
||||
Modu(LiftedBinaryOp),
|
||||
ModuDp(LiftedBinaryOp),
|
||||
Mods(LiftedBinaryOp),
|
||||
ModsDp(LiftedBinaryOp),
|
||||
CmpE(LiftedBinaryOp),
|
||||
CmpNe(LiftedBinaryOp),
|
||||
CmpSlt(LiftedBinaryOp),
|
||||
CmpUlt(LiftedBinaryOp),
|
||||
CmpSle(LiftedBinaryOp),
|
||||
CmpUle(LiftedBinaryOp),
|
||||
CmpSge(LiftedBinaryOp),
|
||||
CmpUge(LiftedBinaryOp),
|
||||
CmpSgt(LiftedBinaryOp),
|
||||
CmpUgt(LiftedBinaryOp),
|
||||
TestBit(LiftedBinaryOp),
|
||||
AddOverflow(LiftedBinaryOp),
|
||||
FcmpE(LiftedBinaryOp),
|
||||
FcmpNe(LiftedBinaryOp),
|
||||
FcmpLt(LiftedBinaryOp),
|
||||
FcmpLe(LiftedBinaryOp),
|
||||
FcmpGe(LiftedBinaryOp),
|
||||
FcmpGt(LiftedBinaryOp),
|
||||
FcmpO(LiftedBinaryOp),
|
||||
FcmpUo(LiftedBinaryOp),
|
||||
Fadd(LiftedBinaryOp),
|
||||
Fsub(LiftedBinaryOp),
|
||||
Fmul(LiftedBinaryOp),
|
||||
Fdiv(LiftedBinaryOp),
|
||||
Adc(LiftedBinaryOpCarry),
|
||||
Sbb(LiftedBinaryOpCarry),
|
||||
Rlc(LiftedBinaryOpCarry),
|
||||
Rrc(LiftedBinaryOpCarry),
|
||||
Call(LiftedCall),
|
||||
Tailcall(LiftedCall),
|
||||
Intrinsic(LiftedInnerCall),
|
||||
Syscall(LiftedInnerCall),
|
||||
IntrinsicSsa(LiftedIntrinsicSsa),
|
||||
CallSsa(LiftedCallSsa),
|
||||
TailcallSsa(LiftedCallSsa),
|
||||
CallUntypedSsa(LiftedCallUntypedSsa),
|
||||
TailcallUntypedSsa(LiftedCallUntypedSsa),
|
||||
SyscallSsa(LiftedSyscallSsa),
|
||||
SyscallUntypedSsa(LiftedSyscallUntypedSsa),
|
||||
CallUntyped(LiftedCallUntyped),
|
||||
TailcallUntyped(LiftedCallUntyped),
|
||||
SyscallUntyped(LiftedSyscallUntyped),
|
||||
SeparateParamList(LiftedSeparateParamList),
|
||||
SharedParamSlot(LiftedSharedParamSlot),
|
||||
Neg(LiftedUnaryOp),
|
||||
Not(LiftedUnaryOp),
|
||||
Sx(LiftedUnaryOp),
|
||||
Zx(LiftedUnaryOp),
|
||||
LowPart(LiftedUnaryOp),
|
||||
BoolToInt(LiftedUnaryOp),
|
||||
UnimplMem(LiftedUnaryOp),
|
||||
Fsqrt(LiftedUnaryOp),
|
||||
Fneg(LiftedUnaryOp),
|
||||
Fabs(LiftedUnaryOp),
|
||||
FloatToInt(LiftedUnaryOp),
|
||||
IntToFloat(LiftedUnaryOp),
|
||||
FloatConv(LiftedUnaryOp),
|
||||
RoundToInt(LiftedUnaryOp),
|
||||
Floor(LiftedUnaryOp),
|
||||
Ceil(LiftedUnaryOp),
|
||||
Ftrunc(LiftedUnaryOp),
|
||||
Load(LiftedUnaryOp),
|
||||
LoadStruct(LiftedLoadStruct),
|
||||
LoadStructSsa(LiftedLoadStructSsa),
|
||||
LoadSsa(LiftedLoadSsa),
|
||||
Ret(LiftedRet),
|
||||
Var(Var),
|
||||
AddressOf(Var),
|
||||
VarField(Field),
|
||||
AddressOfField(Field),
|
||||
VarSsa(VarSsa),
|
||||
VarAliased(VarSsa),
|
||||
VarSsaField(VarSsaField),
|
||||
VarAliasedField(VarSsaField),
|
||||
Trap(Trap),
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user