Miasm2
 All Classes Namespaces Files Functions Variables Typedefs Properties Macros
jitload.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 import logging
4 from functools import wraps
5 from collections import Sequence, namedtuple
6 
7 from miasm2.jitter.csts import *
8 from miasm2.core.utils import *
9 from miasm2.core.bin_stream import bin_stream_vm
10 from miasm2.ir.ir2C import init_arch_C
11 
12 hnd = logging.StreamHandler()
13 hnd.setFormatter(logging.Formatter("[%(levelname)s]: %(message)s"))
14 log = logging.getLogger('jitload.py')
15 log.addHandler(hnd)
16 log.setLevel(logging.CRITICAL)
17 log_func = logging.getLogger('jit function call')
18 log_func.addHandler(hnd)
19 log_func.setLevel(logging.CRITICAL)
20 
21 try:
22  from miasm2.jitter.jitcore_tcc import JitCore_Tcc
23 except ImportError:
24  log.error('cannot import jit tcc')
25 
26 try:
27  from miasm2.jitter.jitcore_llvm import JitCore_LLVM
28 except ImportError:
29  log.error('cannot import jit llvm')
30 
31 try:
32  from miasm2.jitter.jitcore_python import JitCore_Python
33 except ImportError:
34  log.error('cannot import jit python')
35 
36 try:
37  from miasm2.jitter import VmMngr
38 except ImportError:
39  log.error('cannot import VmMngr')
40 
41 
42 def named_arguments(func):
43  """Function decorator to allow the use of .func_args_*() methods
44  with either the number of arguments or the list of the argument
45  names.
46 
47  The wrapper is also used to log the argument values.
48 
49  @func: function
50 
51  """
52  @wraps(func)
53  def newfunc(self, args):
54  if isinstance(args, Sequence):
55  ret_ad, arg_vals = func(self, len(args))
56  arg_vals = namedtuple("args", args)(*arg_vals)
57  # func_name(arguments) return address
58  log_func.info('%s(%s) ret addr: %s',
59  whoami(),
60  ', '.join("%s=0x%x" % (field, value)
61  for field, value in arg_vals._asdict().iteritems()),
62  hex(ret_ad))
63  return ret_ad, namedtuple("args", args)(*arg_vals)
64  else:
65  ret_ad, arg_vals = func(self, args)
66  # func_name(arguments) return address
67  log_func.info('%s(%s) ret addr: %s',
68  whoami(),
69  ', '.join(hex(arg) for arg in arg_vals),
70  hex(ret_ad))
71  return ret_ad, arg_vals
72  return newfunc
73 
74 
76 
77  "Handle a list of callback"
78 
79  def __init__(self):
80  self.callbacks = {} # Key -> [callback list]
81 
82  def add_callback(self, name, callback):
83  """Add a callback to the key @name, iff the @callback isn't already
84  assigned to it"""
85  if callback not in self.callbacks.get(name, []):
86  self.callbacks[name] = self.callbacks.get(name, []) + [callback]
87 
88  def set_callback(self, name, *args):
89  "Set the list of callback for key 'name'"
90  self.callbacks[name] = list(args)
91 
92  def get_callbacks(self, name):
93  "Return the list of callbacks associated to key 'name'"
94  return self.callbacks.get(name, [])
95 
96  def remove_callback(self, callback):
97  """Remove the callback from the list.
98  Return the list of empty keys (removed)"""
99 
100  to_check = set()
101  for key, cb_list in self.callbacks.items():
102  try:
103  cb_list.remove(callback)
104  to_check.add(key)
105  except ValueError:
106  pass
107 
108  empty_keys = []
109  for key in to_check:
110  if len(self.callbacks[key]) == 0:
111  empty_keys.append(key)
112  del(self.callbacks[key])
113 
114  return empty_keys
115 
116  def has_callbacks(self, name):
117  return name in self.callbacks
118 
119  def call_callbacks(self, name, *args):
120  """Call callbacks associated to key 'name' with arguments args. While
121  callbacks return True, continue with next callback.
122  Iterator on other results."""
123 
124  res = True
125 
126  for c in self.get_callbacks(name):
127  res = c(*args)
128  if res is not True:
129  yield res
130 
131  def __call__(self, name, *args):
132  "Wrapper for call_callbacks"
133  return self.call_callbacks(name, *args)
134 
135 
137 
138  "Handle a list of callback with conditions on bitflag"
139 
140  def call_callbacks(self, bitflag, *args):
141  """Call each callbacks associated with bit set in bitflag. While
142  callbacks return True, continue with next callback.
143  Iterator on other results"""
144 
145  res = True
146  for b in self.callbacks:
147 
148  if b & bitflag != 0:
149  # If the flag matched
150  for res in super(CallbackHandlerBitflag,
151  self).call_callbacks(b, *args):
152  if res is not True:
153  yield res
154 
155 
157 
158  "Return type for exception handler"
159 
160  def __init__(self, except_flag):
161  self.except_flag = except_flag
162 
163  @classmethod
165  return cls(EXCEPT_BREAKPOINT_INTERN)
166 
167  def __eq__(self, to_cmp):
168  if not isinstance(to_cmp, ExceptionHandle):
169  return False
170  return (self.except_flag == to_cmp.except_flag)
171 
172 
173 class jitter:
174 
175  "Main class for JIT handling"
176 
177  def __init__(self, ir_arch, jit_type="tcc"):
178  """Init an instance of jitter.
179  @ir_arch: ir instance for this architecture
180  @jit_type: JiT backend to use. Available options are:
181  - "tcc"
182  - "llvm"
183  - "python"
184  """
185 
186  self.arch = ir_arch.arch
187  self.attrib = ir_arch.attrib
188  arch_name = ir_arch.arch.name # (ir_arch.arch.name, ir_arch.attrib)
189  if arch_name == "x86":
190  from miasm2.jitter.arch import JitCore_x86 as jcore
191  elif arch_name == "arm":
192  from miasm2.jitter.arch import JitCore_arm as jcore
193  elif arch_name == "aarch64":
194  from miasm2.jitter.arch import JitCore_aarch64 as jcore
195  elif arch_name == "msp430":
196  from miasm2.jitter.arch import JitCore_msp430 as jcore
197  elif arch_name == "mips32":
198  from miasm2.jitter.arch import JitCore_mips32 as jcore
199  else:
200  raise ValueError("unsupported jit arch!")
201 
202  self.vm = VmMngr.Vm()
203  self.cpu = jcore.JitCpu()
204 
205  self.bs = bin_stream_vm(self.vm)
206  self.ir_arch = ir_arch
207  init_arch_C(self.arch)
208 
209  if jit_type == "tcc":
210  self.jit = JitCore_Tcc(self.ir_arch, self.bs)
211  elif jit_type == "llvm":
212  self.jit = JitCore_LLVM(self.ir_arch, self.bs)
213  elif jit_type == "python":
214  self.jit = JitCore_Python(self.ir_arch, self.bs)
215  else:
216  raise Exception("Unkown JiT Backend")
217 
218  self.cpu.init_regs()
219  self.vm.init_memory_page_pool()
220  self.vm.init_code_bloc_pool()
221  self.vm.init_memory_breakpoint()
222 
223  self.vm.set_addr2obj(self.jit.addr2obj)
224 
225  self.jit.load()
226  self.cpu.vmmngr = self.vm
227  self.cpu.jitter = self.jit
228  self.stack_size = 0x10000
229  self.stack_base = 0x1230000
230 
231 
232  # Init callback handler
236  self.exec_cb = None
237 
239  "Add common exceptions handlers"
240 
241  def exception_automod(jitter):
242  "Tell the JiT backend to update blocs modified"
243 
244  self.jit.updt_automod_code(jitter.vm)
245  self.vm.set_exception(0)
246 
247  return True
248 
249  def exception_memory_breakpoint(jitter):
250  "Stop the execution and return an identifier"
251  return ExceptionHandle.memoryBreakpoint()
252 
253  self.add_exception_handler(EXCEPT_CODE_AUTOMOD, exception_automod)
254  self.add_exception_handler(EXCEPT_BREAKPOINT_INTERN,
255  exception_memory_breakpoint)
256 
257  def add_breakpoint(self, addr, callback):
258  """Add a callback associated with addr.
259  @addr: breakpoint address
260  @callback: function with definition (jitter instance)
261  """
262  self.breakpoints_handler.add_callback(addr, callback)
263  self.jit.add_disassembly_splits(addr)
264 
265  def set_breakpoint(self, addr, *args):
266  """Set callbacks associated with addr.
267  @addr: breakpoint address
268  @args: functions with definition (jitter instance)
269  """
270  self.breakpoints_handler.set_callback(addr, *args)
271  self.jit.add_disassembly_splits(addr)
272 
273  def remove_breakpoints_by_callback(self, callback):
274  """Remove callbacks associated with breakpoint.
275  @callback: callback to remove
276  """
277  empty_keys = self.breakpoints_handler.remove_callback(callback)
278  for key in empty_keys:
279  self.jit.remove_disassembly_splits(key)
280 
281  def add_exception_handler(self, flag, callback):
282  """Add a callback associated with an exception flag.
283  @flag: bitflag
284  @callback: function with definition (jitter instance)
285  """
286  self.exceptions_handler.add_callback(flag, callback)
287 
288  def runbloc(self, pc):
289  """Wrapper on JiT backend. Run the code at PC and return the next PC.
290  @pc: address of code to run"""
291 
292  return self.jit.runbloc(self.cpu, self.vm, pc, self.breakpoints_handler.callbacks)
293 
294  def runiter_once(self, pc):
295  """Iterator on callbacks results on code running from PC.
296  Check exceptions before breakpoints."""
297 
298  self.pc = pc
299 
300  # Callback called before exec
301  if self.exec_cb is not None:
302  res = self.exec_cb(self)
303  if res is not True:
304  yield res
305 
306  # Check breakpoints
307  old_pc = self.pc
308  for res in self.breakpoints_handler.call_callbacks(self.pc, self):
309  if res is not True:
310  yield res
311 
312  # If a callback changed pc, re call every callback
313  if old_pc != self.pc:
314  return
315 
316  # Exceptions should never be activated before run
317  assert(self.get_exception() == 0)
318 
319  # Run the bloc at PC
320  self.pc = self.runbloc(self.pc)
321 
322  # Check exceptions
323  exception_flag = self.get_exception()
324  for res in self.exceptions_handler(exception_flag, self):
325  if res is not True:
326  yield res
327 
328  def init_run(self, pc):
329  """Create an iterator on pc with runiter.
330  @pc: address of code to run
331  """
332  self.run_iterator = self.runiter_once(pc)
333  self.pc = pc
334  self.run = True
335 
336  def continue_run(self, step=False):
337  """PRE: init_run.
338  Continue the run of the current session until iterator returns or run is
339  set to False.
340  If step is True, run only one time.
341  Return the iterator value"""
342 
343  while self.run:
344  try:
345  return self.run_iterator.next()
346  except StopIteration:
347  pass
348 
349  self.run_iterator = self.runiter_once(self.pc)
350 
351  if step is True:
352  return None
353 
354  return None
355 
356  def init_stack(self):
357  self.vm.add_memory_page(
358  self.stack_base, PAGE_READ | PAGE_WRITE, "\x00" * self.stack_size)
359  sp = self.arch.getsp(self.attrib)
360  setattr(self.cpu, sp.name, self.stack_base + self.stack_size)
361  # regs = self.cpu.get_gpreg()
362  # regs[sp.name] = self.stack_base+self.stack_size
363  # self.cpu.set_gpreg(regs)
364 
365  def get_exception(self):
366  return self.cpu.get_exception() | self.vm.get_exception()
367 
368  # commun functions
369  def get_str_ansi(self, addr, max_char=None):
370  """Get ansi str from vm.
371  @addr: address in memory
372  @max_char: maximum len"""
373  l = 0
374  tmp = addr
375  while ((max_char is None or l < max_char) and
376  self.vm.get_mem(tmp, 1) != "\x00"):
377  tmp += 1
378  l += 1
379  return self.vm.get_mem(addr, l)
380 
381  def get_str_unic(self, addr, max_char=None):
382  """Get unicode str from vm.
383  @addr: address in memory
384  @max_char: maximum len"""
385  l = 0
386  tmp = addr
387  while ((max_char is None or l < max_char) and
388  self.vm.get_mem(tmp, 2) != "\x00\x00"):
389  tmp += 2
390  l += 2
391  s = self.vm.get_mem(addr, l)
392  s = s[::2] # TODO: real unicode decoding
393  return s
394 
395  def set_str_ansi(self, addr, s):
396  """Set an ansi string in memory"""
397  s = s + "\x00"
398  self.vm.set_mem(addr, s)
399 
400  def set_str_unic(self, addr, s):
401  """Set an unicode string in memory"""
402  s = "\x00".join(list(s)) + '\x00' * 3
403  self.vm.set_mem(addr, s)
404 
405  @staticmethod
406  def handle_lib(jitter):
407  """Resolve the name of the function which cause the handler call. Then
408  call the corresponding handler from users callback.
409  """
410  fname = jitter.libs.fad2cname[jitter.pc]
411  if fname in jitter.user_globals:
412  func = jitter.user_globals[fname]
413  else:
414  log.debug('%r', fname)
415  raise ValueError('unknown api', hex(jitter.pc), repr(fname))
416  func(jitter)
417  jitter.pc = getattr(jitter.cpu, jitter.ir_arch.pc.name)
418  return True
419 
420  def handle_function(self, f_addr):
421  """Add a brakpoint which will trigger the function handler"""
422  self.add_breakpoint(f_addr, self.handle_lib)
423 
424  def add_lib_handler(self, libs, user_globals=None):
425  """Add a function to handle libs call with breakpoints
426  @libs: libimp instance
427  @user_globals: dictionnary for defined user function
428  """
429  if user_globals is None:
430  user_globals = {}
431 
432  self.libs = libs
433  self.user_globals = user_globals
434 
435  for f_addr in libs.fad2cname:
436  self.handle_function(f_addr)
tuple c
Definition: ir2C.py:23
def init_arch_C
Definition: ir2C.py:28