use arch::CapstoneBuilder;
use capstone_sys::cs_opt_value::*;
use capstone_sys::*;
use constants::{Arch, Endian, ExtraMode, Mode, OptValue, Syntax};
use error::*;
use instruction::{Insn, InsnDetail, InsnGroupId, InsnId, Instructions, RegId};
use std::convert::From;
use std::ffi::CStr;
use std::marker::PhantomData;
use std::mem;
use std::os::raw::{c_int, c_uint, c_void};
#[derive(Debug)]
pub struct Capstone {
csh: *mut c_void,
mode: cs_mode,
endian: cs_mode,
syntax: cs_opt_value::Type,
extra_mode: cs_mode,
detail_enabled: bool,
skipdata_enabled: bool,
raw_mode: cs_mode,
arch: Arch,
}
macro_rules! define_set_mode {
(
$( #[$func_attr:meta] )*
=> $($visibility:ident)*, $fn_name:ident,
$opt_type:ident, $param_name:ident : $param_type:ident ;
$cs_base_type:ident
) => {
$( #[$func_attr] )*
$($visibility)* fn $fn_name(&mut self, $param_name: $param_type) -> CsResult<()> {
let old_val = self.$param_name;
self.$param_name = $cs_base_type::from($param_name);
let old_raw_mode = self.raw_mode;
let new_raw_mode = self.update_raw_mode();
let result = self._set_cs_option(
cs_opt_type::$opt_type,
new_raw_mode.0 as usize,
);
if result.is_err() {
self.raw_mode = old_raw_mode;
self.$param_name = old_val;
}
result
}
}
}
pub static NO_EXTRA_MODE: EmptyExtraModeIter = EmptyExtraModeIter(PhantomData);
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct EmptyExtraModeIter(PhantomData<()>);
impl Iterator for EmptyExtraModeIter {
type Item = ExtraMode;
fn next(&mut self) -> Option<Self::Item> {
None
}
}
impl Capstone {
pub fn new() -> CapstoneBuilder {
CapstoneBuilder::new()
}
pub fn new_raw<T: Iterator<Item = ExtraMode>>(
arch: Arch,
mode: Mode,
extra_mode: T,
endian: Option<Endian>,
) -> CsResult<Capstone> {
let mut handle: csh = 0;
let csarch: cs_arch = arch.into();
let csmode: cs_mode = mode.into();
let endian = match endian {
Some(endian) => cs_mode::from(endian),
None => cs_mode(0),
};
let extra_mode = Self::extra_mode_value(extra_mode);
let combined_mode = csmode | endian | extra_mode;
let err = unsafe { cs_open(csarch, combined_mode, &mut handle) };
if cs_err::CS_ERR_OK == err {
let syntax = CS_OPT_SYNTAX_DEFAULT;
let raw_mode = cs_mode(0);
let detail_enabled = false;
let skipdata_enabled = detail_enabled;
let mut cs = Capstone {
csh: handle as *mut c_void,
syntax,
endian,
mode: csmode,
extra_mode,
detail_enabled,
skipdata_enabled,
raw_mode,
arch,
};
cs.update_raw_mode();
Ok(cs)
} else {
Err(err.into())
}
}
pub fn disasm_all<'a>(&'a self, code: &[u8], addr: u64) -> CsResult<Instructions<'a>> {
self.disasm(code, addr, 0)
}
pub fn disasm_count<'a>(
&'a self,
code: &[u8],
addr: u64,
count: usize,
) -> CsResult<Instructions<'a>> {
if count == 0 {
return Err(Error::CustomError("Invalid dissasemble count; must be > 0"));
}
self.disasm(code, addr, count)
}
fn disasm<'a>(&'a self, code: &[u8], addr: u64, count: usize) -> CsResult<Instructions<'a>> {
let mut ptr: *mut cs_insn = unsafe { mem::zeroed() };
let insn_count = unsafe {
cs_disasm(
self.csh(),
code.as_ptr(),
code.len() as usize,
addr,
count as usize,
&mut ptr,
)
};
if insn_count == 0 {
match self.error_result() {
Ok(_) => Ok(Instructions::new_empty()),
Err(err) => Err(err),
}
} else {
Ok(unsafe { Instructions::from_raw_parts(ptr, insn_count) })
}
}
#[inline]
fn csh(&self) -> csh {
self.csh as csh
}
#[allow(dead_code)]
pub(crate) fn raw_mode(&self) -> cs_mode {
self.raw_mode
}
fn update_raw_mode(&mut self) -> cs_mode {
self.raw_mode = self.mode | self.extra_mode | self.endian;
self.raw_mode
}
fn extra_mode_value<T: Iterator<Item = ExtraMode>>(extra_mode: T) -> cs_mode {
extra_mode.fold(cs_mode(0), |acc, x| acc | cs_mode::from(x))
}
pub fn set_extra_mode<T: Iterator<Item = ExtraMode>>(&mut self, extra_mode: T) -> CsResult<()> {
let old_val = self.extra_mode;
self.extra_mode = Self::extra_mode_value(extra_mode);
let old_mode = self.raw_mode;
let new_mode = self.update_raw_mode();
let result = self._set_cs_option(cs_opt_type::CS_OPT_MODE, new_mode.0 as usize);
if result.is_err() {
self.raw_mode = old_mode;
self.extra_mode = old_val;
}
result
}
pub fn set_syntax(&mut self, syntax: Syntax) -> CsResult<()> {
let syntax_int = cs_opt_value::Type::from(syntax);
let result = self._set_cs_option(cs_opt_type::CS_OPT_SYNTAX, syntax_int as usize);
if result.is_ok() {
self.syntax = syntax_int;
}
result
}
define_set_mode!(
=> pub, set_endian, CS_OPT_MODE, endian : Endian; cs_mode);
define_set_mode!(
=> pub, set_mode, CS_OPT_MODE, mode : Mode; cs_mode);
fn error_result(&self) -> CsResult<()> {
let errno = unsafe { cs_errno(self.csh()) };
if errno == cs_err::CS_ERR_OK {
Ok(())
} else {
Err(errno.into())
}
}
fn _set_cs_option(&mut self, option_type: cs_opt_type, option_value: usize) -> CsResult<()> {
let err = unsafe { cs_option(self.csh(), option_type, option_value) };
if cs_err::CS_ERR_OK == err {
Ok(())
} else {
Err(err.into())
}
}
pub fn set_detail(&mut self, enable_detail: bool) -> CsResult<()> {
let option_value: usize = OptValue::from(enable_detail).0 as usize;
let result = self._set_cs_option(cs_opt_type::CS_OPT_DETAIL, option_value);
if result.is_ok() {
self.detail_enabled = enable_detail;
}
result
}
pub fn set_skipdata(&mut self, enable_skipdata: bool) -> CsResult<()> {
let option_value: usize = OptValue::from(enable_skipdata).0 as usize;
let result = self._set_cs_option(cs_opt_type::CS_OPT_SKIPDATA, option_value);
if result.is_ok() {
self.skipdata_enabled = enable_skipdata;
}
result
}
pub fn reg_name(&self, reg_id: RegId) -> Option<String> {
let reg_name = unsafe {
let _reg_name = cs_reg_name(self.csh(), c_uint::from(reg_id.0));
if _reg_name.is_null() {
return None;
}
CStr::from_ptr(_reg_name).to_string_lossy().into_owned()
};
Some(reg_name)
}
pub fn insn_name(&self, insn_id: InsnId) -> Option<String> {
let insn_name = unsafe {
let _insn_name = cs_insn_name(self.csh(), insn_id.0 as c_uint);
if _insn_name.is_null() {
return None;
}
CStr::from_ptr(_insn_name).to_string_lossy().into_owned()
};
Some(insn_name)
}
pub fn group_name(&self, group_id: InsnGroupId) -> Option<String> {
let group_name = unsafe {
let _group_name = cs_group_name(self.csh(), c_uint::from(group_id.0));
if _group_name.is_null() {
return None;
}
CStr::from_ptr(_group_name).to_string_lossy().into_owned()
};
Some(group_name)
}
pub fn insn_detail<'s, 'i: 's>(&'s self, insn: &'i Insn) -> CsResult<InsnDetail<'i>> {
if !self.detail_enabled {
Err(Error::DetailOff)
} else if insn.id().0 == 0 {
Err(Error::IrrelevantDataInSkipData)
} else if Self::is_diet() {
Err(Error::IrrelevantDataInDiet)
} else {
Ok(unsafe { insn.detail(self.arch) })
}
}
pub fn lib_version() -> (u32, u32) {
let mut major: c_int = 0;
let mut minor: c_int = 0;
let major_ptr: *mut c_int = &mut major;
let minor_ptr: *mut c_int = &mut minor;
let _ = unsafe { cs_version(major_ptr, minor_ptr) };
(major as u32, minor as u32)
}
pub fn supports_arch(arch: Arch) -> bool {
unsafe { cs_support(arch as c_int) }
}
pub fn is_diet() -> bool {
unsafe { cs_support(CS_SUPPORT_DIET as c_int) }
}
}
impl Drop for Capstone {
fn drop(&mut self) {
unsafe { cs_close(&mut self.csh()) };
}
}