initial commit
This commit is contained in:
+2884
File diff suppressed because it is too large
Load Diff
@@ -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 {}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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 {}
|
||||
@@ -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
@@ -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 _,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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) })
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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))
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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()
|
||||
})
|
||||
}};
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
*/
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
mod block;
|
||||
mod function;
|
||||
mod instruction;
|
||||
mod lift;
|
||||
pub mod operation;
|
||||
|
||||
pub use self::block::*;
|
||||
pub use self::function::*;
|
||||
pub use self::instruction::*;
|
||||
pub use self::lift::*;
|
||||
File diff suppressed because it is too large
Load Diff
+383
@@ -0,0 +1,383 @@
|
||||
// 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 all information related to the execution environment of the binary, mainly the calling conventions used
|
||||
|
||||
use std::{borrow::Borrow, collections::HashMap, os::raw, path::Path, ptr, slice};
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::{
|
||||
architecture::{Architecture, CoreArchitecture},
|
||||
callingconvention::CallingConvention,
|
||||
rc::*,
|
||||
string::*,
|
||||
types::{QualifiedName, QualifiedNameAndType, Type},
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct Platform {
|
||||
pub(crate) handle: *mut BNPlatform,
|
||||
}
|
||||
|
||||
unsafe impl Send for Platform {}
|
||||
unsafe impl Sync for Platform {}
|
||||
|
||||
macro_rules! cc_func {
|
||||
($get_name:ident, $get_api:ident, $set_name:ident, $set_api:ident) => {
|
||||
pub fn $get_name(&self) -> Option<Ref<CallingConvention<CoreArchitecture>>> {
|
||||
let arch = self.arch();
|
||||
|
||||
unsafe {
|
||||
let cc = $get_api(self.handle);
|
||||
|
||||
if cc.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(CallingConvention::ref_from_raw(cc, arch))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn $set_name<A: Architecture>(&self, cc: &CallingConvention<A>) {
|
||||
let arch = self.arch();
|
||||
|
||||
assert!(
|
||||
cc.arch_handle.borrow().as_ref().0 == arch.0,
|
||||
"use of calling convention with non-matching Platform architecture!"
|
||||
);
|
||||
|
||||
unsafe {
|
||||
$set_api(self.handle, cc.handle);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Platform {
|
||||
pub(crate) unsafe fn ref_from_raw(handle: *mut BNPlatform) -> Ref<Self> {
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Ref::new(Self { handle })
|
||||
}
|
||||
|
||||
pub fn by_name<S: BnStrCompatible>(name: S) -> Option<Ref<Self>> {
|
||||
let raw_name = name.into_bytes_with_nul();
|
||||
unsafe {
|
||||
let res = BNGetPlatformByName(raw_name.as_ref().as_ptr() as *mut _);
|
||||
|
||||
if res.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Ref::new(Self { handle: res }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_all() -> Array<Platform> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let handles = BNGetPlatformList(&mut count);
|
||||
|
||||
Array::new(handles, count, ())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_by_arch(arch: &CoreArchitecture) -> Array<Platform> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let handles = BNGetPlatformListByArchitecture(arch.0, &mut count);
|
||||
|
||||
Array::new(handles, count, ())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_by_os<S: BnStrCompatible>(name: S) -> Array<Platform> {
|
||||
let raw_name = name.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let handles = BNGetPlatformListByOS(raw_name.as_ref().as_ptr() as *mut _, &mut count);
|
||||
|
||||
Array::new(handles, count, ())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_by_os_and_arch<S: BnStrCompatible>(
|
||||
name: S,
|
||||
arch: &CoreArchitecture,
|
||||
) -> Array<Platform> {
|
||||
let raw_name = name.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let handles = BNGetPlatformListByOSAndArchitecture(
|
||||
raw_name.as_ref().as_ptr() as *mut _,
|
||||
arch.0,
|
||||
&mut count,
|
||||
);
|
||||
|
||||
Array::new(handles, count, ())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_available_os() -> Array<BnString> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let list = BNGetPlatformOSList(&mut count);
|
||||
|
||||
Array::new(list, count, ())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new<A: Architecture, S: BnStrCompatible>(arch: &A, name: S) -> Ref<Self> {
|
||||
let name = name.into_bytes_with_nul();
|
||||
unsafe {
|
||||
let handle = BNCreatePlatform(arch.as_ref().0, name.as_ref().as_ptr() as *mut _);
|
||||
|
||||
assert!(!handle.is_null());
|
||||
|
||||
Ref::new(Self { handle })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> BnString {
|
||||
unsafe {
|
||||
let raw_name = BNGetPlatformName(self.handle);
|
||||
BnString::from_raw(raw_name)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arch(&self) -> CoreArchitecture {
|
||||
unsafe { CoreArchitecture::from_raw(BNGetPlatformArchitecture(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn register_os<S: BnStrCompatible>(&self, os: S) {
|
||||
let os = os.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
BNRegisterPlatform(os.as_ref().as_ptr() as *mut _, self.handle);
|
||||
}
|
||||
}
|
||||
|
||||
cc_func!(
|
||||
get_default_calling_convention,
|
||||
BNGetPlatformDefaultCallingConvention,
|
||||
set_default_calling_convention,
|
||||
BNRegisterPlatformDefaultCallingConvention
|
||||
);
|
||||
|
||||
cc_func!(
|
||||
get_cdecl_calling_convention,
|
||||
BNGetPlatformCdeclCallingConvention,
|
||||
set_cdecl_calling_convention,
|
||||
BNRegisterPlatformCdeclCallingConvention
|
||||
);
|
||||
|
||||
cc_func!(
|
||||
get_stdcall_calling_convention,
|
||||
BNGetPlatformStdcallCallingConvention,
|
||||
set_stdcall_calling_convention,
|
||||
BNRegisterPlatformStdcallCallingConvention
|
||||
);
|
||||
|
||||
cc_func!(
|
||||
get_fastcall_calling_convention,
|
||||
BNGetPlatformFastcallCallingConvention,
|
||||
set_fastcall_calling_convention,
|
||||
BNRegisterPlatformFastcallCallingConvention
|
||||
);
|
||||
|
||||
cc_func!(
|
||||
get_syscall_convention,
|
||||
BNGetPlatformSystemCallConvention,
|
||||
set_syscall_convention,
|
||||
BNSetPlatformSystemCallConvention
|
||||
);
|
||||
|
||||
pub fn calling_conventions(&self) -> Array<CallingConvention<CoreArchitecture>> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let handles = BNGetPlatformCallingConventions(self.handle, &mut count);
|
||||
|
||||
Array::new(handles, count, self.arch())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn types(&self) -> Array<QualifiedNameAndType> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let handles = BNGetPlatformTypes(self.handle, &mut count);
|
||||
|
||||
Array::new(handles, count, ())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn variables(&self) -> Array<QualifiedNameAndType> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let handles = BNGetPlatformVariables(self.handle, &mut count);
|
||||
|
||||
Array::new(handles, count, ())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn functions(&self) -> Array<QualifiedNameAndType> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let handles = BNGetPlatformFunctions(self.handle, &mut count);
|
||||
|
||||
Array::new(handles, count, ())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TypeParser {
|
||||
fn parse_types_from_source<S: BnStrCompatible, P: AsRef<Path>>(
|
||||
&self,
|
||||
_source: S,
|
||||
_filename: S,
|
||||
_include_directories: &[P],
|
||||
_auto_type_source: S,
|
||||
) -> Result<TypeParserResult, String> {
|
||||
Err(String::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct TypeParserResult {
|
||||
pub types: HashMap<String, Ref<Type>>,
|
||||
pub variables: HashMap<String, Ref<Type>>,
|
||||
pub functions: HashMap<String, Ref<Type>>,
|
||||
}
|
||||
|
||||
impl TypeParser for Platform {
|
||||
fn parse_types_from_source<S: BnStrCompatible, P: AsRef<Path>>(
|
||||
&self,
|
||||
source: S,
|
||||
filename: S,
|
||||
include_directories: &[P],
|
||||
auto_type_source: S,
|
||||
) -> Result<TypeParserResult, String> {
|
||||
let mut result = BNTypeParserResult {
|
||||
functionCount: 0,
|
||||
typeCount: 0,
|
||||
variableCount: 0,
|
||||
functions: ptr::null_mut(),
|
||||
types: ptr::null_mut(),
|
||||
variables: ptr::null_mut(),
|
||||
};
|
||||
|
||||
let mut type_parser_result = TypeParserResult::default();
|
||||
|
||||
let mut error_string: *mut raw::c_char = ptr::null_mut();
|
||||
|
||||
let src = source.into_bytes_with_nul();
|
||||
let filename = filename.into_bytes_with_nul();
|
||||
let auto_type_source = auto_type_source.into_bytes_with_nul();
|
||||
|
||||
let mut include_dirs = vec![];
|
||||
|
||||
for dir in include_directories.iter() {
|
||||
let d = dir
|
||||
.as_ref()
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
.into_bytes_with_nul();
|
||||
include_dirs.push(d.as_ptr() as _);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let success = BNParseTypesFromSource(
|
||||
self.handle,
|
||||
src.as_ref().as_ptr() as _,
|
||||
filename.as_ref().as_ptr() as _,
|
||||
&mut result,
|
||||
&mut error_string,
|
||||
include_dirs.as_mut_ptr(),
|
||||
include_dirs.len(),
|
||||
auto_type_source.as_ref().as_ptr() as _,
|
||||
);
|
||||
|
||||
let error_msg = BnString::from_raw(error_string);
|
||||
|
||||
if !success {
|
||||
return Err(error_msg.to_string());
|
||||
}
|
||||
|
||||
for i in slice::from_raw_parts(result.types, result.typeCount) {
|
||||
let name = QualifiedName(i.name);
|
||||
type_parser_result
|
||||
.types
|
||||
.insert(name.string(), Type::ref_from_raw(i.type_));
|
||||
}
|
||||
|
||||
for i in slice::from_raw_parts(result.functions, result.functionCount) {
|
||||
let name = QualifiedName(i.name);
|
||||
type_parser_result
|
||||
.functions
|
||||
.insert(name.string(), Type::ref_from_raw(i.type_));
|
||||
}
|
||||
|
||||
for i in slice::from_raw_parts(result.variables, result.variableCount) {
|
||||
let name = QualifiedName(i.name);
|
||||
type_parser_result
|
||||
.variables
|
||||
.insert(name.string(), Type::ref_from_raw(i.type_));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(type_parser_result)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for Platform {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for Platform {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewPlatformReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreePlatform(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for Platform {
|
||||
type Raw = *mut BNPlatform;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for Platform {
|
||||
unsafe fn free(raw: *mut *mut BNPlatform, count: usize, _context: &()) {
|
||||
BNFreePlatformList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for Platform {
|
||||
type Wrapped = Guard<'a, Platform>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a *mut BNPlatform, context: &'a ()) -> Guard<'a, Platform> {
|
||||
debug_assert!(!raw.is_null());
|
||||
Guard::new(Platform { handle: *raw }, context)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,521 @@
|
||||
// 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.
|
||||
|
||||
//! Reference counting for core Binary Ninja objects.
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
// RefCountable provides an abstraction over the various
|
||||
// core-allocated refcounted resources.
|
||||
//
|
||||
// It is important that consumers don't acquire ownership
|
||||
// of a `RefCountable` object directly -- they should only
|
||||
// ever get their hands on a `Ref<T>` or `&T`, otherwise it
|
||||
// would be possible for the allocation in the core to
|
||||
// be trivially leaked, as `T` does not have the `Drop` impl
|
||||
//
|
||||
// `T` does not have the `Drop` impl in order to allow more
|
||||
// efficient handling of core owned objects we receive pointers
|
||||
// to in callbacks
|
||||
pub unsafe trait RefCountable: ToOwned<Owned = Ref<Self>> + Sized {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self>;
|
||||
unsafe fn dec_ref(handle: &Self);
|
||||
}
|
||||
|
||||
// Represents an 'owned' reference tracked by the core
|
||||
// that we are responsible for cleaning up once we're
|
||||
// done with the encapsulated value.
|
||||
pub struct Ref<T: RefCountable> {
|
||||
contents: T,
|
||||
}
|
||||
|
||||
impl<T: RefCountable> Ref<T> {
|
||||
/// Safety: You need to make sure wherever you got the contents from incremented the ref count already. Anywhere the core passes out an object to the API does this.
|
||||
pub(crate) unsafe fn new(contents: T) -> Self {
|
||||
Self { contents }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn into_raw(obj: Self) -> T {
|
||||
let res = ptr::read(&obj.contents);
|
||||
|
||||
mem::forget(obj);
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RefCountable> AsRef<T> for Ref<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
&self.contents
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RefCountable> Deref for Ref<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.contents
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RefCountable> DerefMut for Ref<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.contents
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RefCountable> Borrow<T> for Ref<T> {
|
||||
fn borrow(&self) -> &T {
|
||||
&self.contents
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RefCountable> Drop for Ref<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
RefCountable::dec_ref(&self.contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RefCountable> Clone for Ref<T> {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { RefCountable::inc_ref(&self.contents) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RefCountable + Display> Display for Ref<T> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
self.contents.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RefCountable + Debug> Debug for Ref<T> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
self.contents.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RefCountable + PartialEq> PartialEq for Ref<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.contents.eq(&other.contents)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RefCountable + Eq> Eq for Ref<T> {}
|
||||
|
||||
impl<T: RefCountable + Hash> Hash for Ref<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.contents.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
// Guard provides access to a core-allocated resource whose
|
||||
// reference is held indirectly (e.g. a core-allocated array
|
||||
// of raw `*mut BNRawT`).
|
||||
//
|
||||
// This wrapper is necessary because `binja-rs` wrappers around
|
||||
// core objects can be bigger than the raw pointer to the core
|
||||
// object. This lets us create the full wrapper object and ensure
|
||||
// that it does not outlive the core-allocated array (or similar)
|
||||
// that our object came from.
|
||||
pub struct Guard<'a, T> {
|
||||
contents: T,
|
||||
_guard: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a, T> Guard<'a, T> {
|
||||
pub(crate) unsafe fn new<O: 'a>(contents: T, _owner: &O) -> Self {
|
||||
Self {
|
||||
contents,
|
||||
_guard: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Guard<'a, T>
|
||||
where
|
||||
T: RefCountable,
|
||||
{
|
||||
#[allow(clippy::should_implement_trait)] // This _is_ out own (lite) version of that trait
|
||||
pub fn clone(&self) -> Ref<T> {
|
||||
unsafe { <T as RefCountable>::inc_ref(&self.contents) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> AsRef<T> for Guard<'a, T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
&self.contents
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for Guard<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.contents
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for Guard<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.contents
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Borrow<T> for Guard<'a, T> {
|
||||
fn borrow(&self) -> &T {
|
||||
&self.contents
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CoreArrayProvider {
|
||||
type Raw;
|
||||
type Context;
|
||||
}
|
||||
|
||||
pub unsafe trait CoreOwnedArrayProvider: CoreArrayProvider {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, context: &Self::Context);
|
||||
}
|
||||
|
||||
pub unsafe trait CoreArrayWrapper<'a>: CoreArrayProvider
|
||||
where
|
||||
Self::Raw: 'a,
|
||||
Self::Context: 'a,
|
||||
{
|
||||
type Wrapped: 'a;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped;
|
||||
}
|
||||
|
||||
pub struct Array<P: CoreOwnedArrayProvider> {
|
||||
contents: *mut P::Raw,
|
||||
count: usize,
|
||||
context: P::Context,
|
||||
}
|
||||
|
||||
unsafe impl<P> Sync for Array<P>
|
||||
where
|
||||
P: CoreOwnedArrayProvider,
|
||||
P::Context: Sync,
|
||||
{
|
||||
}
|
||||
unsafe impl<P> Send for Array<P>
|
||||
where
|
||||
P: CoreOwnedArrayProvider,
|
||||
P::Context: Send,
|
||||
{
|
||||
}
|
||||
|
||||
impl<P: CoreOwnedArrayProvider> Array<P> {
|
||||
pub(crate) unsafe fn new(raw: *mut P::Raw, count: usize, context: P::Context) -> Self {
|
||||
Self {
|
||||
contents: raw,
|
||||
count,
|
||||
context,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.count
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.count == 0
|
||||
}
|
||||
|
||||
pub fn into_raw_parts(self) -> (*mut P::Raw, usize) {
|
||||
let me = mem::ManuallyDrop::new(self);
|
||||
(me.contents, me.count)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreOwnedArrayProvider> Array<P> {
|
||||
#[inline]
|
||||
pub fn get(&'a self, index: usize) -> P::Wrapped {
|
||||
unsafe {
|
||||
let backing = slice::from_raw_parts(self.contents, self.count);
|
||||
P::wrap_raw(&backing[index], &self.context)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&'a self) -> ArrayIter<'a, P> {
|
||||
ArrayIter {
|
||||
it: unsafe { slice::from_raw_parts(self.contents, self.count).iter() },
|
||||
context: &self.context,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreOwnedArrayProvider> IntoIterator for &'a Array<P> {
|
||||
type Item = P::Wrapped;
|
||||
type IntoIter = ArrayIter<'a, P>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: CoreOwnedArrayProvider> Drop for Array<P> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
P::free(self.contents, self.count, &self.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArrayGuard<P: CoreArrayProvider> {
|
||||
contents: *mut P::Raw,
|
||||
count: usize,
|
||||
context: P::Context,
|
||||
}
|
||||
|
||||
unsafe impl<P> Sync for ArrayGuard<P>
|
||||
where
|
||||
P: CoreArrayProvider,
|
||||
P::Context: Sync,
|
||||
{
|
||||
}
|
||||
unsafe impl<P> Send for ArrayGuard<P>
|
||||
where
|
||||
P: CoreArrayProvider,
|
||||
P::Context: Send,
|
||||
{
|
||||
}
|
||||
|
||||
impl<P: CoreArrayProvider> ArrayGuard<P> {
|
||||
pub(crate) unsafe fn new(raw: *mut P::Raw, count: usize, context: P::Context) -> Self {
|
||||
Self {
|
||||
contents: raw,
|
||||
count,
|
||||
context,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.count
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.count == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreArrayProvider> ArrayGuard<P> {
|
||||
#[inline]
|
||||
pub fn get(&'a self, index: usize) -> P::Wrapped {
|
||||
unsafe {
|
||||
let backing = slice::from_raw_parts(self.contents, self.count);
|
||||
P::wrap_raw(&backing[index], &self.context)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&'a self) -> ArrayIter<'a, P> {
|
||||
ArrayIter {
|
||||
it: unsafe { slice::from_raw_parts(self.contents, self.count).iter() },
|
||||
context: &self.context,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreArrayProvider> IntoIterator for &'a ArrayGuard<P> {
|
||||
type Item = P::Wrapped;
|
||||
type IntoIter = ArrayIter<'a, P>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
{
|
||||
it: slice::Iter<'a, P::Raw>,
|
||||
context: &'a P::Context,
|
||||
}
|
||||
|
||||
unsafe impl<'a, P> Send for ArrayIter<'a, P>
|
||||
where
|
||||
P: CoreArrayWrapper<'a>,
|
||||
P::Context: Sync,
|
||||
{
|
||||
}
|
||||
|
||||
impl<'a, P> Iterator for ArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
{
|
||||
type Item = P::Wrapped;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<P::Wrapped> {
|
||||
self.it
|
||||
.next()
|
||||
.map(|r| unsafe { P::wrap_raw(r, self.context) })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.it.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P> ExactSizeIterator for ArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
{
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.it.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P> DoubleEndedIterator for ArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
{
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<P::Wrapped> {
|
||||
self.it
|
||||
.next_back()
|
||||
.map(|r| unsafe { P::wrap_raw(r, self.context) })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rayon")]
|
||||
use rayon::prelude::*;
|
||||
|
||||
#[cfg(feature = "rayon")]
|
||||
use rayon::iter::plumbing::*;
|
||||
|
||||
#[cfg(feature = "rayon")]
|
||||
impl<'a, P> Array<P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a> + CoreOwnedArrayProvider,
|
||||
P::Context: Sync,
|
||||
P::Wrapped: Send,
|
||||
{
|
||||
pub fn par_iter(&'a self) -> ParArrayIter<'a, P> {
|
||||
ParArrayIter { it: self.iter() }
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "rayon")]
|
||||
pub struct ParArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
ArrayIter<'a, P>: Send,
|
||||
{
|
||||
it: ArrayIter<'a, P>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "rayon")]
|
||||
impl<'a, P> ParallelIterator for ParArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
P::Wrapped: Send,
|
||||
ArrayIter<'a, P>: Send,
|
||||
{
|
||||
type Item = P::Wrapped;
|
||||
|
||||
fn drive_unindexed<C>(self, consumer: C) -> C::Result
|
||||
where
|
||||
C: UnindexedConsumer<Self::Item>,
|
||||
{
|
||||
bridge(self, consumer)
|
||||
}
|
||||
|
||||
fn opt_len(&self) -> Option<usize> {
|
||||
Some(self.it.len())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rayon")]
|
||||
impl<'a, P> IndexedParallelIterator for ParArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
P::Wrapped: Send,
|
||||
ArrayIter<'a, P>: Send,
|
||||
{
|
||||
fn drive<C>(self, consumer: C) -> C::Result
|
||||
where
|
||||
C: Consumer<Self::Item>,
|
||||
{
|
||||
bridge(self, consumer)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.it.len()
|
||||
}
|
||||
|
||||
fn with_producer<CB>(self, callback: CB) -> CB::Output
|
||||
where
|
||||
CB: ProducerCallback<Self::Item>,
|
||||
{
|
||||
callback.callback(ArrayIterProducer { it: self.it })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rayon")]
|
||||
struct ArrayIterProducer<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
ArrayIter<'a, P>: Send,
|
||||
{
|
||||
it: ArrayIter<'a, P>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "rayon")]
|
||||
impl<'a, P> Producer for ArrayIterProducer<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
ArrayIter<'a, P>: Send,
|
||||
{
|
||||
type Item = P::Wrapped;
|
||||
type IntoIter = ArrayIter<'a, P>;
|
||||
|
||||
fn into_iter(self) -> ArrayIter<'a, P> {
|
||||
self.it
|
||||
}
|
||||
|
||||
fn split_at(self, index: usize) -> (Self, Self) {
|
||||
let (l, r) = self.it.it.as_slice().split_at(index);
|
||||
|
||||
(
|
||||
Self {
|
||||
it: ArrayIter {
|
||||
it: l.iter(),
|
||||
context: self.it.context,
|
||||
},
|
||||
},
|
||||
Self {
|
||||
it: ArrayIter {
|
||||
it: r.iter(),
|
||||
context: self.it.context,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
use crate::architecture::CoreArchitecture;
|
||||
use crate::function::Function;
|
||||
use crate::rc::{CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Ref};
|
||||
use binaryninjacore_sys::{BNFreeCodeReferences, BNFreeDataReferences, BNReferenceSource};
|
||||
use std::mem::ManuallyDrop;
|
||||
|
||||
/// A struct representing a single code cross-reference.
|
||||
/// Taking a cue from [`crate::linearview::LinearDisassemblyLine`], this struct uses [ManuallyDrop] to
|
||||
/// prevent destructors from being run on the [`Function`] object allocated by
|
||||
/// the core in `BNGetCodeReferences` (et al). The reference is cleaned up on [Drop] of
|
||||
/// the enclosing array object.
|
||||
#[derive(Debug)]
|
||||
pub struct CodeReference {
|
||||
arch: CoreArchitecture,
|
||||
func: ManuallyDrop<Ref<Function>>,
|
||||
pub address: u64,
|
||||
}
|
||||
|
||||
/// A struct representing a single data cross-reference.
|
||||
/// Data references have no associated metadata, so this object has only
|
||||
/// a single [`DataReference::address`] attribute.
|
||||
pub struct DataReference {
|
||||
pub address: u64,
|
||||
}
|
||||
|
||||
impl CodeReference {
|
||||
pub(crate) unsafe fn new(handle: &BNReferenceSource) -> Self {
|
||||
let func = ManuallyDrop::new(Function::from_raw(handle.func));
|
||||
let arch = CoreArchitecture::from_raw(handle.arch);
|
||||
let address = handle.addr;
|
||||
Self {
|
||||
func,
|
||||
arch,
|
||||
address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CodeReference {
|
||||
/// A handle to the referenced function bound by the [CodeReference] object's lifetime.
|
||||
/// A user can call `.to_owned()` to promote this into its own ref-counted struct
|
||||
/// and use it after the lifetime of the [CodeReference].
|
||||
pub fn function(&'a self) -> &'a Function {
|
||||
self.func.as_ref()
|
||||
}
|
||||
|
||||
/// A handle to the [CodeReference]'s [CoreArchitecture]. This type is [Copy] so reference
|
||||
/// shenanigans are not needed here.
|
||||
pub fn architecture(&self) -> CoreArchitecture {
|
||||
self.arch
|
||||
}
|
||||
}
|
||||
|
||||
// Code Reference Array<T> boilerplate
|
||||
|
||||
impl CoreArrayProvider for CodeReference {
|
||||
type Raw = BNReferenceSource;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for CodeReference {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeCodeReferences(raw, count)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for CodeReference {
|
||||
type Wrapped = CodeReference;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
CodeReference::new(raw)
|
||||
}
|
||||
}
|
||||
|
||||
// Data Reference Array<T> boilerplate
|
||||
|
||||
impl CoreArrayProvider for DataReference {
|
||||
type Raw = u64;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for DataReference {
|
||||
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
|
||||
BNFreeDataReferences(raw)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for DataReference {
|
||||
type Wrapped = DataReference;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
DataReference { address: *raw }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,570 @@
|
||||
use crate::string::BnStrCompatible;
|
||||
use crate::{
|
||||
architecture::{Architecture, CoreArchitecture},
|
||||
binaryview::BinaryView,
|
||||
llil,
|
||||
rc::{CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Ref, RefCountable},
|
||||
symbol::Symbol,
|
||||
};
|
||||
use binaryninjacore_sys::*;
|
||||
use std::borrow::Borrow;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum RelocationType {
|
||||
ELFGlobalRelocationType,
|
||||
ELFCopyRelocationType,
|
||||
ELFJumpSlotRelocationType,
|
||||
StandardRelocationType,
|
||||
IgnoredRelocation,
|
||||
UnhandledRelocation,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum RelocationOperand {
|
||||
Operand(usize),
|
||||
AutocoerceExternPtr,
|
||||
NocoerceExternPtr,
|
||||
Invalid,
|
||||
}
|
||||
|
||||
impl From<BNRelocationType> for RelocationType {
|
||||
fn from(t: BNRelocationType) -> Self {
|
||||
match t {
|
||||
BNRelocationType::ELFGlobalRelocationType => RelocationType::ELFGlobalRelocationType,
|
||||
BNRelocationType::ELFCopyRelocationType => RelocationType::ELFCopyRelocationType,
|
||||
BNRelocationType::ELFJumpSlotRelocationType => {
|
||||
RelocationType::ELFJumpSlotRelocationType
|
||||
}
|
||||
BNRelocationType::StandardRelocationType => RelocationType::StandardRelocationType,
|
||||
BNRelocationType::IgnoredRelocation => RelocationType::IgnoredRelocation,
|
||||
BNRelocationType::UnhandledRelocation => RelocationType::UnhandledRelocation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RelocationType> for BNRelocationType {
|
||||
fn from(t: RelocationType) -> Self {
|
||||
match t {
|
||||
RelocationType::ELFGlobalRelocationType => BNRelocationType::ELFGlobalRelocationType,
|
||||
RelocationType::ELFCopyRelocationType => BNRelocationType::ELFCopyRelocationType,
|
||||
RelocationType::ELFJumpSlotRelocationType => {
|
||||
BNRelocationType::ELFJumpSlotRelocationType
|
||||
}
|
||||
RelocationType::StandardRelocationType => BNRelocationType::StandardRelocationType,
|
||||
RelocationType::IgnoredRelocation => BNRelocationType::IgnoredRelocation,
|
||||
RelocationType::UnhandledRelocation => BNRelocationType::UnhandledRelocation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for RelocationOperand {
|
||||
fn from(operand: usize) -> Self {
|
||||
match operand {
|
||||
0xfffffffd => RelocationOperand::AutocoerceExternPtr,
|
||||
0xfffffffe => RelocationOperand::NocoerceExternPtr,
|
||||
0xffffffff => RelocationOperand::Invalid,
|
||||
_ => RelocationOperand::Operand(operand),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RelocationOperand> for usize {
|
||||
fn from(operand: RelocationOperand) -> Self {
|
||||
match operand {
|
||||
RelocationOperand::Operand(operand) => operand,
|
||||
RelocationOperand::AutocoerceExternPtr => 0xfffffffd,
|
||||
RelocationOperand::NocoerceExternPtr => 0xfffffffe,
|
||||
RelocationOperand::Invalid => 0xffffffff,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: How to handle related relocation linked lists?
|
||||
#[derive(Clone)]
|
||||
pub struct RelocationInfo {
|
||||
pub type_: RelocationType,
|
||||
pub pc_relative: bool,
|
||||
pub base_relative: bool,
|
||||
pub base: u64,
|
||||
pub size: usize,
|
||||
pub truncate_size: usize,
|
||||
pub native_type: u64,
|
||||
pub addend: usize,
|
||||
pub has_sign: bool,
|
||||
pub implicit_addend: bool,
|
||||
pub external: bool,
|
||||
pub symbol_index: usize,
|
||||
pub section_index: usize,
|
||||
pub address: u64,
|
||||
pub target: u64,
|
||||
pub data_relocation: bool,
|
||||
relocation_data_cache: [u8; MAX_RELOCATION_SIZE as usize],
|
||||
}
|
||||
|
||||
impl RelocationInfo {
|
||||
pub fn new() -> Self {
|
||||
RelocationInfo {
|
||||
type_: RelocationType::UnhandledRelocation,
|
||||
pc_relative: false,
|
||||
base_relative: false,
|
||||
base: 0,
|
||||
size: 0,
|
||||
truncate_size: 0,
|
||||
native_type: 0,
|
||||
addend: 0,
|
||||
has_sign: false,
|
||||
implicit_addend: false,
|
||||
external: false,
|
||||
symbol_index: 0,
|
||||
section_index: 0,
|
||||
address: 0,
|
||||
target: 0,
|
||||
data_relocation: false,
|
||||
relocation_data_cache: [0; MAX_RELOCATION_SIZE as usize],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_raw(reloc: &BNRelocationInfo) -> Self {
|
||||
RelocationInfo {
|
||||
type_: reloc.type_.into(),
|
||||
pc_relative: reloc.pcRelative,
|
||||
base_relative: reloc.baseRelative,
|
||||
base: reloc.base,
|
||||
size: reloc.size,
|
||||
truncate_size: reloc.truncateSize,
|
||||
native_type: reloc.nativeType,
|
||||
addend: reloc.addend,
|
||||
has_sign: reloc.hasSign,
|
||||
implicit_addend: reloc.implicitAddend,
|
||||
external: reloc.external,
|
||||
symbol_index: reloc.symbolIndex,
|
||||
section_index: reloc.sectionIndex,
|
||||
address: reloc.address,
|
||||
target: reloc.target,
|
||||
data_relocation: reloc.dataRelocation,
|
||||
relocation_data_cache: reloc.relocationDataCache,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_raw(&self) -> BNRelocationInfo {
|
||||
BNRelocationInfo {
|
||||
type_: self.type_.into(),
|
||||
pcRelative: self.pc_relative,
|
||||
baseRelative: self.base_relative,
|
||||
base: self.base,
|
||||
size: self.size,
|
||||
truncateSize: self.truncate_size,
|
||||
nativeType: self.native_type,
|
||||
addend: self.addend,
|
||||
hasSign: self.has_sign,
|
||||
implicitAddend: self.implicit_addend,
|
||||
external: self.external,
|
||||
symbolIndex: self.symbol_index,
|
||||
sectionIndex: self.section_index,
|
||||
address: self.address,
|
||||
target: self.target,
|
||||
dataRelocation: self.data_relocation,
|
||||
relocationDataCache: self.relocation_data_cache,
|
||||
prev: core::ptr::null_mut(),
|
||||
next: core::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Relocation(*mut BNRelocation);
|
||||
|
||||
impl Relocation {
|
||||
pub(crate) unsafe fn from_raw(reloc: *mut BNRelocation) -> Self {
|
||||
Relocation(reloc)
|
||||
}
|
||||
|
||||
pub fn info(&self) -> RelocationInfo {
|
||||
RelocationInfo::from_raw(unsafe { &BNRelocationGetInfo(self.0) })
|
||||
}
|
||||
|
||||
pub fn architecture(&self) -> Option<CoreArchitecture> {
|
||||
let raw = unsafe { BNRelocationGetArchitecture(self.0) };
|
||||
if raw.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(unsafe { CoreArchitecture::from_raw(raw) })
|
||||
}
|
||||
|
||||
pub fn target(&self) -> u64 {
|
||||
unsafe { BNRelocationGetTarget(self.0) }
|
||||
}
|
||||
|
||||
pub fn address(&self) -> u64 {
|
||||
unsafe { BNRelocationGetReloc(self.0) }
|
||||
}
|
||||
|
||||
pub fn symbol(&self) -> Option<Ref<Symbol>> {
|
||||
let raw = unsafe { BNRelocationGetSymbol(self.0) };
|
||||
if raw.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(unsafe { Symbol::ref_from_raw(raw) })
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for Relocation {
|
||||
type Raw = *mut BNRelocation;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for Relocation {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeRelocationList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for Relocation {
|
||||
type Wrapped = Relocation;
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
Relocation(*raw)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RelocationHandler: 'static + Sized + AsRef<CoreRelocationHandler> {
|
||||
type Handle: Borrow<Self>;
|
||||
|
||||
fn get_relocation_info(
|
||||
&self,
|
||||
_bv: &BinaryView,
|
||||
_arch: &CoreArchitecture,
|
||||
_info: &mut [RelocationInfo],
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn apply_relocation(
|
||||
&self,
|
||||
bv: &BinaryView,
|
||||
arch: &CoreArchitecture,
|
||||
reloc: &Relocation,
|
||||
dest: &mut [u8],
|
||||
) -> bool {
|
||||
self.default_apply_relocation(bv, arch, reloc, dest)
|
||||
}
|
||||
|
||||
fn get_operand_for_external_relocation(
|
||||
&self,
|
||||
_data: &[u8],
|
||||
_addr: u64,
|
||||
_il: &llil::RegularFunction<CoreArchitecture>,
|
||||
_reloc: &Relocation,
|
||||
) -> RelocationOperand {
|
||||
RelocationOperand::AutocoerceExternPtr
|
||||
}
|
||||
|
||||
fn handle(&self) -> Self::Handle;
|
||||
}
|
||||
|
||||
pub trait RelocationHandlerExt: RelocationHandler {
|
||||
fn default_apply_relocation(
|
||||
&self,
|
||||
bv: &BinaryView,
|
||||
arch: &CoreArchitecture,
|
||||
reloc: &Relocation,
|
||||
dest: &mut [u8],
|
||||
) -> bool {
|
||||
unsafe {
|
||||
BNRelocationHandlerDefaultApplyRelocation(
|
||||
self.as_ref().0,
|
||||
bv.handle,
|
||||
arch.handle().as_ref().0,
|
||||
reloc.0,
|
||||
dest.as_mut_ptr(),
|
||||
dest.len(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RelocationHandler> RelocationHandlerExt for T {}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Debug)]
|
||||
pub struct CoreRelocationHandler(*mut BNRelocationHandler);
|
||||
|
||||
unsafe impl Send for CoreRelocationHandler {}
|
||||
unsafe impl Sync for CoreRelocationHandler {}
|
||||
|
||||
impl CoreRelocationHandler {
|
||||
pub(crate) unsafe fn ref_from_raw(raw: *mut BNRelocationHandler) -> Ref<Self> {
|
||||
unsafe { Ref::new(CoreRelocationHandler(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<CoreRelocationHandler> for CoreRelocationHandler {
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RelocationHandler for CoreRelocationHandler {
|
||||
type Handle = Self;
|
||||
|
||||
fn get_relocation_info(
|
||||
&self,
|
||||
bv: &BinaryView,
|
||||
arch: &CoreArchitecture,
|
||||
info: &mut [RelocationInfo],
|
||||
) -> bool {
|
||||
let mut raw_info = info.iter().map(|i| i.as_raw()).collect::<Vec<_>>();
|
||||
let res = unsafe {
|
||||
BNRelocationHandlerGetRelocationInfo(
|
||||
self.0,
|
||||
bv.handle,
|
||||
arch.handle().as_ref().0,
|
||||
raw_info.as_mut_ptr(),
|
||||
raw_info.len(),
|
||||
)
|
||||
};
|
||||
for (info, raw) in info.iter_mut().zip(raw_info.iter()) {
|
||||
*info = RelocationInfo::from_raw(raw);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn apply_relocation(
|
||||
&self,
|
||||
bv: &BinaryView,
|
||||
arch: &CoreArchitecture,
|
||||
reloc: &Relocation,
|
||||
dest: &mut [u8],
|
||||
) -> bool {
|
||||
unsafe {
|
||||
BNRelocationHandlerApplyRelocation(
|
||||
self.0,
|
||||
bv.handle,
|
||||
arch.handle().as_ref().0,
|
||||
reloc.0,
|
||||
dest.as_mut_ptr(),
|
||||
dest.len(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_operand_for_external_relocation(
|
||||
&self,
|
||||
data: &[u8],
|
||||
addr: u64,
|
||||
il: &llil::RegularFunction<CoreArchitecture>,
|
||||
reloc: &Relocation,
|
||||
) -> RelocationOperand {
|
||||
unsafe {
|
||||
BNRelocationHandlerGetOperandForExternalRelocation(
|
||||
self.0,
|
||||
data.as_ptr(),
|
||||
addr,
|
||||
data.len(),
|
||||
il.handle,
|
||||
reloc.0,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
fn handle(&self) -> CoreRelocationHandler {
|
||||
CoreRelocationHandler(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for CoreRelocationHandler {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for CoreRelocationHandler {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self(BNNewRelocationHandlerReference(handle.0)))
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeRelocationHandler(handle.0);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn register_relocation_handler<S, R, F>(arch: &CoreArchitecture, name: S, func: F)
|
||||
where
|
||||
S: BnStrCompatible,
|
||||
R: 'static + RelocationHandler<Handle = CustomRelocationHandlerHandle<R>> + Send + Sync + Sized,
|
||||
F: FnOnce(CustomRelocationHandlerHandle<R>, CoreRelocationHandler) -> R,
|
||||
{
|
||||
#[repr(C)]
|
||||
struct RelocationHandlerBuilder<R>
|
||||
where
|
||||
R: 'static + RelocationHandler<Handle = CustomRelocationHandlerHandle<R>> + Send + Sync,
|
||||
{
|
||||
handler: R,
|
||||
}
|
||||
|
||||
extern "C" fn cb_free<R>(ctxt: *mut c_void)
|
||||
where
|
||||
R: 'static + RelocationHandler<Handle = CustomRelocationHandlerHandle<R>> + Send + Sync,
|
||||
{
|
||||
unsafe {
|
||||
let _handler = Box::from_raw(ctxt as *mut RelocationHandlerBuilder<R>);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn cb_get_relocation_info<R>(
|
||||
ctxt: *mut c_void,
|
||||
bv: *mut BNBinaryView,
|
||||
arch: *mut BNArchitecture,
|
||||
result: *mut BNRelocationInfo,
|
||||
count: usize,
|
||||
) -> bool
|
||||
where
|
||||
R: 'static + RelocationHandler<Handle = CustomRelocationHandlerHandle<R>> + Send + Sync,
|
||||
{
|
||||
let custom_handler = unsafe { &*(ctxt as *mut R) };
|
||||
let bv = unsafe { BinaryView::from_raw(BNNewViewReference(bv)) };
|
||||
let arch = unsafe { CoreArchitecture::from_raw(arch) };
|
||||
let result = unsafe { core::slice::from_raw_parts_mut(result, count) };
|
||||
let mut info = result
|
||||
.iter()
|
||||
.map(|i| RelocationInfo::from_raw(i))
|
||||
.collect::<Vec<_>>();
|
||||
let ok =
|
||||
custom_handler.get_relocation_info(bv.as_ref(), arch.as_ref(), info.as_mut_slice());
|
||||
for (result, info) in result.iter_mut().zip(info.iter()) {
|
||||
*result = info.as_raw();
|
||||
}
|
||||
ok
|
||||
}
|
||||
|
||||
extern "C" fn cb_apply_relocation<R>(
|
||||
ctxt: *mut c_void,
|
||||
bv: *mut BNBinaryView,
|
||||
arch: *mut BNArchitecture,
|
||||
reloc: *mut BNRelocation,
|
||||
dest: *mut u8,
|
||||
len: usize,
|
||||
) -> bool
|
||||
where
|
||||
R: 'static + RelocationHandler<Handle = CustomRelocationHandlerHandle<R>> + Send + Sync,
|
||||
{
|
||||
let custom_handler = unsafe { &*(ctxt as *mut R) };
|
||||
let bv = unsafe { BinaryView::from_raw(BNNewViewReference(bv)) };
|
||||
let arch = unsafe { CoreArchitecture::from_raw(arch) };
|
||||
let reloc = unsafe { Relocation::from_raw(reloc) };
|
||||
let dest = unsafe { core::slice::from_raw_parts_mut(dest, len) };
|
||||
custom_handler.apply_relocation(bv.as_ref(), arch.as_ref(), &reloc, dest)
|
||||
}
|
||||
|
||||
extern "C" fn cb_get_operand_for_external_relocation<R>(
|
||||
ctxt: *mut c_void,
|
||||
data: *const u8,
|
||||
addr: u64,
|
||||
len: usize,
|
||||
il: *mut BNLowLevelILFunction,
|
||||
reloc: *mut BNRelocation,
|
||||
) -> usize
|
||||
where
|
||||
R: 'static + RelocationHandler<Handle = CustomRelocationHandlerHandle<R>> + Send + Sync,
|
||||
{
|
||||
let custom_handler = unsafe { &*(ctxt as *mut R) };
|
||||
let data = unsafe { core::slice::from_raw_parts(data, len) };
|
||||
let reloc = unsafe { Relocation::from_raw(reloc) };
|
||||
|
||||
let func = unsafe { BNGetLowLevelILOwnerFunction(il) };
|
||||
if func.is_null() {
|
||||
return RelocationOperand::Invalid.into();
|
||||
}
|
||||
|
||||
let arch = unsafe { BNGetFunctionArchitecture(func) };
|
||||
unsafe { BNFreeFunction(func) };
|
||||
if arch.is_null() {
|
||||
return RelocationOperand::Invalid.into();
|
||||
}
|
||||
let arch = unsafe { CoreArchitecture::from_raw(arch) };
|
||||
|
||||
let il = unsafe { llil::RegularFunction::from_raw(arch, il) };
|
||||
|
||||
custom_handler
|
||||
.get_operand_for_external_relocation(data, addr, &il, &reloc)
|
||||
.into()
|
||||
}
|
||||
|
||||
let name = name.into_bytes_with_nul();
|
||||
|
||||
let uninit_handler = RelocationHandlerBuilder {
|
||||
handler: unsafe { std::mem::zeroed() },
|
||||
};
|
||||
let raw = Box::into_raw(Box::new(uninit_handler));
|
||||
let mut custom_handler = BNCustomRelocationHandler {
|
||||
context: raw as *mut _,
|
||||
freeObject: Some(cb_free::<R>),
|
||||
getRelocationInfo: Some(cb_get_relocation_info::<R>),
|
||||
applyRelocation: Some(cb_apply_relocation::<R>),
|
||||
getOperandForExternalRelocation: Some(cb_get_operand_for_external_relocation::<R>),
|
||||
};
|
||||
|
||||
let handle_raw = unsafe { BNCreateRelocationHandler(&mut custom_handler) };
|
||||
assert!(!handle_raw.is_null());
|
||||
let handle = CoreRelocationHandler(handle_raw);
|
||||
let custom_handle = CustomRelocationHandlerHandle {
|
||||
handle: raw as *mut R,
|
||||
};
|
||||
unsafe {
|
||||
core::ptr::write(
|
||||
&mut raw.as_mut().unwrap().handler,
|
||||
func(custom_handle, CoreRelocationHandler(handle.0)),
|
||||
);
|
||||
|
||||
BNArchitectureRegisterRelocationHandler(
|
||||
arch.handle().as_ref().0,
|
||||
name.as_ref().as_ptr() as *const _,
|
||||
handle.handle().as_ref().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CustomRelocationHandlerHandle<R>
|
||||
where
|
||||
R: 'static + RelocationHandler<Handle = CustomRelocationHandlerHandle<R>> + Send + Sync,
|
||||
{
|
||||
handle: *mut R,
|
||||
}
|
||||
|
||||
unsafe impl<R> Send for CustomRelocationHandlerHandle<R> where
|
||||
R: 'static + RelocationHandler<Handle = CustomRelocationHandlerHandle<R>> + Send + Sync
|
||||
{
|
||||
}
|
||||
|
||||
unsafe impl<R> Sync for CustomRelocationHandlerHandle<R> where
|
||||
R: 'static + RelocationHandler<Handle = CustomRelocationHandlerHandle<R>> + Send + Sync
|
||||
{
|
||||
}
|
||||
|
||||
impl<R> Clone for CustomRelocationHandlerHandle<R>
|
||||
where
|
||||
R: 'static + RelocationHandler<Handle = Self> + Send + Sync,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
handle: self.handle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> Copy for CustomRelocationHandlerHandle<R> where
|
||||
R: 'static + RelocationHandler<Handle = Self> + Send + Sync
|
||||
{
|
||||
}
|
||||
|
||||
impl<R> Borrow<R> for CustomRelocationHandlerHandle<R>
|
||||
where
|
||||
R: 'static + RelocationHandler<Handle = Self> + Send + Sync,
|
||||
{
|
||||
fn borrow(&self) -> &R {
|
||||
unsafe { &*self.handle }
|
||||
}
|
||||
}
|
||||
+308
@@ -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.
|
||||
|
||||
//! Sections are [crate::segment::Segment]s that are loaded into memory at run time
|
||||
|
||||
use std::fmt;
|
||||
use std::ops::Range;
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::binaryview::BinaryView;
|
||||
use crate::rc::*;
|
||||
use crate::string::*;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum Semantics {
|
||||
DefaultSection,
|
||||
ReadOnlyCode,
|
||||
ReadOnlyData,
|
||||
ReadWriteData,
|
||||
External,
|
||||
}
|
||||
|
||||
impl From<BNSectionSemantics> for Semantics {
|
||||
fn from(bn: BNSectionSemantics) -> Self {
|
||||
use self::BNSectionSemantics::*;
|
||||
|
||||
match bn {
|
||||
DefaultSectionSemantics => Semantics::DefaultSection,
|
||||
ReadOnlyCodeSectionSemantics => Semantics::ReadOnlyCode,
|
||||
ReadOnlyDataSectionSemantics => Semantics::ReadOnlyData,
|
||||
ReadWriteDataSectionSemantics => Semantics::ReadWriteData,
|
||||
ExternalSectionSemantics => Semantics::External,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Semantics> for BNSectionSemantics {
|
||||
fn from(semantics: Semantics) -> Self {
|
||||
use self::BNSectionSemantics::*;
|
||||
|
||||
match semantics {
|
||||
Semantics::DefaultSection => DefaultSectionSemantics,
|
||||
Semantics::ReadOnlyCode => ReadOnlyCodeSectionSemantics,
|
||||
Semantics::ReadOnlyData => ReadOnlyDataSectionSemantics,
|
||||
Semantics::ReadWriteData => ReadWriteDataSectionSemantics,
|
||||
Semantics::External => ExternalSectionSemantics,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct Section {
|
||||
handle: *mut BNSection,
|
||||
}
|
||||
|
||||
impl Section {
|
||||
pub(crate) unsafe fn from_raw(raw: *mut BNSection) -> Self {
|
||||
Self { handle: raw }
|
||||
}
|
||||
|
||||
/// You need to create a section builder, customize that section, then add it to a binary view:
|
||||
///
|
||||
/// ```
|
||||
/// bv.add_section(Section::new().align(4).entry_size(4))
|
||||
/// ```
|
||||
pub fn builder<S: BnStrCompatible>(name: S, range: Range<u64>) -> SectionBuilder<S> {
|
||||
SectionBuilder::new(name, range)
|
||||
}
|
||||
|
||||
pub fn name(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNSectionGetName(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn section_type(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNSectionGetType(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn start(&self) -> u64 {
|
||||
unsafe { BNSectionGetStart(self.handle) }
|
||||
}
|
||||
|
||||
pub fn end(&self) -> u64 {
|
||||
unsafe { BNSectionGetEnd(self.handle) }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { BNSectionGetLength(self.handle) as usize }
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
unsafe { BNSectionGetLength(self.handle) as usize == 0 }
|
||||
}
|
||||
|
||||
pub fn address_range(&self) -> Range<u64> {
|
||||
self.start()..self.end()
|
||||
}
|
||||
|
||||
pub fn semantics(&self) -> Semantics {
|
||||
unsafe { BNSectionGetSemantics(self.handle).into() }
|
||||
}
|
||||
|
||||
pub fn linked_section(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNSectionGetLinkedSection(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn info_section(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNSectionGetInfoSection(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn info_data(&self) -> u64 {
|
||||
unsafe { BNSectionGetInfoData(self.handle) }
|
||||
}
|
||||
|
||||
pub fn align(&self) -> u64 {
|
||||
unsafe { BNSectionGetAlign(self.handle) }
|
||||
}
|
||||
|
||||
pub fn entry_size(&self) -> usize {
|
||||
unsafe { BNSectionGetEntrySize(self.handle) as usize }
|
||||
}
|
||||
|
||||
pub fn auto_defined(&self) -> bool {
|
||||
unsafe { BNSectionIsAutoDefined(self.handle) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Section {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"<section '{}' @ {:x}-{:x}>",
|
||||
self.name(),
|
||||
self.start(),
|
||||
self.end()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for Section {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for Section {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewSectionReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeSection(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for Section {
|
||||
type Raw = *mut BNSection;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for Section {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeSectionList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for Section {
|
||||
type Wrapped = Guard<'a, Section>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
|
||||
Guard::new(Section::from_raw(*raw), context)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct SectionBuilder<S: BnStrCompatible> {
|
||||
is_auto: bool,
|
||||
name: S,
|
||||
range: Range<u64>,
|
||||
semantics: Semantics,
|
||||
_ty: Option<S>,
|
||||
align: u64,
|
||||
entry_size: u64,
|
||||
linked_section: Option<S>,
|
||||
info_section: Option<S>,
|
||||
info_data: u64,
|
||||
}
|
||||
|
||||
impl<S: BnStrCompatible> SectionBuilder<S> {
|
||||
pub fn new(name: S, range: Range<u64>) -> Self {
|
||||
Self {
|
||||
is_auto: false,
|
||||
name,
|
||||
range,
|
||||
semantics: Semantics::DefaultSection,
|
||||
_ty: None,
|
||||
align: 1,
|
||||
entry_size: 1,
|
||||
linked_section: None,
|
||||
info_section: None,
|
||||
info_data: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn semantics(mut self, semantics: Semantics) -> Self {
|
||||
self.semantics = semantics;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn section_type(mut self, ty: S) -> Self {
|
||||
self._ty = Some(ty);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn align(mut self, align: u64) -> Self {
|
||||
self.align = align;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn entry_size(mut self, entry_size: u64) -> Self {
|
||||
self.entry_size = entry_size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn linked_section(mut self, linked_section: S) -> Self {
|
||||
self.linked_section = Some(linked_section);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn info_section(mut self, info_section: S) -> Self {
|
||||
self.info_section = Some(info_section);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn info_data(mut self, info_data: u64) -> Self {
|
||||
self.info_data = info_data;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_auto(mut self, is_auto: bool) -> Self {
|
||||
self.is_auto = is_auto;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn create(self, view: &BinaryView) {
|
||||
let name = self.name.into_bytes_with_nul();
|
||||
let ty = self._ty.map(|s| s.into_bytes_with_nul());
|
||||
let linked_section = self.linked_section.map(|s| s.into_bytes_with_nul());
|
||||
let info_section = self.info_section.map(|s| s.into_bytes_with_nul());
|
||||
|
||||
let start = self.range.start;
|
||||
let len = self.range.end.wrapping_sub(start);
|
||||
|
||||
unsafe {
|
||||
let nul_str = std::ffi::CStr::from_bytes_with_nul_unchecked(b"\x00").as_ptr();
|
||||
let name_ptr = name.as_ref().as_ptr() as *mut _;
|
||||
let ty_ptr = ty.map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _);
|
||||
let linked_section_ptr =
|
||||
linked_section.map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _);
|
||||
let info_section_ptr = info_section.map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _);
|
||||
|
||||
if self.is_auto {
|
||||
BNAddAutoSection(
|
||||
view.handle,
|
||||
name_ptr,
|
||||
start,
|
||||
len,
|
||||
self.semantics.into(),
|
||||
ty_ptr,
|
||||
self.align,
|
||||
self.entry_size,
|
||||
linked_section_ptr,
|
||||
info_section_ptr,
|
||||
self.info_data,
|
||||
);
|
||||
} else {
|
||||
BNAddUserSection(
|
||||
view.handle,
|
||||
name_ptr,
|
||||
start,
|
||||
len,
|
||||
self.semantics.into(),
|
||||
ty_ptr,
|
||||
self.align,
|
||||
self.entry_size,
|
||||
linked_section_ptr,
|
||||
info_section_ptr,
|
||||
self.info_data,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
// 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.
|
||||
|
||||
//! Labeled segments in a binary file that aren't loaded in to memory
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::binaryview::BinaryView;
|
||||
use crate::rc::*;
|
||||
|
||||
fn set_bit(val: u32, bit_mask: u32, new_val: bool) -> u32 {
|
||||
(val & !bit_mask) | if new_val { bit_mask } else { 0 }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct SegmentBuilder {
|
||||
ea: Range<u64>,
|
||||
parent_backing: Option<Range<u64>>,
|
||||
flags: u32,
|
||||
is_auto: bool,
|
||||
}
|
||||
|
||||
impl SegmentBuilder {
|
||||
pub fn new(ea: Range<u64>) -> Self {
|
||||
SegmentBuilder {
|
||||
ea,
|
||||
parent_backing: None,
|
||||
flags: 0,
|
||||
is_auto: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parent_backing(mut self, parent_backing: Range<u64>) -> Self {
|
||||
self.parent_backing = Some(parent_backing);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn executable(mut self, executable: bool) -> Self {
|
||||
self.flags = set_bit(self.flags, 0x01, executable);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn writable(mut self, writable: bool) -> Self {
|
||||
self.flags = set_bit(self.flags, 0x02, writable);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn readable(mut self, readable: bool) -> Self {
|
||||
self.flags = set_bit(self.flags, 0x04, readable);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn contains_data(mut self, contains_data: bool) -> Self {
|
||||
self.flags = set_bit(self.flags, 0x08, contains_data);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn contains_code(mut self, contains_code: bool) -> Self {
|
||||
self.flags = set_bit(self.flags, 0x10, contains_code);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn deny_write(mut self, deny_write: bool) -> Self {
|
||||
self.flags = set_bit(self.flags, 0x20, deny_write);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn deny_execute(mut self, deny_execute: bool) -> Self {
|
||||
self.flags = set_bit(self.flags, 0x40, deny_execute);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_auto(mut self, is_auto: bool) -> Self {
|
||||
self.is_auto = is_auto;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn create(self, view: &BinaryView) {
|
||||
let ea_start = self.ea.start;
|
||||
let ea_len = self.ea.end.wrapping_sub(ea_start);
|
||||
let (b_start, b_len) = self
|
||||
.parent_backing
|
||||
.map_or((0, 0), |s| (s.start, s.end.wrapping_sub(s.start)));
|
||||
|
||||
unsafe {
|
||||
if self.is_auto {
|
||||
BNAddAutoSegment(view.handle, ea_start, ea_len, b_start, b_len, self.flags);
|
||||
} else {
|
||||
BNAddUserSegment(view.handle, ea_start, ea_len, b_start, b_len, self.flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct Segment {
|
||||
handle: *mut BNSegment,
|
||||
}
|
||||
|
||||
impl Segment {
|
||||
pub(crate) unsafe fn from_raw(raw: *mut BNSegment) -> Self {
|
||||
Self { handle: raw }
|
||||
}
|
||||
|
||||
/// You need to create a segment builder, customize that segment, then add it to a binary view:
|
||||
///
|
||||
/// ```
|
||||
/// bv.add_segment(Segment::new().align(4).entry_size(4))
|
||||
/// ```
|
||||
pub fn builder(ea_range: Range<u64>) -> SegmentBuilder {
|
||||
SegmentBuilder::new(ea_range)
|
||||
}
|
||||
|
||||
pub fn address_range(&self) -> Range<u64> {
|
||||
let start = unsafe { BNSegmentGetStart(self.handle) };
|
||||
let end = unsafe { BNSegmentGetEnd(self.handle) };
|
||||
start..end
|
||||
}
|
||||
|
||||
pub fn parent_backing(&self) -> Option<Range<u64>> {
|
||||
let start = unsafe { BNSegmentGetDataOffset(self.handle) };
|
||||
let end = unsafe { BNSegmentGetDataEnd(self.handle) };
|
||||
|
||||
if start != end {
|
||||
Some(start..end)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn flags(&self) -> u32 {
|
||||
unsafe { BNSegmentGetFlags(self.handle) }
|
||||
}
|
||||
|
||||
pub fn executable(&self) -> bool {
|
||||
self.flags() & 0x01 != 0
|
||||
}
|
||||
|
||||
pub fn writable(&self) -> bool {
|
||||
self.flags() & 0x02 != 0
|
||||
}
|
||||
|
||||
pub fn readable(&self) -> bool {
|
||||
self.flags() & 0x04 != 0
|
||||
}
|
||||
|
||||
pub fn contains_data(&self) -> bool {
|
||||
self.flags() & 0x08 != 0
|
||||
}
|
||||
|
||||
pub fn contains_code(&self) -> bool {
|
||||
self.flags() & 0x10 != 0
|
||||
}
|
||||
|
||||
pub fn deny_write(&self) -> bool {
|
||||
self.flags() & 0x20 != 0
|
||||
}
|
||||
|
||||
pub fn deny_execute(&self) -> bool {
|
||||
self.flags() & 0x40 != 0
|
||||
}
|
||||
|
||||
pub fn auto_defined(&self) -> bool {
|
||||
unsafe { BNSegmentIsAutoDefined(self.handle) }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for Segment {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for Segment {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewSegmentReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeSegment(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for Segment {
|
||||
type Raw = *mut BNSegment;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for Segment {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeSegmentList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for Segment {
|
||||
type Wrapped = Guard<'a, Segment>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
|
||||
Guard::new(Segment::from_raw(*raw), context)
|
||||
}
|
||||
}
|
||||
+469
@@ -0,0 +1,469 @@
|
||||
// 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 reading, writing, and creating new settings
|
||||
|
||||
pub use binaryninjacore_sys::BNSettingsScope as SettingsScope;
|
||||
use binaryninjacore_sys::*;
|
||||
use std::iter::FromIterator;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use crate::binaryview::BinaryView;
|
||||
use crate::rc::*;
|
||||
use crate::string::{BnStrCompatible, BnString};
|
||||
|
||||
use std::ptr;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct Settings {
|
||||
pub(crate) handle: *mut BNSettings,
|
||||
}
|
||||
|
||||
unsafe impl Send for Settings {}
|
||||
unsafe impl Sync for Settings {}
|
||||
|
||||
impl Settings {
|
||||
pub(crate) unsafe fn from_raw(handle: *mut BNSettings) -> Ref<Self> {
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Ref::new(Self { handle })
|
||||
}
|
||||
|
||||
pub fn new<S: BnStrCompatible>(instance_id: S) -> Ref<Self> {
|
||||
let instance_id = instance_id.into_bytes_with_nul();
|
||||
unsafe {
|
||||
let handle = BNCreateSettings(instance_id.as_ref().as_ptr() as *mut _);
|
||||
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Ref::new(Self { handle })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_resource_id<S: BnStrCompatible>(&self, resource_id: S) {
|
||||
let resource_id = resource_id.into_bytes_with_nul();
|
||||
unsafe { BNSettingsSetResourceId(self.handle, resource_id.as_ref().as_ptr() as *mut _) };
|
||||
}
|
||||
|
||||
pub fn serialize_schema(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNSettingsSerializeSchema(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn deserialize_schema<S: BnStrCompatible>(&self, schema: S) -> bool {
|
||||
let schema = schema.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNSettingsDeserializeSchema(
|
||||
self.handle,
|
||||
schema.as_ref().as_ptr() as *mut _,
|
||||
BNSettingsScope::SettingsAutoScope,
|
||||
true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains<S: BnStrCompatible>(&self, key: S) -> bool {
|
||||
let key = key.into_bytes_with_nul();
|
||||
|
||||
unsafe { BNSettingsContains(self.handle, key.as_ref().as_ptr() as *mut _) }
|
||||
}
|
||||
|
||||
pub fn get_bool<S: BnStrCompatible>(
|
||||
&self,
|
||||
key: S,
|
||||
view: Option<&BinaryView>,
|
||||
scope: Option<Box<SettingsScope>>,
|
||||
) -> bool {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let view_handle = match view {
|
||||
Some(view) => view.handle,
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
let scope_ptr = match scope {
|
||||
Some(mut scope) => scope.as_mut(),
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
unsafe {
|
||||
BNSettingsGetBool(
|
||||
self.handle,
|
||||
key.as_ref().as_ptr() as *mut _,
|
||||
view_handle,
|
||||
scope_ptr,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_double<S: BnStrCompatible>(
|
||||
&self,
|
||||
key: S,
|
||||
view: Option<&BinaryView>,
|
||||
scope: Option<Box<SettingsScope>>,
|
||||
) -> f64 {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let view_handle = match view {
|
||||
Some(view) => view.handle,
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
let scope_ptr = match scope {
|
||||
Some(mut scope) => scope.as_mut(),
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
unsafe {
|
||||
BNSettingsGetDouble(
|
||||
self.handle,
|
||||
key.as_ref().as_ptr() as *mut _,
|
||||
view_handle,
|
||||
scope_ptr,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_integer<S: BnStrCompatible>(
|
||||
&self,
|
||||
key: S,
|
||||
view: Option<&BinaryView>,
|
||||
scope: Option<Box<SettingsScope>>,
|
||||
) -> u64 {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let view_handle = match view {
|
||||
Some(view) => view.handle,
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
let scope_ptr = match scope {
|
||||
Some(mut scope) => scope.as_mut(),
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
unsafe {
|
||||
BNSettingsGetUInt64(
|
||||
self.handle,
|
||||
key.as_ref().as_ptr() as *mut _,
|
||||
view_handle,
|
||||
scope_ptr,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_string<S: BnStrCompatible>(
|
||||
&self,
|
||||
key: S,
|
||||
view: Option<&BinaryView>,
|
||||
scope: Option<Box<SettingsScope>>,
|
||||
) -> BnString {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let view_handle = match view {
|
||||
Some(view) => view.handle,
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
let scope_ptr = match scope {
|
||||
Some(mut scope) => scope.as_mut(),
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
unsafe {
|
||||
BnString::from_raw(BNSettingsGetString(
|
||||
self.handle,
|
||||
key.as_ref().as_ptr() as *mut _,
|
||||
view_handle,
|
||||
scope_ptr,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_string_list<S: BnStrCompatible>(
|
||||
&self,
|
||||
key: S,
|
||||
view: Option<&BinaryView>,
|
||||
scope: Option<Box<SettingsScope>>,
|
||||
) -> Array<BnString> {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let view_handle = match view {
|
||||
Some(view) => view.handle,
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
let scope_ptr = match scope {
|
||||
Some(mut scope) => scope.as_mut(),
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
let mut size: usize = 0;
|
||||
unsafe {
|
||||
Array::new(
|
||||
BNSettingsGetStringList(
|
||||
self.handle,
|
||||
key.as_ref().as_ptr() as *mut _,
|
||||
view_handle,
|
||||
scope_ptr,
|
||||
&mut size,
|
||||
) as *mut *mut c_char,
|
||||
size,
|
||||
(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_json<S: BnStrCompatible>(
|
||||
&self,
|
||||
key: S,
|
||||
view: Option<&BinaryView>,
|
||||
scope: Option<Box<SettingsScope>>,
|
||||
) -> BnString {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let view_handle = match view {
|
||||
Some(view) => view.handle,
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
let scope_ptr = match scope {
|
||||
Some(mut scope) => scope.as_mut(),
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
unsafe {
|
||||
BnString::from_raw(BNSettingsGetJson(
|
||||
self.handle,
|
||||
key.as_ref().as_ptr() as *mut _,
|
||||
view_handle,
|
||||
scope_ptr,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_bool<S: BnStrCompatible>(
|
||||
&self,
|
||||
key: S,
|
||||
value: bool,
|
||||
view: Option<&BinaryView>,
|
||||
scope: Option<SettingsScope>,
|
||||
) {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let view_handle = match view {
|
||||
Some(view) => view.handle,
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
let scope = match scope {
|
||||
Some(scope) => scope,
|
||||
_ => SettingsScope::SettingsAutoScope,
|
||||
};
|
||||
unsafe {
|
||||
BNSettingsSetBool(
|
||||
self.handle,
|
||||
view_handle,
|
||||
scope,
|
||||
key.as_ref().as_ptr() as *mut _,
|
||||
value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_double<S: BnStrCompatible>(
|
||||
&self,
|
||||
key: S,
|
||||
value: f64,
|
||||
view: Option<&BinaryView>,
|
||||
scope: Option<SettingsScope>,
|
||||
) {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let view_handle = match view {
|
||||
Some(view) => view.handle,
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
let scope = match scope {
|
||||
Some(scope) => scope,
|
||||
_ => SettingsScope::SettingsAutoScope,
|
||||
};
|
||||
unsafe {
|
||||
BNSettingsSetDouble(
|
||||
self.handle,
|
||||
view_handle,
|
||||
scope,
|
||||
key.as_ref().as_ptr() as *mut _,
|
||||
value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_integer<S: BnStrCompatible>(
|
||||
&self,
|
||||
key: S,
|
||||
value: u64,
|
||||
view: Option<&BinaryView>,
|
||||
scope: Option<SettingsScope>,
|
||||
) {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let view_handle = match view {
|
||||
Some(view) => view.handle,
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
let scope = match scope {
|
||||
Some(scope) => scope,
|
||||
_ => SettingsScope::SettingsAutoScope,
|
||||
};
|
||||
unsafe {
|
||||
BNSettingsSetUInt64(
|
||||
self.handle,
|
||||
view_handle,
|
||||
scope,
|
||||
key.as_ref().as_ptr() as *mut _,
|
||||
value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_string<S1: BnStrCompatible, S2: BnStrCompatible>(
|
||||
&self,
|
||||
key: S1,
|
||||
value: S2,
|
||||
view: Option<&BinaryView>,
|
||||
scope: Option<SettingsScope>,
|
||||
) {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let value = value.into_bytes_with_nul();
|
||||
let view_handle = match view {
|
||||
Some(view) => view.handle,
|
||||
_ => ptr::null_mut() as *mut _,
|
||||
};
|
||||
let scope = match scope {
|
||||
Some(scope) => scope,
|
||||
_ => SettingsScope::SettingsAutoScope,
|
||||
};
|
||||
unsafe {
|
||||
BNSettingsSetString(
|
||||
self.handle,
|
||||
view_handle,
|
||||
scope,
|
||||
key.as_ref().as_ptr() as *mut _,
|
||||
value.as_ref().as_ptr() as *mut _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_string_list<S1: BnStrCompatible, S2: BnStrCompatible, I: Iterator<Item = S2>>(
|
||||
&self,
|
||||
key: S1,
|
||||
value: I,
|
||||
view: Option<&BinaryView>,
|
||||
scope: Option<SettingsScope>,
|
||||
) -> bool {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let v = Vec::from_iter(value);
|
||||
|
||||
let mut value = vec![];
|
||||
for item in v {
|
||||
value.push(item.into_bytes_with_nul().as_ref().as_ptr() as *const c_char);
|
||||
}
|
||||
|
||||
let view_handle = match view {
|
||||
Some(view) => view.handle,
|
||||
None => ptr::null_mut() as *mut _,
|
||||
};
|
||||
|
||||
let scope = match scope {
|
||||
Some(scope) => scope,
|
||||
None => SettingsScope::SettingsAutoScope,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
BNSettingsSetStringList(
|
||||
self.handle,
|
||||
view_handle,
|
||||
scope,
|
||||
key.as_ref().as_ptr() as *mut _,
|
||||
value.as_mut_ptr(),
|
||||
value.len(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_json<S1: BnStrCompatible, S2: BnStrCompatible>(
|
||||
&self,
|
||||
key: S1,
|
||||
value: S2,
|
||||
view: Option<&BinaryView>,
|
||||
scope: Option<SettingsScope>,
|
||||
) -> bool {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let value = value.into_bytes_with_nul();
|
||||
|
||||
let view_handle = match view {
|
||||
Some(view) => view.handle,
|
||||
None => ptr::null_mut() as *mut _,
|
||||
};
|
||||
|
||||
let scope = match scope {
|
||||
Some(scope) => scope,
|
||||
None => SettingsScope::SettingsAutoScope,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
BNSettingsSetJson(
|
||||
self.handle,
|
||||
view_handle,
|
||||
scope,
|
||||
key.as_ref().as_ptr() as *mut _,
|
||||
value.as_ref().as_ptr() as *mut _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_group<S1: BnStrCompatible, S2: BnStrCompatible>(
|
||||
&self,
|
||||
group: S1,
|
||||
title: S2,
|
||||
) -> bool {
|
||||
let group = group.into_bytes_with_nul();
|
||||
let title = title.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
BNSettingsRegisterGroup(
|
||||
self.handle,
|
||||
group.as_ref().as_ptr() as *mut _,
|
||||
title.as_ref().as_ptr() as *mut _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_setting_json<S1: BnStrCompatible, S2: BnStrCompatible>(
|
||||
&self,
|
||||
group: S1,
|
||||
properties: S2,
|
||||
) -> bool {
|
||||
let group = group.into_bytes_with_nul();
|
||||
let properties = properties.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
BNSettingsRegisterSetting(
|
||||
self.handle,
|
||||
group.as_ref().as_ptr() as *mut _,
|
||||
properties.as_ref().as_ptr() as *mut _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: register_setting but type-safely turn it into json
|
||||
}
|
||||
|
||||
impl ToOwned for Settings {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for Settings {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewSettingsReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeSettings(handle.handle);
|
||||
}
|
||||
}
|
||||
+296
@@ -0,0 +1,296 @@
|
||||
// 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.
|
||||
|
||||
//! String wrappers for core-owned strings and strings being passed to the core
|
||||
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::os::raw;
|
||||
|
||||
use crate::rc::*;
|
||||
use crate::types::QualifiedName;
|
||||
|
||||
pub(crate) fn raw_to_string(ptr: *const raw::c_char) -> Option<String> {
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() })
|
||||
}
|
||||
}
|
||||
|
||||
/// These are strings that the core will both allocate and free.
|
||||
/// We just have a reference to these strings and want to be able use them, but aren't responsible for cleanup
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct BnStr {
|
||||
raw: [u8],
|
||||
}
|
||||
|
||||
impl BnStr {
|
||||
pub(crate) unsafe fn from_raw<'a>(ptr: *const raw::c_char) -> &'a Self {
|
||||
mem::transmute(CStr::from_ptr(ptr).to_bytes_with_nul())
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.as_cstr().to_str().unwrap()
|
||||
}
|
||||
|
||||
pub fn as_cstr(&self) -> &CStr {
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(&self.raw) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for BnStr {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for BnStr {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.raw
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for BnStr {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for BnStr {
|
||||
fn borrow(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BnStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.as_cstr().to_string_lossy())
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct BnString {
|
||||
raw: *mut raw::c_char,
|
||||
}
|
||||
|
||||
/// A nul-terminated C string allocated by the core.
|
||||
///
|
||||
/// Received from a variety of core function calls, and
|
||||
/// must be used when giving strings to the core from many
|
||||
/// core-invoked callbacks.
|
||||
///
|
||||
/// These are strings we're responsible for freeing, such as
|
||||
/// strings allocated by the core and given to us through the API
|
||||
/// and then forgotten about by the core.
|
||||
impl BnString {
|
||||
pub fn new<S: BnStrCompatible>(s: S) -> Self {
|
||||
use binaryninjacore_sys::BNAllocString;
|
||||
|
||||
let raw = s.into_bytes_with_nul();
|
||||
|
||||
unsafe {
|
||||
let ptr = raw.as_ref().as_ptr() as *mut _;
|
||||
|
||||
Self {
|
||||
raw: BNAllocString(ptr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a BnString from an owned const char* allocated by BNAllocString
|
||||
pub(crate) unsafe fn from_raw(raw: *mut raw::c_char) -> Self {
|
||||
Self { raw }
|
||||
}
|
||||
|
||||
pub(crate) fn into_raw(self) -> *mut raw::c_char {
|
||||
let res = self.raw;
|
||||
|
||||
// we're surrendering ownership over the *mut c_char to
|
||||
// the core, so ensure we don't free it
|
||||
mem::forget(self);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
unsafe { BnStr::from_raw(self.raw).as_str() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BnString {
|
||||
fn drop(&mut self) {
|
||||
use binaryninjacore_sys::BNFreeString;
|
||||
|
||||
unsafe {
|
||||
BNFreeString(self.raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for BnString {
|
||||
fn clone(&self) -> Self {
|
||||
use binaryninjacore_sys::BNAllocString;
|
||||
unsafe {
|
||||
Self {
|
||||
raw: BNAllocString(self.raw),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for BnString {
|
||||
type Target = BnStr;
|
||||
|
||||
fn deref(&self) -> &BnStr {
|
||||
unsafe { BnStr::from_raw(self.raw) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for BnString {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_cstr().to_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for BnString {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.raw.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BnString {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.deref() == other.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for BnString {}
|
||||
|
||||
impl fmt::Display for BnString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.as_cstr().to_string_lossy())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BnString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.as_cstr().to_string_lossy())
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for BnString {
|
||||
type Raw = *mut raw::c_char;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for BnString {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
use binaryninjacore_sys::BNFreeStringList;
|
||||
BNFreeStringList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for BnString {
|
||||
type Wrapped = &'a BnStr;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
BnStr::from_raw(*raw)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe trait BnStrCompatible {
|
||||
type Result: AsRef<[u8]>;
|
||||
fn into_bytes_with_nul(self) -> Self::Result;
|
||||
}
|
||||
|
||||
unsafe impl<'a> BnStrCompatible for &'a BnStr {
|
||||
type Result = &'a [u8];
|
||||
|
||||
fn into_bytes_with_nul(self) -> Self::Result {
|
||||
self.as_cstr().to_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl BnStrCompatible for BnString {
|
||||
type Result = Self;
|
||||
|
||||
fn into_bytes_with_nul(self) -> Self::Result {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> BnStrCompatible for &'a CStr {
|
||||
type Result = &'a [u8];
|
||||
|
||||
fn into_bytes_with_nul(self) -> Self::Result {
|
||||
self.to_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl BnStrCompatible for CString {
|
||||
type Result = Vec<u8>;
|
||||
|
||||
fn into_bytes_with_nul(self) -> Self::Result {
|
||||
self.into_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> BnStrCompatible for &'a str {
|
||||
type Result = Vec<u8>;
|
||||
|
||||
fn into_bytes_with_nul(self) -> Self::Result {
|
||||
let ret = CString::new(self).expect("can't pass strings with internal nul bytes to core!");
|
||||
ret.into_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl BnStrCompatible for String {
|
||||
type Result = Vec<u8>;
|
||||
|
||||
fn into_bytes_with_nul(self) -> Self::Result {
|
||||
self.as_str().into_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> BnStrCompatible for &'a String {
|
||||
type Result = Vec<u8>;
|
||||
|
||||
fn into_bytes_with_nul(self) -> Self::Result {
|
||||
self.as_str().into_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> BnStrCompatible for &'a Cow<'a, str> {
|
||||
type Result = Vec<u8>;
|
||||
|
||||
fn into_bytes_with_nul(self) -> Self::Result {
|
||||
self.to_string().into_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl BnStrCompatible for &QualifiedName {
|
||||
type Result = Vec<u8>;
|
||||
|
||||
fn into_bytes_with_nul(self) -> Self::Result {
|
||||
self.string().into_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
// 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 the various kinds of symbols in a binary.
|
||||
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ptr;
|
||||
|
||||
use crate::rc::*;
|
||||
use crate::string::*;
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
// TODO : Rename
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum SymbolType {
|
||||
Function,
|
||||
LibraryFunction,
|
||||
ImportAddress,
|
||||
ImportedFunction,
|
||||
Data,
|
||||
ImportedData,
|
||||
External,
|
||||
Symbolic,
|
||||
LocalLabel,
|
||||
}
|
||||
|
||||
impl From<BNSymbolType> for SymbolType {
|
||||
fn from(bn: BNSymbolType) -> SymbolType {
|
||||
use self::BNSymbolType::*;
|
||||
|
||||
match bn {
|
||||
FunctionSymbol => SymbolType::Function,
|
||||
LibraryFunctionSymbol => SymbolType::LibraryFunction,
|
||||
ImportAddressSymbol => SymbolType::ImportAddress,
|
||||
ImportedFunctionSymbol => SymbolType::ImportedFunction,
|
||||
DataSymbol => SymbolType::Data,
|
||||
ImportedDataSymbol => SymbolType::ImportedData,
|
||||
ExternalSymbol => SymbolType::External,
|
||||
SymbolicFunctionSymbol => SymbolType::Symbolic,
|
||||
LocalLabelSymbol => SymbolType::LocalLabel,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SymbolType> for BNSymbolType {
|
||||
fn from(symbol_type: SymbolType) -> Self {
|
||||
use self::BNSymbolType::*;
|
||||
|
||||
match symbol_type {
|
||||
SymbolType::Function => FunctionSymbol,
|
||||
SymbolType::LibraryFunction => LibraryFunctionSymbol,
|
||||
SymbolType::ImportAddress => ImportAddressSymbol,
|
||||
SymbolType::ImportedFunction => ImportedFunctionSymbol,
|
||||
SymbolType::Data => DataSymbol,
|
||||
SymbolType::ImportedData => ImportedDataSymbol,
|
||||
SymbolType::External => ExternalSymbol,
|
||||
SymbolType::Symbolic => SymbolicFunctionSymbol,
|
||||
SymbolType::LocalLabel => LocalLabelSymbol,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum Binding {
|
||||
None,
|
||||
Local,
|
||||
Global,
|
||||
Weak,
|
||||
}
|
||||
|
||||
impl From<BNSymbolBinding> for Binding {
|
||||
fn from(bn: BNSymbolBinding) -> Binding {
|
||||
use self::BNSymbolBinding::*;
|
||||
|
||||
match bn {
|
||||
NoBinding => Binding::None,
|
||||
LocalBinding => Binding::Local,
|
||||
GlobalBinding => Binding::Global,
|
||||
WeakBinding => Binding::Weak,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Binding> for BNSymbolBinding {
|
||||
fn from(binding: Binding) -> Self {
|
||||
use self::BNSymbolBinding::*;
|
||||
|
||||
match binding {
|
||||
Binding::None => NoBinding,
|
||||
Binding::Local => LocalBinding,
|
||||
Binding::Global => GlobalBinding,
|
||||
Binding::Weak => WeakBinding,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : Clean this up
|
||||
#[must_use]
|
||||
pub struct SymbolBuilder {
|
||||
ty: SymbolType,
|
||||
binding: Binding,
|
||||
addr: u64,
|
||||
raw_name: String,
|
||||
short_name: Option<String>,
|
||||
full_name: Option<String>,
|
||||
ordinal: u64,
|
||||
}
|
||||
|
||||
impl SymbolBuilder {
|
||||
pub fn new(ty: SymbolType, raw_name: &str, addr: u64) -> Self {
|
||||
Self {
|
||||
ty,
|
||||
binding: Binding::None,
|
||||
addr,
|
||||
raw_name: raw_name.to_owned(),
|
||||
short_name: None,
|
||||
full_name: None,
|
||||
ordinal: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn binding(mut self, binding: Binding) -> Self {
|
||||
self.binding = binding;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn short_name(mut self, short_name: &str) -> Self {
|
||||
self.short_name = Some(short_name.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn full_name(mut self, full_name: &str) -> Self {
|
||||
self.full_name = Some(full_name.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ordinal(mut self, ordinal: u64) -> Self {
|
||||
self.ordinal = ordinal;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn create(self) -> Ref<Symbol> {
|
||||
let raw_name = self.raw_name.into_bytes_with_nul();
|
||||
let short_name = self.short_name.map(|s| s.into_bytes_with_nul());
|
||||
let full_name = self.full_name.map(|s| s.into_bytes_with_nul());
|
||||
|
||||
// Lifetimes, man
|
||||
let raw_name = raw_name.as_ptr() as _;
|
||||
unsafe {
|
||||
if let Some(short_name) = short_name {
|
||||
if let Some(full_name) = full_name {
|
||||
let res = BNCreateSymbol(
|
||||
self.ty.into(),
|
||||
short_name.as_ptr() as _,
|
||||
full_name.as_ptr() as _,
|
||||
raw_name,
|
||||
self.addr,
|
||||
self.binding.into(),
|
||||
ptr::null_mut(),
|
||||
self.ordinal,
|
||||
);
|
||||
Symbol::ref_from_raw(res)
|
||||
} else {
|
||||
let res = BNCreateSymbol(
|
||||
self.ty.into(),
|
||||
short_name.as_ptr() as _,
|
||||
raw_name,
|
||||
raw_name,
|
||||
self.addr,
|
||||
self.binding.into(),
|
||||
ptr::null_mut(),
|
||||
self.ordinal,
|
||||
);
|
||||
Symbol::ref_from_raw(res)
|
||||
}
|
||||
} else if let Some(full_name) = full_name {
|
||||
let res = BNCreateSymbol(
|
||||
self.ty.into(),
|
||||
raw_name,
|
||||
full_name.as_ptr() as _,
|
||||
raw_name,
|
||||
self.addr,
|
||||
self.binding.into(),
|
||||
ptr::null_mut(),
|
||||
self.ordinal,
|
||||
);
|
||||
Symbol::ref_from_raw(res)
|
||||
} else {
|
||||
let res = BNCreateSymbol(
|
||||
self.ty.into(),
|
||||
raw_name,
|
||||
raw_name,
|
||||
raw_name,
|
||||
self.addr,
|
||||
self.binding.into(),
|
||||
ptr::null_mut(),
|
||||
self.ordinal,
|
||||
);
|
||||
Symbol::ref_from_raw(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq)]
|
||||
pub struct Symbol {
|
||||
pub(crate) handle: *mut BNSymbol,
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
pub(crate) unsafe fn ref_from_raw(raw: *mut BNSymbol) -> Ref<Self> {
|
||||
Ref::new(Self { handle: raw })
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn from_raw(raw: *mut BNSymbol) -> Self {
|
||||
Self { handle: raw }
|
||||
}
|
||||
|
||||
/// To create a new symbol, you need to create a symbol builder, customize that symbol, then add `SymbolBuilder::create` it into a `Ref<Symbol>`:
|
||||
///
|
||||
/// ```
|
||||
/// Symbol::new().short_name("hello").full_name("hello").create();
|
||||
/// ```
|
||||
pub fn builder(ty: SymbolType, raw_name: &str, addr: u64) -> SymbolBuilder {
|
||||
SymbolBuilder::new(ty, raw_name, addr)
|
||||
}
|
||||
|
||||
pub fn sym_type(&self) -> SymbolType {
|
||||
unsafe { BNGetSymbolType(self.handle).into() }
|
||||
}
|
||||
|
||||
pub fn binding(&self) -> Binding {
|
||||
unsafe { BNGetSymbolBinding(self.handle).into() }
|
||||
}
|
||||
|
||||
pub fn full_name(&self) -> BnString {
|
||||
unsafe {
|
||||
let name = BNGetSymbolFullName(self.handle);
|
||||
BnString::from_raw(name)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn short_name(&self) -> BnString {
|
||||
unsafe {
|
||||
let name = BNGetSymbolShortName(self.handle);
|
||||
BnString::from_raw(name)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw_name(&self) -> BnString {
|
||||
unsafe {
|
||||
let name = BNGetSymbolRawName(self.handle);
|
||||
BnString::from_raw(name)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn address(&self) -> u64 {
|
||||
unsafe { BNGetSymbolAddress(self.handle) }
|
||||
}
|
||||
|
||||
pub fn auto_defined(&self) -> bool {
|
||||
unsafe { BNIsSymbolAutoDefined(self.handle) }
|
||||
}
|
||||
|
||||
/// Wether this symbol has external linkage
|
||||
pub fn external(&self) -> bool {
|
||||
self.binding() == Binding::Weak || self.binding() == Binding::Global
|
||||
}
|
||||
|
||||
pub fn imported_function_from_import_address_symbol(sym: &Symbol, addr: u64) -> Ref<Symbol> {
|
||||
unsafe {
|
||||
let res = BNImportedFunctionFromImportAddressSymbol(sym.handle, addr);
|
||||
Symbol::ref_from_raw(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Symbol {}
|
||||
unsafe impl Sync for Symbol {}
|
||||
|
||||
impl fmt::Debug for Symbol {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"<sym {:?} '{}' @ {:x} (handle: {:?})>",
|
||||
self.sym_type(),
|
||||
self.full_name(),
|
||||
self.address(),
|
||||
self.handle
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for Symbol {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for Symbol {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewSymbolReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeSymbol(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for Symbol {
|
||||
type Raw = *mut BNSymbol;
|
||||
type Context = ();
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for Symbol {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeSymbolList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for Symbol {
|
||||
type Wrapped = Guard<'a, Symbol>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
|
||||
Guard::new(Symbol::from_raw(*raw), context)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Symbol {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.handle.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Symbol {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.handle == other.handle
|
||||
}
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
// 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 creating and modifying tags in a BinaryView.
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::binaryview::BinaryView;
|
||||
|
||||
use crate::rc::*;
|
||||
use crate::string::*;
|
||||
|
||||
pub struct Tag {
|
||||
pub(crate) handle: *mut BNTag,
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
pub(crate) unsafe fn from_raw(handle: *mut BNTag) -> Ref<Self> {
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Ref::new(Self { handle })
|
||||
}
|
||||
|
||||
pub fn new<S: BnStrCompatible>(t: &TagType, data: S) -> Ref<Self> {
|
||||
let data = data.into_bytes_with_nul();
|
||||
unsafe { Self::from_raw(BNCreateTag(t.handle, data.as_ref().as_ptr() as *mut _)) }
|
||||
}
|
||||
|
||||
pub fn id(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNTagGetId(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn data(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNTagGetData(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn t(&self) -> Ref<TagType> {
|
||||
unsafe { TagType::from_raw(BNTagGetType(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn set_data<S: BnStrCompatible>(&self, data: S) {
|
||||
let data = data.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNTagSetData(self.handle, data.as_ref().as_ptr() as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for Tag {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewTagReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeTag(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for Tag {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Tag {}
|
||||
unsafe impl Sync for Tag {}
|
||||
|
||||
pub type TagTypeType = BNTagTypeType;
|
||||
|
||||
pub struct TagType {
|
||||
pub(crate) handle: *mut BNTagType,
|
||||
}
|
||||
|
||||
impl TagType {
|
||||
pub(crate) unsafe fn from_raw(handle: *mut BNTagType) -> Ref<Self> {
|
||||
debug_assert!(!handle.is_null());
|
||||
|
||||
Ref::new(Self { handle })
|
||||
}
|
||||
|
||||
pub fn create<N: BnStrCompatible, I: BnStrCompatible>(
|
||||
view: &BinaryView,
|
||||
name: N,
|
||||
icon: I,
|
||||
) -> Ref<Self> {
|
||||
let tag_type = unsafe { Self::from_raw(BNCreateTagType(view.handle)) };
|
||||
tag_type.set_name(name);
|
||||
tag_type.set_icon(icon);
|
||||
tag_type
|
||||
}
|
||||
|
||||
pub fn id(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNTagTypeGetId(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn icon(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNTagTypeGetIcon(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn set_icon<S: BnStrCompatible>(&self, icon: S) {
|
||||
let icon = icon.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNTagTypeSetName(self.handle, icon.as_ref().as_ptr() as *mut _);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNTagTypeGetName(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn set_name<S: BnStrCompatible>(&self, name: S) {
|
||||
let name = name.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNTagTypeSetName(self.handle, name.as_ref().as_ptr() as *mut _);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visible(&self) -> bool {
|
||||
unsafe { BNTagTypeGetVisible(self.handle) }
|
||||
}
|
||||
|
||||
pub fn set_visible(&self, visible: bool) {
|
||||
unsafe { BNTagTypeSetVisible(self.handle, visible) }
|
||||
}
|
||||
|
||||
pub fn t(&self) -> TagTypeType {
|
||||
unsafe { BNTagTypeGetType(self.handle) }
|
||||
}
|
||||
|
||||
pub fn set_type<S: BnStrCompatible>(&self, t: S) {
|
||||
let t = t.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNTagTypeSetName(self.handle, t.as_ref().as_ptr() as *mut _);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view(&self) -> Ref<BinaryView> {
|
||||
unsafe { BinaryView::from_raw(BNTagTypeGetView(self.handle)) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl RefCountable for TagType {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
|
||||
Ref::new(Self {
|
||||
handle: BNNewTagTypeReference(handle.handle),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(handle: &Self) {
|
||||
BNFreeTagType(handle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for TagType {
|
||||
type Owned = Ref<Self>;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
unsafe { RefCountable::inc_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for TagType {}
|
||||
unsafe impl Sync for TagType {}
|
||||
@@ -0,0 +1,20 @@
|
||||
use crate::{
|
||||
string::{BnStrCompatible, BnString},
|
||||
types::QualifiedName,
|
||||
};
|
||||
use binaryninjacore_sys::{BNRustSimplifyStrToFQN, BNRustSimplifyStrToStr};
|
||||
|
||||
pub fn simplify_str_to_str<S: BnStrCompatible>(input: S) -> BnString {
|
||||
let name = input.into_bytes_with_nul();
|
||||
unsafe { BnString::from_raw(BNRustSimplifyStrToStr(name.as_ref().as_ptr() as *mut _)) }
|
||||
}
|
||||
|
||||
pub fn simplify_str_to_fqn<S: BnStrCompatible>(input: S, simplify: bool) -> QualifiedName {
|
||||
let name = input.into_bytes_with_nul();
|
||||
unsafe {
|
||||
QualifiedName(BNRustSimplifyStrToFQN(
|
||||
name.as_ref().as_ptr() as *mut _,
|
||||
simplify,
|
||||
))
|
||||
}
|
||||
}
|
||||
+2607
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user