hexpatch_keystone/
lib.rs

1//! Keystone Assembler Engine (www.keystone-engine.org) \
2//! By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 \
3//! Rust bindings by Remco Verhoef <remco@dutchcoders.io>, 2016
4//!
5//! ```rust
6//! extern crate hexpatch_keystone;
7//! use hexpatch_keystone::{Keystone, Arch, Mode, OptionType, OptionValue};
8//!
9//! fn main() {
10//!     let engine = Keystone::new(Arch::X86, Mode::MODE_32)
11//!         .expect("Could not initialize Keystone engine");
12//!     engine.option(OptionType::SYNTAX, OptionValue::SYNTAX_NASM)
13//!         .expect("Could not set option to nasm syntax");
14//!     let result = engine.asm("mov ah, 0x80".to_string(), 0)
15//!         .expect("Could not assemble");
16//! }
17//! ```
18
19extern crate hexpatch_keystone_sys as ffi;
20extern crate libc;
21
22use std::{
23    convert::TryInto,
24    ffi::{CStr, CString},
25    fmt,
26    ops::Not,
27};
28
29pub use ffi::keystone_const::*;
30pub use ffi::ks_handle;
31
32#[derive(Debug, PartialEq)]
33pub struct AsmResult {
34    pub size: u32,
35    pub stat_count: u32,
36    pub bytes: Vec<u8>,
37}
38
39impl fmt::Display for AsmResult {
40    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41        for &byte in &self.bytes {
42            f.write_fmt(format_args!("{:02x}", byte))?;
43        }
44
45        Ok(())
46    }
47}
48
49pub fn bindings_version() -> (u32, u32) {
50    (API_MAJOR, API_MINOR)
51}
52
53/// Return tuple `(major, minor)` API version numbers.
54pub fn version() -> (u32, u32) {
55    let mut major: u32 = 0;
56    let mut minor: u32 = 0;
57
58    unsafe {
59        ffi::ks_version(&mut major, &mut minor);
60    }
61    (major, minor)
62}
63
64/// Return tuple `(major, minor)` API version numbers.
65pub fn arch_supported(arch: Arch) -> bool {
66    unsafe { ffi::ks_arch_supported(arch) != 0 }
67}
68
69pub fn error_msg(error: Error) -> String {
70    unsafe {
71        CStr::from_ptr(ffi::ks_strerror(error))
72            .to_string_lossy()
73            .into_owned()
74    }
75}
76
77pub struct Keystone {
78    handle: ks_handle,
79}
80
81impl Keystone {
82    /// Create new instance of Keystone engine.
83    pub fn new(arch: Arch, mode: Mode) -> Result<Keystone, Error> {
84        if version() != bindings_version() {
85            return Err(Error::VERSION);
86        }
87
88        let mut handle: Option<ks_handle> = None;
89
90        let err = unsafe { ffi::ks_open(arch, mode, &mut handle) };
91        if err == Error::OK {
92            Ok(Keystone {
93                handle: handle.expect("Got NULL engine from ks_open()")
94            })
95        } else {
96            Err(err)
97        }
98    }
99
100    /// Report the last error number when some API function fail.
101    pub fn error(&self) -> Option<Error> {
102        let err = unsafe { ffi::ks_errno(self.handle) };
103        if err == Error::OK {
104            None
105        } else {
106            Some(err)
107        }
108    }
109
110    /// Set option for Keystone engine at runtime
111    pub fn option(&self, option_type: OptionType, value: OptionValue) -> Result<(), Error> {
112        let err = unsafe { ffi::ks_option(self.handle, option_type, value) };
113        if err == Error::OK {
114            Ok(())
115        } else {
116            Err(err)
117        }
118    }
119
120    /// Assemble a string given its the buffer, size, start address and number
121    /// of instructions to be decoded.
122    ///
123    /// This API dynamically allocate memory to contain assembled instruction.
124    /// Resulted array of bytes containing the machine code  is put into @*encoding
125    pub fn asm(&self, str: String, address: u64) -> Result<AsmResult, Error> {
126        let mut size: libc::size_t = 0;
127        let mut stat_count: libc::size_t = 0;
128
129        let s = CString::new(str).unwrap();
130        let mut ptr: *mut libc::c_uchar = std::ptr::null_mut();
131
132        let err = Error::from_bits_truncate(unsafe {
133            ffi::ks_asm(
134                self.handle,
135                s.as_ptr(),
136                address,
137                &mut ptr,
138                &mut size,
139                &mut stat_count,
140            )
141        });
142
143        if err == Error::OK {
144            debug_assert!(ptr.is_null().not());
145            let bytes_slice = unsafe { std::slice::from_raw_parts(ptr, size) };
146            let bytes = bytes_slice.to_vec();
147
148            unsafe {
149                ffi::ks_free(ptr);
150            };
151
152            Ok(AsmResult {
153                size: size.try_into().expect("size_t overflowed u32"),
154                stat_count: stat_count.try_into().expect("size_t overflowed u32"),
155                bytes,
156            })
157        } else {
158            let err = self.error().unwrap_or(err);
159            Err(err)
160        }
161    }
162}
163
164impl Drop for Keystone {
165    fn drop(&mut self) {
166        unsafe { ffi::ks_close(self.handle) };
167    }
168}