use libc;
use std::collections::HashMap;
use std::convert::From;
use std::ffi::CStr;
use std::mem;
use error::*;
use capstone_sys::*;
use constants::{Arch, Mode, OptValue};
use instruction::{Insn, Instructions, Detail};
pub struct Capstone {
csh: csh, cs_option_state: HashMap<cs_opt_type, libc::size_t>, _arch: Arch,
}
impl Capstone {
pub fn new(arch: Arch, mode: Mode) -> CsResult<Capstone> {
let mut handle = 0;
let csarch: cs_arch = arch.into();
let csmode: cs_mode = mode.into();
let err = unsafe { cs_open(csarch, csmode, &mut handle) };
if cs_err::CS_ERR_OK == err {
let mut opt_state: HashMap<cs_opt_type, libc::size_t> = HashMap::new();
opt_state.insert(cs_opt_type::CS_OPT_SYNTAX,
CS_OPT_SYNTAX_DEFAULT as libc::size_t);
opt_state.insert(cs_opt_type::CS_OPT_DETAIL,
cs_opt_value::CS_OPT_OFF as libc::size_t);
opt_state.insert(cs_opt_type::CS_OPT_MODE, mode as libc::size_t);
opt_state.insert(cs_opt_type::CS_OPT_MEM, 0);
opt_state.insert(cs_opt_type::CS_OPT_SKIPDATA,
cs_opt_value::CS_OPT_OFF as libc::size_t);
Ok(Capstone {
csh: handle,
cs_option_state: opt_state,
_arch: arch,
})
} else {
Err(err.into())
}
}
#[inline]
fn set_option(&self, opt_type: cs_opt_type, value: usize) -> CsResult<()> {
let err = unsafe { cs_option(self.csh, opt_type, value) };
if cs_err::CS_ERR_OK == err {
Ok(())
} else {
Err(Error::from(err))
}
}
pub fn disasm_all(&self, code: &[u8], addr: u64) -> CsResult<Instructions> {
self.disasm(code, addr, 0)
}
pub fn disasm_count(&self, code: &[u8], addr: u64, count: usize) -> CsResult<Instructions> {
if count == 0 {
return Err(Error::CustomError("Invalid dissasemble count; must be > 0"));
}
self.disasm(code, addr, count)
}
fn disasm(&self, code: &[u8], addr: u64, count: usize) -> CsResult<Instructions> {
let mut ptr: *mut cs_insn = unsafe { mem::zeroed() };
let insn_count = unsafe {
cs_disasm(self.csh,
code.as_ptr(),
code.len() as libc::size_t,
addr,
count as libc::size_t,
&mut ptr)
};
if insn_count == 0 {
return self.error_result();
}
Ok(unsafe {
Instructions::from_raw_parts(ptr, insn_count as isize)
})
}
pub fn set_mode(&mut self, modes: &[Mode]) -> CsResult<()> {
let mut value: usize = 0;
for mode in modes {
let mode = cs_mode::from(*mode);
value |= mode as usize;
}
self.set_option(cs_opt_type::CS_OPT_MODE, value)
}
pub fn att(&self) {
self.set_option(
cs_opt_type::CS_OPT_SYNTAX,
cs_opt_value::CS_OPT_SYNTAX_ATT as usize,
).unwrap()
}
pub fn intel(&self) {
self.set_option(
cs_opt_type::CS_OPT_SYNTAX,
cs_opt_value::CS_OPT_SYNTAX_INTEL as usize,
).unwrap()
}
fn error_result<T>(&self) -> CsResult<T> {
Err(unsafe { cs_errno(self.csh) }.into())
}
fn set_cs_option(&mut self,
option_type: cs_opt_type,
option_value: libc::size_t)
-> CsResult<()> {
let err = unsafe { cs_option(self.csh, option_type, option_value) };
if cs_err::CS_ERR_OK == err {
self.cs_option_state.insert(option_type, option_value);
Ok(())
} else {
Err(err.into())
}
}
pub fn set_detail(&mut self, enable_detail: bool) -> CsResult<()> {
let option_value: libc::size_t = OptValue::from(enable_detail).0 as libc::size_t;
self.set_cs_option(cs_opt_type::CS_OPT_DETAIL, option_value)
}
pub fn reg_name(&self, reg_id: u64) -> Option<String> {
let reg_name = unsafe {
let _reg_name = cs_reg_name(self.csh, reg_id as libc::c_uint);
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: u64) -> Option<String> {
let insn_name = unsafe {
let _insn_name = cs_insn_name(self.csh, insn_id as libc::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: u64) -> Option<String> {
let group_name = unsafe {
let _group_name = cs_group_name(self.csh, group_id as libc::c_uint);
if _group_name.is_null() {
return None;
}
CStr::from_ptr(_group_name)
.to_string_lossy()
.into_owned()
};
Some(group_name)
}
fn insn_detail<'s, 'i: 's>(&'s self, insn: &'i Insn) -> CsResult<Detail<'i>> {
if self.cs_option_state[&cs_opt_type::CS_OPT_DETAIL] == cs_opt_value::CS_OPT_OFF as libc::size_t {
Err(Error::Capstone(CapstoneError::DetailOff))
} else if insn.id() == 0 {
Err(Error::Capstone(CapstoneError::IrrelevantDataInSkipData))
} else if Self::is_diet() {
Err(Error::Capstone(CapstoneError::IrrelevantDataInDiet))
} else {
Ok(unsafe { insn.detail() })
}
}
pub fn insn_belongs_to_group(&self, insn: &Insn, group_id: u64) -> CsResult<bool> {
self.insn_detail(insn)?;
Ok(unsafe { cs_insn_group(self.csh, &insn.0 as *const cs_insn, group_id as libc::c_uint) })
}
pub fn insn_groups<'i>(&self, insn: &'i Insn) -> CsResult<&'i [u8]> {
let detail = self.insn_detail(insn)?;
let group_ids: &'i [libc::uint8_t] = unsafe { mem::transmute(detail.groups()) };
Ok(group_ids)
}
pub fn register_id_is_read(&self, insn: &Insn, reg_id: u64) -> CsResult<bool> {
self.insn_detail(insn)?;
Ok(unsafe { cs_reg_read(self.csh, &insn.0 as *const cs_insn, reg_id as libc::c_uint) })
}
pub fn read_registers<'i>(&self, insn: &'i Insn) -> CsResult<&'i [u8]> {
let detail = self.insn_detail(insn)?;
let reg_read_ids: &'i [libc::uint8_t] = unsafe { mem::transmute(detail.regs_read()) };
Ok(reg_read_ids)
}
pub fn register_is_written(&self, insn: &Insn, reg_id: u64) -> CsResult<bool> {
self.insn_detail(insn)?;
Ok(unsafe { cs_reg_write(self.csh, &insn.0 as *const cs_insn, reg_id as libc::c_uint) })
}
pub fn write_registers<'i>(&self, insn: &'i Insn) -> CsResult<&'i [u8]> {
let detail = self.insn_detail(insn)?;
let reg_write_ids: &'i [libc::uint8_t] = unsafe { mem::transmute(detail.regs_write()) };
Ok(reg_write_ids)
}
pub fn lib_version() -> (u32, u32) {
let mut major: libc::c_int = 0;
let mut minor: libc::c_int = 0;
let major_ptr: *mut libc::c_int = &mut major;
let minor_ptr: *mut libc::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 libc::c_int) }
}
pub fn is_diet() -> bool {
unsafe { cs_support(CS_SUPPORT_DIET as libc::c_int) }
}
}
impl Drop for Capstone {
fn drop(&mut self) {
unsafe { cs_close(&mut self.csh) };
}
}