1 """
2 Tracer Platform Base
3 """
4
5 import os
6 import struct
7 import vtrace
8 import traceback
9 import platform
10
11 from Queue import Queue
12 from threading import Thread,currentThread,Lock
13
14 import envi
15 import envi.memory as e_mem
16 import envi.threads as e_threads
17 import envi.resolver as e_resolv
18
19 import vstruct.builder as vs_builder
20
22 """
23 The basis for a tracer's internals. All platformFoo/archFoo
24 functions are defaulted, and internal state is initialized.
25 Additionally, a number of internal utilities are housed here.
26 """
28 """
29 The routine to initialize a tracer's initial internal state. This
30 is used by the initial creation routines, AND on attaches/executes
31 to re-fresh the state of the tracer.
32 WARNING: This will erase all metadata/symbols (modes/notifiers are kept)
33 """
34 vtrace.Notifier.__init__(self)
35
36 self.pid = 0
37 self.exited = False
38 self.breakpoints = {}
39 self.newbreaks = []
40 self.bpbyid = {}
41 self.bpid = 0
42 self.curbp = None
43 self.bplock = Lock()
44 self.deferred = []
45 self.running = False
46 self.runagain = False
47 self.attached = False
48
49 self.mapcache = None
50 self.thread = None
51 self.threadcache = None
52 self.fds = None
53 self.signal_ignores = []
54 self.localvars = {}
55
56
57 self._join_thread = None
58 self._break_after_bp = True
59
60 self.vsbuilder = vs_builder.VStructBuilder()
61
62 self.psize = self.getPointerSize()
63
64
65
66 self.libloaded = {}
67 self.libpaths = {}
68
69
70 self.setMeta('PendingSignal', None)
71 self.setMeta('SignalInfo', None)
72 self.setMeta("IgnoredSignals",[])
73 self.setMeta("LibraryBases", {})
74 self.setMeta("LibraryPaths", {})
75 self.setMeta("ThreadId", 0)
76 plat = platform.system()
77 rel = platform.release()
78 self.setMeta("Platform", plat)
79 self.setMeta("Release", rel)
80
81
82
83
84 self.setMeta("ShouldBreak", False)
85
87 self.bplock.acquire()
88 x = self.bpid
89 self.bpid += 1
90 self.bplock.release()
91 return x
92
94
95 self.pid = pid
96 self.attached = True
97 self.breakpoints = {}
98 self.bpbyid = {}
99 self.setMeta("PendingSignal", None)
100 self.setMeta("ExitCode", 0)
101 self.exited = False
102
104 res = self.resbynorm.get(filename, None)
105 if res: return res
106 res = self.resbyfile.get(filename, None)
107 if res: return res
108 return None
109
111 """
112 Continue stepi'ing in a loop until shouldRunAgain()
113 returns false (like RunForever mode or something)
114 """
115 if self.getMode("NonBlocking", False):
116 e_threads.firethread(self.doStepLoop)()
117 else:
118 self.doStepLoop()
119
125
153
155 """
156 Wait for the trace target to have
157 something happen... If the trace is in
158 NonBlocking mode, this will fire a thread
159 to wait for you and return control immediately.
160 """
161 if self.getMode("NonBlocking"):
162 e_threads.firethread(self._doWait)()
163 else:
164 self._doWait()
165
178
183
187
192
194
195 if bp.isEnabled():
196 try:
197 bp.activate(self)
198 except Exception, e:
199 traceback.print_exc()
200 print "WARNING: bpid %d activate failed (deferring): %s" % (bp.id, e)
201 self.deferred.append(bp)
202
204 """
205 Run through the breakpoints and setup
206 the ones that are enabled.
207
208 NOTE: This should *not* get called when continuing
209 from a fastbreak...
210 """
211
212
213 for bp in self.deferred:
214 addr = bp.resolveAddress(self)
215 if addr != None:
216 self.deferred.remove(bp)
217 self.breakpoints[addr] = bp
218
219 for bp in self.breakpoints.values():
220 self._activateBreak(bp)
221
223 """
224 Sync the reg-cache into the target process
225 """
226 if self.regcache != None:
227 for tid, ctx in self.regcache.items():
228 if ctx.isDirty():
229 self.platformSetRegCtx(tid, ctx)
230 self.regcache = None
231
233 """
234 Make sure the reg-cache is populated
235 """
236 if self.regcache == None:
237 self.regcache = {}
238 ret = self.regcache.get(threadid)
239 if ret == None:
240 ret = self.platformGetRegCtx(threadid)
241 ret.setIsDirty(False)
242 self.regcache[threadid] = ret
243 return ret
244
246 """
247 Check to see if we've landed on a breakpoint, and if so
248 deactivate and step us past it.
249
250 WARNING: Unfortunatly, cause this is used immidiatly before
251 a call to run/wait, we must block briefly even for the GUI
252 """
253
254
255 bp = self.curbp
256 if bp != None and bp.isEnabled():
257 if bp.active:
258 bp.deactivate(self)
259 orig = self.getMode("FastStep")
260 self.setMode("FastStep", True)
261 self.stepi()
262 self.setMode("FastStep", orig)
263 bp.activate(self)
264 self.curbp = None
265
267 """
268 A unified place for the test as to weather this trace
269 should be told to run again after reaching some stopping
270 condition.
271 """
272 if not self.attached:
273 return False
274
275 if self.exited:
276 return False
277
278 if self.getMode("RunForever"):
279 return True
280
281 if self.runagain:
282 return True
283
284 return False
285
287 run = "stopped"
288 exe = "None"
289 if self.isRunning():
290 run = "running"
291 elif self.exited:
292 run = "exited"
293 exe = self.getMeta("ExeName")
294 return "[%d]\t- %s <%s>" % (self.pid, exe, run)
295
296 - def initMode(self, name, value, descr):
297 """
298 Initialize a mode, this should ONLY be called
299 during setup routines for the trace! It determines
300 the available mode setings.
301 """
302 self.modes[name] = bool(value)
303 self.modedocs[name] = descr
304
306 """
307 Do cleanup when we're done. This is mostly necissary
308 because of the thread proxy holding a reference to this
309 tracer... We need to let him die off and try to get
310 garbage collected.
311 """
312 if self.thread:
313 self.thread.go = False
314
317
319 if self.thread != None:
320 self.thread.queue.put(None)
321 self.thread.join(timeout=2)
322 self.thread = None
323
325 if not self._released:
326 print 'Warning! tracer del w/o release()!'
327
329
330
331 if self.thread == None:
332 self.thread = TracerThread()
333
335 """
336 Fire the registered notifiers for the NOTIFY_* event.
337 """
338 if event == vtrace.NOTIFY_SIGNAL:
339 signo = self.getCurrentSignal()
340 if signo in self.getMeta("IgnoredSignals", []):
341 if vtrace.verbose: print "Ignoring",signo
342 self.runAgain()
343 return
344
345 alllist = self.getNotifiers(vtrace.NOTIFY_ALL)
346 nlist = self.getNotifiers(event)
347
348 trace = self
349
350
351 if self.proxy:
352 trace = self.proxy
353
354
355 self.handleEvent(event, self)
356
357
358 for notifier in alllist:
359 try:
360 notifier.handleEvent(event,trace)
361 except:
362 print "WARNING: Notifier exception for",repr(notifier)
363 traceback.print_exc()
364
365 for notifier in nlist:
366 try:
367 notifier.handleEvent(event,trace)
368 except:
369 print "WARNING: Notifier exception for",repr(notifier)
370 traceback.print_exc()
371
373 '''
374 Cleanup all breakpoints (if the current bp is "fastbreak" this routine
375 will not be called...
376 '''
377 for bp in self.breakpoints.itervalues():
378 bp.deactivate(self)
379
384
405
407 """
408 Check if the given memory fault was part of a valid
409 MapWatchpoint.
410 """
411 faultaddr,faultperm = self.platformGetMemFault()
412
413
414 if faultaddr == None: return False
415 faultpage = faultaddr & 0xfffff000
416
417 wp = self.breakpoints.get(faultpage, None)
418 if wp == None:
419 return False
420
421 self._fireBreakpoint(wp)
422
423 return True
424
432
434 """
435 This is mostly for systems (like linux) where you can't tell
436 the difference between some SIGSTOP/SIGBREAK conditions and
437 an actual breakpoint instruction.
438
439 This method will return true if either the breakpoint
440 subsystem or the sendBreak (via ShouldBreak meta) is true
441 (and it will have handled firing events for the bp)
442 """
443 pc = self.getProgramCounter()
444 if self._break_after_bp:
445 bi = self.archGetBreakInstr()
446 pc -= len(bi)
447
448 bp = self.breakpoints.get(pc, None)
449
450 if bp:
451 addr = bp.getAddress()
452
453 self.setProgramCounter(addr)
454 self._fireBreakpoint(bp)
455 return True
456
457 if self.getMeta("ShouldBreak"):
458 self.setMeta("ShouldBreak", False)
459 self.fireNotifiers(vtrace.NOTIFY_BREAK)
460 return True
461
462 return False
463
464 - def notify(self, event, trace):
465 """
466 We are frequently a notifier for ourselves, so we can do things
467 like handle events on attach and on break in a unified fashion.
468 """
469 self.threadcache = None
470 self.mapcache = None
471 self.fds = None
472 self.running = False
473
474 if event in self.auto_continue:
475 self.runAgain()
476
477
478
479 if event == vtrace.NOTIFY_EXIT_THREAD:
480 tid = self.getMeta("ThreadId")
481 self.sus_threads.pop(tid, None)
482
483 if tid == self._join_thread:
484 self._join_thread = None
485
486 self.setMode('RunForever', False)
487
488 self.runAgain(False)
489
490
491
492 if event == vtrace.NOTIFY_ATTACH:
493 pass
494
495 elif event == vtrace.NOTIFY_DETACH:
496 for tid in self.sus_threads.keys():
497 self.resumeThread(tid)
498 self._cleanupBreakpoints()
499
500 elif event == vtrace.NOTIFY_EXIT:
501 self.setMode("RunForever", False)
502 self.exited = True
503 self.attached = False
504
505 elif event == vtrace.NOTIFY_CONTINUE:
506 self.runagain = False
507
508 else:
509 self._cleanupBreakpoints()
510
527
529 """
530 This should be used *at load time* to setup the library
531 event metadata.
532
533 This *must* be called from a context where it's safe to
534 fire notifiers, because it will fire a notifier to alert
535 about a LOAD_LIBRARY. (This means *not* from inside another
536 notifer)
537 """
538
539 self.setMeta("LatestLibrary", None)
540 self.setMeta("LatestLibraryNorm", None)
541
542 normname = self.normFileName(libname)
543 if self.getSymByName(normname) != None:
544 normname = "%s_%.8x" % (normname,address)
545
546
547 if os.path.exists(libname) or always:
548
549 self.getMeta("LibraryPaths")[address] = libname
550 self.getMeta("LibraryBases")[normname] = address
551 self.setMeta("LatestLibrary", libname)
552 self.setMeta("LatestLibraryNorm", normname)
553
554 width = self.arch.getPointerSize()
555 sym = e_resolv.FileSymbol(normname, address, 0, width=width)
556 sym.casesens = self.casesens
557 self.addSymbol(sym)
558
559 self.libpaths[normname] = libname
560
561 self.fireNotifiers(vtrace.NOTIFY_LOAD_LIBRARY)
562
564 basename = os.path.basename(libname)
565 return basename.split(".")[0].split("-")[0].lower()
566
568
569
570 done = {}
571 mlen = len(magic)
572
573 for addr,size,perms,fname in self.getMemoryMaps():
574
575 if not fname:
576 continue
577
578 if done.get(fname):
579 continue
580
581 try:
582
583 if self.readMemory(addr, mlen) == magic:
584 done[fname] = True
585 self.addLibraryBase(fname, addr, always=always)
586
587 except:
588 pass
589
591 if not self.libloaded.get(normname, False):
592 fname = self.libpaths.get(normname)
593 if fname != None:
594 self._loadBinary(fname)
595 return True
596 return False
597
599 """
600 Check if a filename has yet to be parsed. If it has NOT
601 been parsed, parse it and return True, otherwise, return False
602 """
603 normname = self.normFileName(filename)
604 if not self.libloaded.get(normname, False):
605 address = self.getMeta("LibraryBases").get(normname)
606 if address != None:
607 self.platformParseBinary(filename, address, normname)
608 self.libloaded[normname] = True
609 return True
610 return False
611
622
623
624
625
626
633
642
645
648
651
654
661
664
670
677
684
695
702
704 raise Exception("Architecure must implement argGetStackTrace()!")
705
707 """
708 Add a watchpoint for the given address. Raise if the platform
709 doesn't support, or too many are active...
710 """
711 raise Exception("Architecture doesn't implement watchpoints!")
712
714 raise Exception("Architecture doesn't implement watchpoints!")
715
717 """
718 If the current register state indicates that a watchpoint was hit,
719 return the address of the watchpoint and clear the event. Otherwise
720 return None
721 """
722 pass
723
725 """
726 Return a new empty envi.registers.RegisterContext object for this
727 trace.
728 """
729 raise Exception("Platform must implement archGetRegCtx()")
730
732 """
733 Return a list of the stack frames for this process
734 (currently Intel/ebp based only). Each element of the
735 "frames list" consists of another list which is (eip,ebp)
736 """
737 raise Exception("Platform must implement getStackTrace()")
738
740 """
741 Get the full path to the main executable for this
742 *attached* Trace
743 """
744 return self.getMeta("ExeName","Unknown")
745
753
756
762
770
781
784
787
790
793
796
799
808
821
831
835
842
848
849 import threading
851 def trfunc(self, *args, **kwargs):
852 if threading.currentThread().__class__ == TracerThread:
853 return func(self, *args, **kwargs)
854
855 q = Queue()
856
857 args = (self, ) + args
858 self.thread.queue.put((func, args, kwargs, q))
859 ret = q.get()
860 if issubclass(ret.__class__, Exception):
861 raise ret
862 return ret
863 return trfunc
864
866 """
867 Ok... so here's the catch... most debug APIs do *not* allow
868 one thread to do the attach and another to do continue and another
869 to do wait... they just dont. So there. I have to make a thread
870 per-tracer (on most platforms) and proxy requests (for *some* trace
871 API methods) to it for actual execution. SUCK!
872
873 However, this lets async things like GUIs and threaded things like
874 cobra not have to be aware of which one is allowed and not allowed
875 to make particular calls and on what platforms... YAY!
876 """
878 Thread.__init__(self)
879 self.queue = Queue()
880 self.setDaemon(True)
881 self.start()
882
884 """
885 Run in a circle getting requests from our queue and
886 executing them based on the thread.
887 """
888 while True:
889 try:
890 qobj = self.queue.get()
891 if qobj == None:
892 break
893 meth, args, kwargs, queue = qobj
894 try:
895 queue.put(meth(*args, **kwargs))
896 except Exception,e:
897 queue.put(e)
898 if vtrace.verbose:
899 traceback.print_exc()
900 continue
901 except:
902 if vtrace.verbose:
903 traceback.print_exc()
904