1extern 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
53pub 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
64pub 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 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 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 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 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}