#![deny(missing_docs)]
extern crate libcapstone_sys;
use std::ffi::CStr;
use std::os::raw::c_char;
use std::ptr;
use libcapstone_sys::*;
pub type CsResult<T> = Result<T, cs_err>;
type CsOptionValue = Option<usize>;
pub struct Builder {
arch: cs_arch,
mode: cs_mode,
syntax: CsOptionValue,
detail: CsOptionValue,
skipdata: CsOptionValue,
skipdata_config: Option<cs_opt_skipdata>,
}
impl Builder {
pub fn new(arch: cs_arch, mode: cs_mode) -> Builder {
Builder {
arch: arch,
mode: mode,
syntax: None,
detail: None,
skipdata: None,
skipdata_config: None,
}
}
pub fn build(self) -> CsResult<Capstone> {
let engine = Capstone::new(self.arch, self.mode)?;
if let Some(opt) = self.syntax {
engine.option(CS_OPT_SYNTAX, opt)?;
}
if let Some(opt) = self.detail {
engine.option(CS_OPT_DETAIL, opt)?;
}
if let Some(opt) = self.skipdata {
engine.option(CS_OPT_SKIPDATA, opt)?;
}
if let Some(opt) = self.skipdata_config {
if self.skipdata.is_none() || self.skipdata.unwrap() != CS_OPT_ON as _ {
engine.option(CS_OPT_SKIPDATA, CS_OPT_ON as _)?;
}
engine.option(CS_OPT_SKIPDATA_SETUP, &opt as *const _ as _)?;
}
Ok(engine)
}
pub fn syntax(mut self, syntax: cs_opt_value) -> Builder {
self.syntax = Some(syntax as _);
self
}
pub fn detail(mut self, detail: cs_opt_value) -> Builder {
self.detail = Some(detail as _);
self
}
pub fn skipdata(mut self, doit: cs_opt_value) -> Builder {
self.skipdata = Some(doit as _);
self
}
pub fn skipdata_config(mut self,
mnemonic: Option<&'static str>,
callback: cs_skipdata_cb_t)
-> Builder {
self.skipdata_config = Some(cs_opt_skipdata {
mnemonic: mnemonic.unwrap_or(".byte").as_ptr() as _,
callback: callback,
user_data: ptr::null_mut(),
});
self
}
}
pub struct Capstone {
handle: csh,
}
impl Capstone {
pub fn new(arch: cs_arch, mode: cs_mode) -> CsResult<Capstone> {
let mut handle = 0;
let err = unsafe { cs_open(arch, mode, &mut handle) };
if err != CS_ERR_OK {
Err(err)
} else {
Ok(Capstone { handle: handle })
}
}
pub fn disasm(&self, code: &[u8], address: u64, count: usize) -> CsResult<Instructions> {
let mut instructions: *mut cs_insn = ptr::null_mut();
let count = unsafe {
cs_disasm(self.handle,
code.as_ptr(),
code.len(),
address,
count,
&mut instructions)
};
if count == 0 {
let err = unsafe { cs_errno(self.handle) };
debug_assert_ne!(err, CS_ERR_OK);
Err(err)
} else {
Ok(Instructions::from_raw(instructions, count))
}
}
pub fn disasm_all(&self, code: &[u8], address: u64) -> CsResult<Instructions> {
self.disasm(code, address, 0)
}
pub fn error(&self) -> Option<String> {
let err = unsafe { cs_errno(self.handle) };
if err != CS_ERR_OK {
to_string(unsafe { cs_strerror(err) })
} else {
None
}
}
pub fn group_name(&self, group_id: u32) -> Option<String> {
to_string(unsafe { cs_group_name(self.handle, group_id) })
}
pub fn insn_group(&self, insn: &cs_insn, group_id: u32) -> bool {
unsafe { cs_insn_group(self.handle, insn, group_id) }
}
pub fn insn_name(&self, insn_id: u32) -> Option<String> {
to_string(unsafe { cs_insn_name(self.handle, insn_id) })
}
pub fn option(&self, type_: cs_opt_type, value: usize) -> CsResult<()> {
let err = unsafe { cs_option(self.handle, type_, value) };
if err != CS_ERR_OK { Err(err) } else { Ok(()) }
}
pub fn reg_name(&self, reg_id: u32) -> Option<String> {
to_string(unsafe { cs_reg_name(self.handle, reg_id) })
}
pub fn reg_read(&self, insn: &cs_insn, reg_id: u32) -> bool {
unsafe { cs_reg_read(self.handle, insn, reg_id) }
}
pub fn reg_write(&self, insn: &cs_insn, reg_id: u32) -> bool {
unsafe { cs_reg_write(self.handle, insn, reg_id) }
}
}
impl Drop for Capstone {
fn drop(&mut self) {
let err = unsafe { cs_close(&mut self.handle) };
if err != CS_ERR_OK {
panic!("Error while calling cs_close: {:?}", err)
}
}
}
pub struct Instructions {
instructions: *mut cs_insn,
count: usize,
}
impl Instructions {
fn from_raw(p: *mut cs_insn, count: usize) -> Instructions {
Instructions {
instructions: p,
count: count,
}
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
pub fn iter(&self) -> PointerIter<cs_insn> {
PointerIter::new(self.instructions, self.count)
}
pub fn len(&self) -> usize {
self.count
}
}
impl Drop for Instructions {
fn drop(&mut self) {
unsafe { cs_free(self.instructions, self.count) }
}
}
pub fn support(query: cs_arch) -> bool {
unsafe { cs_support(query as _) }
}
pub fn version() -> (i32, i32, u32) {
let mut major = 0;
let mut minor = 0;
(major, minor, unsafe { cs_version(&mut major, &mut minor) })
}
fn to_string(p: *const c_char) -> Option<String> {
if p.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(p) }.to_string_lossy().into_owned())
}
}