1# Keystone Python bindings, by Nguyen Anh Quynnh <aquynh@gmail.com> 2import sys 3_python2 = sys.version_info[0] < 3 4if _python2: 5 range = xrange 6 7PY_EXTRA_VERSION = ".1" 8 9from . import arm_const, arm64_const, mips_const, sparc_const, hexagon_const, ppc_const, systemz_const, x86_const 10from .keystone_const import * 11 12from ctypes import * 13from platform import system 14from os.path import split, join, dirname, exists 15import distutils.sysconfig, sys 16 17 18import inspect 19if not hasattr(sys.modules[__name__], '__file__'): 20 __file__ = inspect.getfile(inspect.currentframe()) 21 22_lib_path = split(__file__)[0] 23_all_libs = ('keystone.dll', 'libkeystone.so', 'libkeystone.dylib') 24_found = False 25 26for _lib in _all_libs: 27 try: 28 _lib_file = join(_lib_path, _lib) 29 #print(">> 2: Trying to load %s" %_lib_file); 30 _ks = cdll.LoadLibrary(_lib_file) 31 _found = True 32 break 33 except OSError: 34 pass 35 36if _found == False: 37 # try loading from default paths 38 for _lib in _all_libs: 39 try: 40 #print(">> 1: Trying to load %s" %_lib); 41 _ks = cdll.LoadLibrary(_lib) 42 _found = True 43 break 44 except OSError: 45 pass 46 47if _found == False: 48 # last try: loading from python lib directory 49 _lib_path = distutils.sysconfig.get_python_lib() 50 for _lib in _all_libs: 51 try: 52 _lib_file = join(_lib_path, 'keystone', _lib) 53 #print(">> 2: Trying to load %s" %_lib_file); 54 _ks = cdll.LoadLibrary(_lib_file) 55 _found = True 56 break 57 except OSError: 58 pass 59 60# Attempt Darwin specific load (10.11 specific), 61# since LD_LIBRARY_PATH is not guaranteed to exist 62if (_found == False) and (system() == 'Darwin'): 63 _lib_path = '/usr/local/lib/' 64 for _lib in _all_libs: 65 try: 66 _lib_file = join(_lib_path, _lib) 67 #print(">> 3: Trying to load %s" %_lib_file); 68 _ks = cdll.LoadLibrary(_lib_file) 69 _found = True 70 break 71 except OSError: 72 pass 73 74if _found == False: 75 raise ImportError("ERROR: fail to load the dynamic library.") 76 77__version__ = "%s.%s%s" %(KS_API_MAJOR, KS_API_MINOR, PY_EXTRA_VERSION) 78 79# setup all the function prototype 80def _setup_prototype(lib, fname, restype, *argtypes): 81 getattr(lib, fname).restype = restype 82 getattr(lib, fname).argtypes = argtypes 83 84kserr = c_int 85ks_engine = c_void_p 86ks_hook_h = c_size_t 87 88_setup_prototype(_ks, "ks_version", c_uint, POINTER(c_int), POINTER(c_int)) 89_setup_prototype(_ks, "ks_arch_supported", c_bool, c_int) 90_setup_prototype(_ks, "ks_open", kserr, c_uint, c_uint, POINTER(ks_engine)) 91_setup_prototype(_ks, "ks_close", kserr, ks_engine) 92_setup_prototype(_ks, "ks_strerror", c_char_p, kserr) 93_setup_prototype(_ks, "ks_errno", kserr, ks_engine) 94_setup_prototype(_ks, "ks_option", kserr, ks_engine, c_int, c_size_t) 95# int ks_asm(ks_engine *ks, const char *string, uint64_t address, unsigned char **encoding, size_t *encoding_size, size_t *stat_count); 96_setup_prototype(_ks, "ks_asm", c_int, ks_engine, c_char_p, c_uint64, POINTER(POINTER(c_ubyte)), POINTER(c_size_t), POINTER(c_size_t)) 97_setup_prototype(_ks, "ks_free", None, POINTER(c_ubyte)) 98 99 100# access to error code via @errno of KsError 101# this also includes the @stat_count returned by ks_asm 102class KsError(Exception): 103 def __init__(self, errno, count=None): 104 self.stat_count = count 105 self.errno = errno 106 self.message = _ks.ks_strerror(self.errno) 107 if not isinstance(self.message, str) and isinstance(self.message, bytes): 108 self.message = self.message.decode('utf-8') 109 110 # retrieve @stat_count value returned by ks_asm() 111 def get_asm_count(self): 112 return self.stat_count 113 114 def __str__(self): 115 return self.message 116 117 118# return the core's version 119def ks_version(): 120 major = c_int() 121 minor = c_int() 122 combined = _ks.ks_version(byref(major), byref(minor)) 123 return (major.value, minor.value, combined) 124 125 126# return the binding's version 127def version_bind(): 128 return (KS_API_MAJOR, KS_API_MINOR, (KS_API_MAJOR << 8) + KS_API_MINOR) 129 130 131# check to see if this engine supports a particular arch 132def ks_arch_supported(query): 133 return _ks.ks_arch_supported(query) 134 135 136class Ks(object): 137 def __init__(self, arch, mode): 138 # verify version compatibility with the core before doing anything 139 (major, minor, _combined) = ks_version() 140 if major != KS_API_MAJOR or minor != KS_API_MINOR: 141 self._ksh = None 142 # our binding version is different from the core's API version 143 raise KsError(KS_ERR_VERSION) 144 145 self._arch, self._mode = arch, mode 146 self._ksh = c_void_p() 147 status = _ks.ks_open(arch, mode, byref(self._ksh)) 148 if status != KS_ERR_OK: 149 self._ksh = None 150 raise KsError(status) 151 152 if arch == KS_ARCH_X86: 153 # Intel syntax is default for X86 154 self._syntax = KS_OPT_SYNTAX_INTEL 155 else: 156 self._syntax = None 157 158 159 # destructor to be called automatically when object is destroyed. 160 def __del__(self): 161 if self._ksh: 162 try: 163 status = _ks.ks_close(self._ksh) 164 self._ksh = None 165 if status != KS_ERR_OK: 166 raise KsError(status) 167 except: # _ks might be pulled from under our feet 168 pass 169 170 171 # return assembly syntax. 172 @property 173 def syntax(self): 174 return self._syntax 175 176 177 # syntax setter: modify assembly syntax. 178 @syntax.setter 179 def syntax(self, style): 180 status = _ks.ks_option(self._ksh, KS_OPT_SYNTAX, style) 181 if status != KS_ERR_OK: 182 raise KsError(status) 183 # save syntax 184 self._syntax = style 185 186 187 # assemble a string of assembly 188 def asm(self, string, addr = 0): 189 encode = POINTER(c_ubyte)() 190 encode_size = c_size_t() 191 stat_count = c_size_t() 192 if not isinstance(string, bytes) and isinstance(string, str): 193 string = string.encode('ascii') 194 195 status = _ks.ks_asm(self._ksh, string, addr, byref(encode), byref(encode_size), byref(stat_count)) 196 if (status != 0): 197 errno = _ks.ks_errno(self._ksh) 198 raise KsError(errno, stat_count.value) 199 else: 200 if stat_count.value == 0: 201 return (None, 0) 202 else: 203 encoding = [] 204 for i in range(encode_size.value): 205 encoding.append(encode[i]) 206 _ks.ks_free(encode) 207 return (encoding, stat_count.value) 208 209 210# print out debugging info 211def debug(): 212 archs = { "arm": KS_ARCH_ARM, "arm64": KS_ARCH_ARM64, \ 213 "mips": KS_ARCH_MIPS, "sparc": KS_ARCH_SPARC, \ 214 "systemz": KS_ARCH_SYSTEMZ, "ppc": KS_ARCH_PPC, \ 215 "hexagon": KS_ARCH_HEXAGON, "x86": KS_ARCH_X86 } 216 217 all_archs = "" 218 keys = archs.keys() 219 for k in sorted(keys): 220 if ks_arch_supported(archs[k]): 221 all_archs += "-%s" % k 222 223 (major, minor, _combined) = ks_version() 224 225 return "python-%s-c%u.%u-b%u.%u" % (all_archs, major, minor, KS_API_MAJOR, KS_API_MINOR) 226 227