Package vtrace
[hide private]
[frames] | no frames]

Source Code for Package vtrace

   1  """ 
   2  Vtrace Debugger Framework 
   3   
   4  Vtrace is a *mostly* native python debugging framework which 
   5  can be used to quickly write programatic debuggers and research 
   6  tools. 
   7   
   8  I'm not known for writting great docs...  but the code should 
   9  be pretty straight forward... 
  10   
  11  This has been in use for many years privately, but is nowhere 
  12  *near* free of bugs...  idiosyncracies abound. 
  13   
  14  ==== Werd ===================================================== 
  15   
  16  Blah blah blah... many more docs to come. 
  17   
  18  Brought to you by kenshoto.  e-mail invisigoth. 
  19   
  20  Greetz: 
  21      h1kari - eeeeeooorrrmmm  CHKCHKCHKCHKCHKCHKCHK 
  22      Ghetto - wizoo... to the tizoot. 
  23      atlas - *whew* finally...  no more teasing... 
  24      beatle/dnm - come out and play yo! 
  25      The Kenshoto Gophers. 
  26      Blackhats Everywhere. 
  27   
  28  """ 
  29  # Copyright (C) 2007 Invisigoth - See LICENSE file for details 
  30  import os 
  31  import re 
  32  import sys 
  33  import code 
  34  import copy 
  35  import time 
  36  import types 
  37  import struct 
  38  import getopt 
  39  import signal 
  40  import inspect 
  41  import platform 
  42  import traceback 
  43   
  44  import cPickle as pickle 
  45   
  46  import envi 
  47  import envi.bits as e_bits 
  48  import envi.memory as e_mem 
  49  import envi.registers as e_reg 
  50  import envi.expression as e_expr 
  51  import envi.resolver as e_resolv 
  52   
  53  import cobra 
  54  import vstruct 
  55   
  56  remote = None       # If set, we're a vtrace client (set to serverhost) 
  57  cobra_daemon = None 
  58  port = 0x5656 
  59  verbose = False 
  60   
  61  # Order must match format junk 
  62  # NOTIFY_ALL is kinda special, if you registerNotifier 
  63  # with it, you get ALL notifications. 
  64  NOTIFY_ALL = 0          # Get all notifications 
  65  NOTIFY_SIGNAL = 1       # Callback on signal/exception 
  66  NOTIFY_BREAK = 2        # Callback on breakpoint / sigtrap 
  67  NOTIFY_STEP = 3         # Callback on singlestep complete 
  68  NOTIFY_SYSCALL = 4      # Callback on syscall (linux only for now) 
  69  NOTIFY_CONTINUE = 5     # Callback on continue (not done for step) 
  70  NOTIFY_EXIT = 6         # Callback on process exit 
  71  NOTIFY_ATTACH = 7       # Callback on successful attach 
  72  NOTIFY_DETACH = 8       # Callback on impending process detach        
  73  # The following notifiers are *only* available on some platforms 
  74  # (and may be kinda faked out ala library load events on posix) 
  75  NOTIFY_LOAD_LIBRARY = 9 
  76  NOTIFY_UNLOAD_LIBRARY = 10  
  77  NOTIFY_CREATE_THREAD = 11 
  78  NOTIFY_EXIT_THREAD = 12 
  79  NOTIFY_DEBUG_PRINT = 13 # Some platforms support this (win32). 
  80  NOTIFY_MAX = 20 
  81   
  82  # File Descriptor / Handle Types 
  83  FD_UNKNOWN = 0 # Unknown or we don't have a type for it 
  84  FD_FILE = 1 
  85  FD_SOCKET = 2 
  86  FD_PIPE = 3 
  87  FD_LOCK = 4   # Win32 Mutant/Lock/Semaphore 
  88  FD_EVENT = 5  # Win32 Event/KeyedEvent 
  89  FD_THREAD = 6 # Win32 Thread 
  90  FD_REGKEY = 7 # Win32 Registry Key 
  91   
  92  # Vtrace Symbol Types 
  93  SYM_MISC = -1 
  94  SYM_GLOBAL = 0 # Global (mostly vars) 
  95  SYM_LOCAL = 1 # Locals 
  96  SYM_FUNCTION = 2 # Functions 
  97  SYM_SECTION = 3 # Binary section 
  98  SYM_META = 4 # Info that we enumerate 
  99   
 100  # Vtrace Symbol Offsets 
 101  VSYM_NAME = 0 
 102  VSYM_ADDR = 1 
 103  VSYM_SIZE = 2 
 104  VSYM_TYPE = 3 
 105  VSYM_FILE = 4 
 106   
 107  from vtrace.rmi import * 
 108  from vtrace.notifiers import * 
 109  from vtrace.breakpoints import * 
 110  from vtrace.watchpoints import * 
 111  import vtrace.util as v_util 
 112   
113 -class PlatformException(Exception):
114 """ 115 A universal way to represent a failure in the 116 platform layer for this tracer. platformFoo methods 117 should raise this rather than allowing their platform 118 specific exception types (which don't likely pickle, or 119 are not cross platform) 120 """ 121 pass
122
123 -class AccessViolation(Exception):
124 """ 125 An exception which is raised on bad-touch to memory 126 """
127 - def __init__(self, va, perm=0):
128 self.va = va 129 self.perm = perm 130 Exception.__init__(self, "AccessViolation at 0x%.8x (%d)" % (va, perm))
131
132 -class Trace(e_mem.IMemory, e_reg.RegisterContext, e_resolv.SymbolResolver, object):
133 """ 134 The main tracer object. A trace instance is dynamically generated using 135 this and *many* potential mixin classes. However, API users should *not* 136 worry about the methods that come from the mixins... Everything that is 137 *meant* to be used from the API is contained and documented here. 138 """
139 - def __init__(self, archname=None):
140 # For the crazy thread-call-proxy-thing 141 # (must come first for __getattribute__ 142 self.requires_thread = {} 143 self.proxymeth = None # FIXME hack for now... 144 self._released = False 145 146 # The universal place for all modes 147 # that might be platform dependant... 148 self.modes = {} 149 self.modedocs = {} 150 self.notifiers = {} 151 152 # For all transient data (if notifiers want 153 # to track stuff per-trace 154 self.metadata = {} 155 156 self.initMode("RunForever", False, "Run until RunForever = False") 157 self.initMode("NonBlocking", False, "A call to wait() fires a thread to wait *for* you") 158 self.initMode("ThreadProxy", True, "Proxy necissary requests through a single thread (can deadlock...)") 159 self.initMode("SingleStep", False, "All calls to run() actually just step. This allows RunForever + SingleStep to step forever ;)") 160 self.initMode("FastStep", False, "All stepi() will NOT generate a step event") 161 162 self.regcache = None 163 self.regcachedirty = False 164 self.sus_threads = {} # A dictionary of suspended threads 165 166 # Set if we're a server and this trace is proxied 167 self.proxy = None 168 169 # Set us up with an envi arch module 170 # FIXME eventually we should just inherit one... 171 if archname == None: 172 archname = envi.getCurrentArch() 173 self.setMeta('Architecture', archname) 174 self.arch = envi.getArchModule(name=archname) 175 176 e_resolv.SymbolResolver.__init__(self, width=self.arch.getPointerSize()) 177 e_mem.IMemory.__init__(self, archmod=self.arch) 178 e_reg.RegisterContext.__init__(self) 179 180 # Add event numbers to here for auto-continue 181 self.auto_continue = [NOTIFY_LOAD_LIBRARY, NOTIFY_CREATE_THREAD, NOTIFY_UNLOAD_LIBRARY, NOTIFY_EXIT_THREAD, NOTIFY_DEBUG_PRINT]
182
183 - def execute(self, cmdline):
184 """ 185 Start a new process and debug it 186 """ 187 if self.isAttached(): 188 raise Exception("ERROR - Tracer must first be detached before you can execute()") 189 190 pid = self.platformExec(cmdline) 191 self._justAttached(pid) 192 self.setMeta('ExecCommand', cmdline) 193 self.wait()
194
195 - def getCurrentSignal(self):
196 ''' 197 Retrieve the current signal/exception posted to the process. 198 If there are no pending signals/exceptions the API will return 199 None. For POSIX systems, this will be a traditional POSIX signal. 200 For Windows systems it will be a current exception code (if any). 201 202 Example: sig = trace.getCurrentSignal() 203 ''' 204 return self.platformGetSignal()
205
206 - def setCurrentSignal(self, sig=None):
207 ''' 208 Set the currently pending signal for delivery to the target process on 209 continue. This is intended for use by programs wishing the mask or change 210 the delivery of exceptions on a NOTIFY_SIGNAL event. 211 212 Example: trace.setCurrentSignal(None) 213 ''' 214 return self.platformSetSignal(sig)
215
216 - def addIgnoreSignal(self, code, address=0):
217 """ 218 By adding an IgnoreSignal you tell the tracer object to 219 supress the notification of a particular type of signal. 220 In POSIX, these are regular signals, in Win32, these 221 are exception codes. This is mostly useful in RunForever 222 mode because you still need the process to begin running again. 223 (these may be viewed/modified by the metadata key "IgnoredSignals") 224 FIXME: make address do something. 225 """ 226 self.getMeta("IgnoredSignals").append(code)
227
228 - def delIgnoreSignal(self, code, address=0):
229 """ 230 See addIgnoreSignal for a description of signal ignoring. 231 This removes an ignored signal and re-enables it's delivery. 232 """ 233 self.getMeta("IgnoredSignals").remove(code)
234
235 - def attach(self, pid):
236 """ 237 Attach to a new process ID. 238 """ 239 if self.isAttached(): 240 self.detach() 241 242 try: 243 self.platformAttach(pid) 244 self._justAttached(pid) 245 self.wait() 246 except Exception, msg: 247 raise PlatformException(str(msg))
248
249 - def stepi(self):
250 """ 251 Single step the target process ONE instruction (and do 252 NOT activate breakpoints for the one step). Also, we 253 don't deliver pending signals for the single step... 254 Use the mode FastStep to allow/supress notifier callbacks on step 255 """ 256 self.requireNotRunning() 257 258 # Since we don't go through the normal run/wait 259 # code, we have a little house-keeping to do... 260 self.curbp = None 261 262 self._syncRegs() 263 self.platformStepi() 264 event = self.platformWait() 265 self.platformProcessEvent(event)
266
267 - def run(self, until=None):
268 """ 269 Allow the traced target to continue execution. (Depending on the mode 270 "Blocking" this will either block until an event, or return immediately) 271 Additionally, the argument until may be used to cause execution to continue 272 until the specified address is reached (internally uses and removes a breakpoint). 273 """ 274 self.requireNotRunning() 275 if self.getMode("SingleStep", False): 276 self.steploop() 277 278 else: 279 if until != None: 280 self.setMode("RunForever", True) 281 self.addBreakpoint(StopAndRemoveBreak(until)) 282 283 self._doRun() 284 self.wait()
285
286 - def runAgain(self, val=True):
287 """ 288 The runAgain() method may be used from inside a notifier 289 (Notifier, Breakpoint, Watchpoint, etc...) to inform the trace 290 that once event processing is complete, it should continue 291 running the trace. 292 """ 293 self.runagain = val
294
295 - def kill(self):
296 """ 297 Kill the target process for this trace (will result in process 298 exit and fire appropriate notifiers) 299 """ 300 self.requireAttached() 301 self.requireNotExited() 302 # kill may require that we continue 303 # the process before it gets processed, 304 # so we'll try to run the process until it 305 # exits due to the kill 306 self.setMode("RunForever", True) # Forever actually means util exit 307 if self.isRunning(): 308 self.platformKill() 309 else: 310 self.platformKill() 311 self.run()
312
313 - def detach(self):
314 """ 315 Detach from the currently attached process. 316 """ 317 self.requireNotRunning() 318 self.fireNotifiers(NOTIFY_DETACH) 319 self._syncRegs() 320 self.platformDetach() 321 self.attached = False 322 self.pid = 0 323 self.mapcache = None
324
325 - def release(self):
326 ''' 327 Release resources for this tracer. This API should be called 328 once you are done with the trace. 329 ''' 330 if not self._released: 331 self._released = True 332 if self.attached: 333 self.detach() 334 self._cleanupResources() 335 self.platformRelease()
336
337 - def getPid(self):
338 """ 339 Return the pid for this Trace 340 """ 341 return self.pid
342
343 - def getNormalizedLibNames(self):
344 """ 345 Symbols are stored internally based off of 346 "normalized" library names. This method returns 347 the list of normalized names for the loaded libraries. 348 349 (probably only useful for writting symbol browsers...) 350 """ 351 return self.getMeta("LibraryBases").keys()
352
353 - def getSymsForFile(self, libname):
354 """ 355 Return the entire symbol list for the specified 356 normalized library name. 357 """ 358 self._loadBinaryNorm(libname) 359 sym = self.getSymByName(libname) 360 if sym == None: 361 raise Exception('Invalid Library Name: %s' % libname) 362 return sym.getSymList()
363
364 - def getSymByAddr(self, addr, exact=True):
365 """ 366 Return an envi Symbol object for an address. 367 Use exact=False to get the nearest previous match. 368 """ 369 # NOTE: Override this from envi.SymbolResolver to do on-demand 370 # file parsing. 371 372 r = e_resolv.SymbolResolver.getSymByAddr(self, addr, exact=exact) 373 if r != None: 374 return r 375 376 # See if we need to parse the file. 377 map = self.getMemoryMap(addr) 378 if map == None: 379 return None 380 381 va,size,perms,fname = map 382 383 if not self._loadBinary(fname): 384 return None 385 386 # Take a second shot after parsing 387 return e_resolv.SymbolResolver.getSymByAddr(self, addr, exact=exact)
388
389 - def getSymByName(self, name):
390 """ 391 Return an envi.Symbol object for the given name (or None) 392 """ 393 self._loadBinaryNorm(name) 394 return e_resolv.SymbolResolver.getSymByName(self, name)
395
396 - def searchSymbols(self, regex, libname=None):
397 ''' 398 Search for symbols which match the given regular expression. Specify libname 399 as the "normalized" library name to only search the specified lib. 400 401 Example: for sym in trace.searchSymbols('.*CreateFile.*', 'kernel32'): 402 ''' 403 reobj = re.compile(regex) 404 if libname != None: 405 libs = [libname, ] 406 else: 407 libs = self.getNormalizedLibNames() 408 409 ret = [] 410 for lname in libs: 411 for sym in self.getSymsForFile(lname): 412 symstr = str(sym) 413 if reobj.match(symstr): 414 ret.append(sym) 415 return ret
416 417
418 - def getRegisterContext(self, threadid=None):
419 """ 420 Retrieve the envi.registers.RegisterContext object for the 421 specified thread. Use this API to iterate over threads 422 register values without setting the global tracer thread context. 423 """ 424 if threadid == None: 425 threadid = self.getMeta("ThreadId") 426 return self._cacheRegs(threadid)
427 428 ####################################################################### 429 # 430 # We mirror the RegisterContext API using our own thread index based 431 # cache. These APIs must stay in sync with envi.registers.RegisterContext 432 # NOTE: for now we only need to over-ride get/setRegister because all the 433 # higher level APIs call them. 434 # 435
436 - def getRegister(self, idx):
437 ctx = self.getRegisterContext() 438 return ctx.getRegister(idx)
439
440 - def setRegister(self, idx, value):
441 ctx = self.getRegisterContext() 442 ctx.setRegister(idx, value)
443 444 ####################################################################### 445
446 - def allocateMemory(self, size, perms=e_mem.MM_RWX, suggestaddr=0):
447 """ 448 Allocate a chunk of memory inside the target process' address 449 space. Memory wil be mapped rwx unless otherwise specified with 450 perms=envi.memory.MM_FOO values. Optionally you may *suggest* an address 451 to the allocator, but there is no guarentee. Returns the mapped 452 memory address. 453 """ 454 self.requireNotRunning() 455 self.mapcache = None # We may have a new memory map 456 return self.platformAllocateMemory(size, perms=perms, suggestaddr=suggestaddr)
457
458 - def protectMemory(self, va, size, perms):
459 """ 460 Change the page protections on the specified region of memory. 461 See envi.memory for perms values. 462 """ 463 self.requireNotRunning() 464 self.mapcache = None # We may have new memory protections 465 return self.platformProtectMemory(va, size, perms)
466
467 - def readMemory(self, address, size):
468 """ 469 Read memory from address. Areas that are NOT valid memory will be read 470 back as \x00s (this probably goes in a mixin soon) 471 """ 472 self.requireNotRunning() 473 return self.platformReadMemory(long(address), long(size))
474
475 - def writeMemory(self, address, bytes):
476 """ 477 Write the given bytes to the address in the current trace. 478 """ 479 self.requireNotRunning() 480 self.platformWriteMemory(long(address), bytes)
481
482 - def searchMemory(self, needle, regex=False):
483 """ 484 Search all of process memory for a sequence of bytes. 485 """ 486 ret = e_mem.IMemory.searchMemory(self, needle, regex=regex) 487 self.setMeta('search', ret) 488 self.setVariable('search', ret) 489 return ret
490
491 - def searchMemoryRange(self, needle, address, size, regex=False):
492 """ 493 Search a memory range for the specified sequence of bytes 494 """ 495 ret = e_mem.IMemory.searchMemoryRange(self, needle, address, size, regex=regex) 496 self.setMeta('search', ret) 497 self.setVariable('search', ret) 498 return ret
499
500 - def setMeta(self, name, value):
501 """ 502 Set some metadata. Metadata is a clean way for 503 arbitrary trace consumers (and notifiers) to present 504 and track additional information in trace objects. 505 506 Any modules which use this *should* initialize them 507 on attach (so when they get re-used they're clean) 508 509 Some examples of metadata used: 510 ShouldBreak - We're expecting a non-signal related break 511 ExitCode - The int() exit code (if exited) 512 PendingSignal - The current signal 513 514 """ 515 self.metadata[name] = value
516
517 - def getMeta(self, name, default=None):
518 """ 519 Get some metadata. Metadata is a clean way for 520 arbitrary trace consumers (and notifiers) to present 521 and track additional information in trace objects. 522 523 If you specify a default and the key doesn't exist, not 524 not only will the default be returned, but the key will 525 be set to the default specified. 526 """ 527 if default: 528 if not self.metadata.has_key(name): 529 self.metadata[name] = default 530 return self.metadata.get(name, None)
531
532 - def hasMeta(self, name):
533 """ 534 Check to see if a metadata key exists... Mostly un-necissary 535 as getMeta() with a default will set the key to the default 536 if non-existant. 537 """ 538 return self.metadata.has_key(name)
539
540 - def getMode(self, name, default=False):
541 """ 542 Get the value for a mode setting allowing 543 for a clean default... 544 """ 545 return self.modes.get(name, default)
546
547 - def setMode(self, name, value):
548 """ 549 Set a mode setting... This is ONLY valid 550 if that mode has been iniitialized with 551 initMode(name, value). Otherwise, it's an 552 unsupported mode for this platform ;) cute huh? 553 This way, platform sections can cleanly setmodes 554 and such. 555 """ 556 if not self.modes.has_key(name): 557 raise Exception("Mode %s not supported on this platform" % name) 558 self.modes[name] = bool(value)
559
560 - def injectso(self, filename):
561 """ 562 Inject a shared object into the target of the trace. So, on windows 563 this is easy with InjectDll and on *nix... it's.. fugly... 564 565 NOTE: This method will likely cause the trace to run. Do not call from 566 within a notifier! 567 """ 568 self.requireNotRunning() 569 self.platformInjectSo(filename)
570
571 - def ps(self):
572 """ 573 Return a list of proccesses which are currently running on the 574 system. 575 (pid, name) 576 """ 577 return self.platformPs()
578
579 - def addBreakByExpr(self, symname, fastbreak=False):
580 ''' 581 Add a breakpoint by resolving an expression. This will create 582 the Breakpoint object for you and add it to the trace. It 583 returns the newly created breakpoint id. 584 585 Optionally, set fastbreak=True to have the breakpoint behave in 586 "fast break" mode which automatically continues execution and does 587 not fire notifiers for the breakpoint. 588 589 Example: trace.addBreakByExpr('kernel32.CreateFileA + ecx') 590 ''' 591 bp = Breakpoint(None, expression=symname) 592 bp.fastbreak = fastbreak 593 return self.addBreakpoint(bp)
594
595 - def addBreakByAddr(self, va, fastbreak=False):
596 ''' 597 Add a breakpoint by address. This will create the Breakpoint 598 object for you and add it to the trace. It returns the newly 599 created breakpoint id. 600 601 Optionally, set fastbreak=True to have the breakpoint behave in 602 "fast break" mode which automatically continues execution and does 603 not fire notifiers for the breakpoint. 604 605 Example: trace.addBreakByAddr(0x7c770308) 606 ''' 607 bp = Breakpoint(va) 608 bp.fastbreak = fastbreak 609 return self.addBreakpoint(bp)
610
611 - def addBreakpoint(self, breakpoint):
612 """ 613 Add a breakpoint/watchpoint to the trace. The "breakpoint" argument 614 is a vtrace Breakpoint/Watchpoint object or something that extends it. 615 616 To add a basic breakpoint use trace.addBreakpoint(vtrace.Breakpoint(address)) 617 NOTE: expression breakpoints do *not* get evaluated in fastbreak mode 618 619 This will return the internal ID given to the new breakpoint 620 """ 621 breakpoint.inittrace(self) 622 623 breakpoint.id = self.nextBpId() 624 addr = breakpoint.resolveAddress(self) 625 626 627 if addr == None: 628 self.bpbyid[breakpoint.id] = breakpoint 629 self.deferred.append(breakpoint) 630 return breakpoint.id 631 632 if self.breakpoints.has_key(addr): 633 raise Exception("ERROR: Duplicate break for address 0x%.8x" % addr) 634 635 self.bpbyid[breakpoint.id] = breakpoint 636 self.breakpoints[addr] = breakpoint 637 638 # fastbreaks are always active... (except when they're not...) 639 if breakpoint.fastbreak: 640 self._activateBreak(breakpoint) 641 642 return breakpoint.id
643
644 - def removeBreakpoint(self, id):
645 """ 646 Remove the breakpoint with the specified ID 647 """ 648 self.requireAttached() 649 bp = self.bpbyid.pop(id, None) 650 if bp != None: 651 bp.deactivate(self) 652 if bp in self.deferred: 653 self.deferred.remove(bp) 654 else: 655 self.breakpoints.pop(bp.address, None) 656 657 # If the bp is also curbp, set curbp to None 658 if self.curbp == bp: 659 self.curbp = None 660 661 # Remove cached breakpoint code 662 Breakpoint.bpcodeobj.pop(id, None)
663
664 - def getCurrentBreakpoint(self):
665 """ 666 Return the current breakpoint otherwise None 667 """ 668 return self.curbp
669
670 - def getBreakpoint(self,id):
671 """ 672 Return a reference to the breakpoint with the requested ID. 673 674 NOTE: NEVER set locals or use things like setBreakpointCode() 675 method on return'd breakpoint objects as they may be remote 676 and would then be *coppies* of the bp objects. (use the trace's 677 setBreakpointCode() instead). 678 """ 679 return self.bpbyid.get(id)
680
681 - def getBreakpointByAddr(self, va):
682 ''' 683 Return the breakpoint object (or None) for a given virtual address. 684 ''' 685 return self.breakpoints.get(va)
686
687 - def getBreakpoints(self):
688 """ 689 Return a list of the current breakpoints. 690 """ 691 return self.bpbyid.values()
692
693 - def getBreakpointEnabled(self, bpid):
694 """ 695 An accessor method for returning if a breakpoint is 696 currently enabled. 697 NOTE: code which wants to be remote-safe should use this 698 """ 699 bp = self.getBreakpoint(bpid) 700 if bp == None: 701 raise Exception("Breakpoint %d Not Found" % bpid) 702 return bp.isEnabled()
703
704 - def setBreakpointEnabled(self, bpid, enabled=True):
705 """ 706 An accessor method for setting a breakpoint enabled/disabled. 707 708 NOTE: code which wants to be remote-safe should use this 709 """ 710 bp = self.getBreakpoint(bpid) 711 if bp == None: 712 raise Exception("Breakpoint %d Not Found" % bpid) 713 if not enabled: # To catch the "disable" of fastbreaks... 714 bp.deactivate(self) 715 return bp.setEnabled(enabled)
716
717 - def setBreakpointCode(self, bpid, pystr):
718 """ 719 Because breakpoints are potentially on the remote debugger 720 and code is not pickleable in python, special access methods 721 which takes strings of python code are necissary for the 722 vdb interface to quick script breakpoint code. Use this method 723 to set the python code for this breakpoint. 724 """ 725 bp = self.getBreakpoint(bpid) 726 if bp == None: 727 raise Exception("Breakpoint %d Not Found" % bpid) 728 bp.setBreakpointCode(pystr)
729
730 - def getBreakpointCode(self, bpid):
731 """ 732 Return the python string of user specified code that will run 733 when this breakpoint is hit. 734 """ 735 bp = self.getBreakpoint(bpid) 736 if bp != None: 737 return bp.getBreakpointCode() 738 return None
739
740 - def call(self, address, args, convention=None):
741 """ 742 Setup the "stack" and call the target address with the following 743 arguments. If the argument is a string or a buffer, copy that into 744 memory and hand in the argument. 745 746 The current state of ALL registers are returned as a dictionary at the 747 end of the call... 748 749 Additionally, a "convention" string may be specified that the underlying 750 platform may be able to interpret... 751 """ 752 self.requireNotRunning() 753 return self.platformCall(address, args, convention)
754
755 - def registerNotifier(self, event, notifier):
756 """ 757 Register a notifier who will be called for various 758 events. See NOTIFY_* constants for handler hooks. 759 """ 760 nlist = self.notifiers.get(event,None) 761 if nlist: 762 nlist.append(notifier) 763 else: 764 nlist = [] 765 nlist.append(notifier) 766 self.notifiers[event] = nlist
767
768 - def deregisterNotifier(self, event, notifier):
769 nlist = self.notifiers.get(event, []) 770 if notifier in nlist: 771 nlist.remove(notifier)
772
773 - def getNotifiers(self, event):
774 return self.notifiers.get(event, [])
775
776 - def requireNotExited(self):
777 if self.exited: 778 raise Exception("ERROR - Request invalid for trace which exited")
779
780 - def requireNotRunning(self):
781 """ 782 Just a quick method to throw an error if the 783 tracer is already running... 784 """ 785 self.requireAttached() 786 if self.isRunning(): 787 raise Exception("ERROR - Request invalid for running trace")
788
789 - def requireAttached(self):
790 """ 791 A utility method for other methods to use in order 792 to require being attached 793 """ 794 if not self.attached: 795 raise Exception("ERROR - Must be attached to a process")
796
797 - def getFds(self):
798 """ 799 Get a list of (fd,type,bestname) pairs. This is MOSTLY useful 800 for HUMON consumtion... or giving HUMONs consumption... 801 """ 802 self.requireNotRunning() 803 if not self.fds: 804 self.fds = self.platformGetFds() 805 return self.fds
806
807 - def getMemoryMaps(self):
808 """ 809 Return a list of the currently mapped memory for the target 810 process. This is acomplished by calling the platform's 811 platformGetMaps() mixin method. This will also cache the 812 results until CONTINUE. The format is (addr,len,perms,file). 813 """ 814 self.requireNotRunning() 815 if not self.mapcache: 816 self.mapcache = self.platformGetMaps() 817 return self.mapcache
818
819 - def getMemoryFault(self):
820 ''' 821 If the most receent event is a memory access error, this API will 822 return a tuple of (<addr>,<perm>) on supported platforms. Otherwise, 823 a (None, None) will result. 824 825 Example: 826 import envi.memory as e_mem 827 vaddr,vperm = trace.getMemoryFault() 828 if vaddr != None: 829 print 'Memory Fault At: 0x%.8x (perm: %d)' % (vaddr, vperm) 830 ''' 831 return self.platformGetMemFault()
832
833 - def isAttached(self):
834 """ 835 Return boolean true/false for weather or not this trace is 836 currently attached to a process. 837 """ 838 return self.attached
839
840 - def isRunning(self):
841 """ 842 Return true or false if this trace's target process is "running". 843 """ 844 return self.running
845
846 - def isRemote(self):
847 ''' 848 Returns True if the trace is a CobraProxy object to a trace on another system 849 ''' 850 return False
851
852 - def enableAutoContinue(self, event):
853 """ 854 Put the tracer object in to AutoContinue mode 855 for the specified event. To make all events 856 continue running see RunForever mode in setMode(). 857 """ 858 if event not in self.auto_continue: 859 self.auto_continue.append(event)
860
861 - def disableAutoContinue(self, event):
862 """ 863 Disable Auto Continue for the specified 864 event. 865 """ 866 if event in self.auto_continue: 867 self.auto_continue.remove(event)
868
869 - def getAutoContinueList(self):
870 """ 871 Retrieve the list of vtrace notification events 872 that will be auto-continued. 873 """ 874 return list(self.auto_continue)
875
876 - def parseExpression(self, expression):
877 """ 878 Parse a python expression with many useful helpers mapped 879 into the execution namespace. 880 881 Example: trace.parseExpression("ispoi(ecx+ntdll.RtlAllocateHeap)") 882 """ 883 locs = VtraceExpressionLocals(self) 884 return long(e_expr.evaluate(expression, locs))
885
886 - def sendBreak(self):
887 """ 888 Send an asynchronous break signal to the target process. 889 This is only valid if the target is actually running... 890 """ 891 self.requireAttached() 892 self.setMode("RunForever", False) 893 self.setMeta("ShouldBreak", True) 894 self.platformSendBreak() 895 time.sleep(0.01) 896 # If we're non-blocking, we gotta wait... 897 if self.getMode("NonBlocking", True): 898 while self.isRunning(): 899 time.sleep(0.01)
900
901 - def getStackTrace(self):
902 """ 903 Returns a list of (instruction pointer, stack frame) tuples. 904 If stack tracing results in an error, the error entry will 905 be (-1,-1). Otherwise most platforms end up with 0,0 as 906 the top stack frame 907 """ 908 # FIXME thread id argument! 909 return self.archGetStackTrace()
910
911 - def getThreads(self):
912 """ 913 Get a dictionary of <threadid>:<tinfo> pairs where 914 tinfo is platform dependant, but is tyically either 915 the top of the stack for that thread, or the TEB on 916 win32 917 """ 918 if not self.threadcache: 919 self.threadcache = self.platformGetThreads() 920 return self.threadcache
921
922 - def getCurrentThread(self):
923 ''' 924 Return the thread id of the currently selected thread. 925 ''' 926 return self.getMeta('ThreadId')
927
928 - def selectThread(self, threadid):
929 """ 930 Set the "current thread" context to the given thread id. 931 (For example stack traces and register values will depend 932 on the current thread context). By default the thread 933 responsible for an "interesting event" is selected. 934 """ 935 if threadid not in self.getThreads(): 936 raise Exception("ERROR: Invalid threadid chosen: %d" % threadid) 937 self.requireNotRunning() 938 self.platformSelectThread(threadid) 939 self.setMeta("ThreadId", threadid)
940
941 - def isThreadSuspended(self, threadid):
942 """ 943 Used to determine if a thread is suspended. 944 """ 945 return self.sus_threads.get(threadid, False)
946
947 - def suspendThread(self, threadid):
948 """ 949 Suspend a thread by ID. This will mean that on continuing 950 the trace, the suspended thread will not be scheduled. 951 """ 952 self.requireNotRunning() 953 if self.sus_threads.get(threadid): 954 raise Exception("The specified thread is already suspended") 955 if threadid not in self.getThreads().keys(): 956 raise Exception("There is no thread %d!" % threadid) 957 self.platformSuspendThread(threadid) 958 self.sus_threads[threadid] = True
959
960 - def resumeThread(self, threadid):
961 """ 962 Resume a suspended thread. 963 """ 964 self.requireNotRunning() 965 if not self.sus_threads.get(threadid): 966 raise Exception("The specified thread is not suspended") 967 self.platformResumeThread(threadid) 968 self.sus_threads.pop(threadid)
969
970 - def injectThread(self, pc):
971 """ 972 Create a new thread inside the target process. This thread 973 will begin execution on the next process run(). 974 """ 975 self.requireNotRunning() 976 #self.platformInjectThread(pc) 977 pass
978
979 - def joinThread(self, threadid):
980 ''' 981 Run the trace in a loop until the specified thread exits. 982 ''' 983 self.setMode('RunForever', True) 984 self._join_thread = threadid 985 # Temporarily make run/wait blocking 986 nb = self.getMode('NonBlocking') 987 self.setMode('NonBlocking', False) 988 self.run() 989 self.setMode('NonBlocking', nb)
990
991 - def getStructNames(self, namespace=None):
992 ''' 993 This method returns either the structure names, or 994 the structure namespaces that the target tracer is aware 995 of. If "namespace" is specified, it is structures within 996 that namespace, otherwise it is "known namespaces" 997 998 Example: namespaces = trace.getStructNames() 999 ntdll_structs = trace.getStructNames(namespace='ntdll') 1000 ''' 1001 if namespace: 1002 return self.vsbuilder.getVStructNames(namespace=namespace) 1003 return self.vsbuilder.getVStructNamespaceNames()
1004
1005 - def getStruct(self, sname, address):
1006 """ 1007 Retrieve a vstruct structure populated with memory from 1008 the specified address. Returns a standard vstruct object. 1009 """ 1010 # Check if we need to parse symbols for a library 1011 libbase = sname.split('.')[0] 1012 self._loadBinaryNorm(libbase) 1013 1014 if self.vsbuilder.hasVStructNamespace(libbase): 1015 vs = self.vsbuilder.buildVStruct(sname) 1016 1017 # FIXME this is depreicated and should die... 1018 else: 1019 vs = vstruct.getStructure(sname) 1020 bytes = self.readMemory(address, len(vs)) 1021 vs.vsParse(bytes) 1022 return vs
1023
1024 - def setVariable(self, name, value):
1025 """ 1026 Set a named variable in the trace which may be used in 1027 subsequent VtraceExpressions. 1028 1029 Example: 1030 trace.setVariable("whereiam", trace.getProgramCounter()) 1031 """ 1032 self.localvars[name] = value
1033
1034 - def getVariable(self, name):
1035 """ 1036 Get the value of a previously set variable name. 1037 (or None on not found) 1038 """ 1039 return self.localvars.get(name)
1040
1041 - def getVariables(self):
1042 """ 1043 Get the dictionary of named variables. 1044 """ 1045 return dict(self.localvars)
1046
1047 - def hex(self, value):
1048 """ 1049 Much like the python hex routine, except this will automatically 1050 pad the value's string length out to pointer width. 1051 """ 1052 w = self.arch.getPointerSize() 1053 return e_bits.hex(value, width)
1054
1055 - def buildNewTrace(self):
1056 ''' 1057 Build a new/clean trace "like" this one. For platforms where a 1058 special trace was handed in, this allows initialization of a new one. 1059 For most implementations, this is very simple.... 1060 1061 Example: 1062 if need_another_trace: 1063 newt = trace.buildNewTrace() 1064 ''' 1065 return self.__class__()
1066
1067 -class TraceGroup(Notifier, v_util.TraceManager):
1068 """ 1069 Encapsulate several traces, run them, and continue to 1070 handle their event notifications. 1071 """
1072 - def __init__(self):
1073 Notifier.__init__(self) 1074 v_util.TraceManager.__init__(self) 1075 self.traces = {} 1076 self.go = True # A little ghetto switch for those who read the source 1077 1078 # We are a notify all notifier by default 1079 self.registerNotifier(NOTIFY_ALL, self) 1080 1081 self.setMode("NonBlocking", True)
1082
1083 - def setMeta(self, name, value):
1084 """ 1085 A trace group's setMeta function will set "persistant" metadata 1086 which will be added again to any trace on attach. Additionally, 1087 setting metadata on a tracegroup will cause all current traces 1088 to get the update as well.... 1089 """ 1090 v_util.TraceManager.setMeta(self,name,value) 1091 for trace in self.traces.values(): 1092 trace.setMeta(name, value)
1093
1094 - def setMode(self, name, value):
1095 v_util.TraceManager.setMode(self, name, value) 1096 for trace in self.getTraces(): 1097 trace.setMode(name, value)
1098
1099 - def detachAll(self):
1100 """ 1101 Detach from ALL the currently targetd processes 1102 """ 1103 for trace in self.traces.values(): 1104 try: 1105 if trace.isRunning(): 1106 trace.sendBreak() 1107 trace.detach() 1108 except: 1109 pass
1110
1111 - def run(self):
1112 """ 1113 Our run method is a little different than a traditional 1114 trace. It will *never* block. 1115 """ 1116 if len(self.traces.keys()) == 0: 1117 raise Exception("ERROR - can't run() with no traces!") 1118 1119 for trace in self.traces.values(): 1120 1121 if trace.exited: 1122 self.traces.pop(trace.pid) 1123 trace.detach() 1124 continue 1125 1126 if not trace.isRunning(): 1127 trace.run()
1128
1129 - def execTrace(self, cmdline):
1130 trace = getTrace() 1131 self._initTrace(trace) 1132 trace.execute(cmdline) 1133 self.traces[trace.getPid()] = trace 1134 return trace
1135
1136 - def addTrace(self, proc):
1137 """ 1138 Add a new tracer to this group the "proc" argument 1139 may be either an long() for a pid (which we will attach 1140 to) or an already attached (and broken) tracer object. 1141 """ 1142 1143 if (type(proc) == types.IntType or 1144 type(proc) == types.LongType): 1145 trace = getTrace() 1146 self._initTrace(trace) 1147 self.traces[proc] = trace 1148 try: 1149 trace.attach(proc) 1150 except: 1151 self.delTrace(proc) 1152 raise 1153 1154 else: # Hopefully a tracer object... if not.. you're dumb. 1155 trace = proc 1156 self._initTrace(trace) 1157 self.traces[trace.getPid()] = trace 1158 1159 return trace
1160
1161 - def getTrace(self):
1162 ''' 1163 Similar to vtrace.getTrace(), but also init's 1164 the trace for being managed by a TraceGroup. 1165 1166 Example: 1167 tg = TraceGroup() 1168 t = tg.getTrace() 1169 t.... 1170 ''' 1171 t = getTrace() 1172 self.addTrace(t) 1173 return t
1174
1175 - def _initTrace(self, trace):
1176 """ 1177 - INTERNAL - 1178 Setup a tracer object to be ready for being in this 1179 trace group (setup modes and notifiers). Only addTrace() 1180 and execTrace() probably need to be aware of this. 1181 """ 1182 self.manageTrace(trace)
1183
1184 - def delTrace(self, pid):
1185 """ 1186 Remove a trace from the current TraceGroup 1187 """ 1188 trace = self.traces.pop(pid, None) 1189 self.unManageTrace(trace)
1190
1191 - def getTraces(self):
1192 """ 1193 Return a list of the current traces 1194 """ 1195 return self.traces.values()
1196
1197 - def getTraceByPid(self, pid):
1198 """ 1199 Return the the trace for process PID if we're 1200 already attached. Return None if not. 1201 """ 1202 return self.traces.get(pid, None)
1203
1204 - def notify(self, event, trace):
1205 # Remove this trace, and free it 1206 # on the server if present 1207 if event == NOTIFY_EXIT: 1208 self.delTrace(trace.getPid())
1209
1210 -class VtraceExpressionLocals(e_expr.MemoryExpressionLocals):
1211 """ 1212 A class which serves as the namespace dictionary during the 1213 evaluation of an expression on a tracer. 1214 """
1215 - def __init__(self, trace):
1216 e_expr.MemoryExpressionLocals.__init__(self, trace, symobj=trace) 1217 self.trace = trace 1218 self.update({ 1219 'trace':trace, 1220 'vtrace':vtrace 1221 }) 1222 self.update({ 1223 'frame':self.frame, 1224 'teb':self.teb, 1225 'bp':self.bp, 1226 'meta':self.meta, 1227 'go':self.go, 1228 })
1229
1230 - def __getitem__(self, name):
1231 # Check registers 1232 if self.trace.isAttached(): 1233 self.trace.requireNotRunning() 1234 1235 regs = self.trace.getRegisters() 1236 r = regs.get(name, None) 1237 if r != None: 1238 return r 1239 1240 # Check local variables 1241 locs = self.trace.getVariables() 1242 r = locs.get(name, None) 1243 if r != None: 1244 return r 1245 return e_expr.MemoryExpressionLocals.__getitem__(self, name)
1246
1247 - def go(self):
1248 ''' 1249 A shortcut for trace.runAgain() which may be used in 1250 breakpoint code (or similar even processors) to begin 1251 execution again after event processing... 1252 ''' 1253 self.trace.runAgain();
1254
1255 - def frame(self, index):
1256 """ 1257 Return the address of the saved base pointer for 1258 the specified frame. 1259 1260 Usage: frame(<index>) 1261 """ 1262 stack = self.trace.getStackTrace() 1263 return stack[index][1]
1264
1265 - def teb(self, threadnum=None):
1266 """ 1267 The expression teb(threadid) will return whatever the 1268 platform stores as the int for threadid. In the case 1269 of windows, this is the TEB, others may be the thread 1270 stack base or whatever. If threadid is left out, it 1271 uses the threadid of the current thread context. 1272 """ 1273 if threadnum == None: 1274 # Get the thread ID of the current Thread Context 1275 threadnum = self.trace.getMeta("ThreadId") 1276 1277 teb = self.trace.getThreads().get(threadnum, None) 1278 if teb == None: 1279 raise Exception("ERROR - Unknown Thread Id %d" % threadnum) 1280 1281 return teb
1282
1283 - def bp(self, bpid):
1284 """ 1285 The expression bp(0) returns the resolved address of the given 1286 breakpoint 1287 """ 1288 bp = self.trace.getBreakpoint(bpid) 1289 if bp == None: 1290 raise Exception("Unknown Breakpoint ID: %d" % bpid) 1291 return bp.resolveAddress(self.trace)
1292
1293 - def meta(self, name):
1294 """ 1295 An expression friendly (terse) way to get trace metadata 1296 (equiv to trace.getMeta(name)) 1297 1298 Example: meta("foo") 1299 """ 1300 return self.trace.getMeta(name)
1301
1302 -def getTrace(plat=None, **kwargs):
1303 """ 1304 Return a tracer object appropriate for this platform. 1305 This is the function you will use to get a tracer object 1306 with the appropriate ancestry for your host. 1307 ex. mytrace = vtrace.getTrace() 1308 1309 1310 NOTE: Use the release() method on the tracer once debugging 1311 is complete. This releases the tracer thread and allows 1312 garbage collection to function correctly. 1313 1314 Some specialized tracers may be constructed by specifying the "plat" 1315 name from one of the following list. Additionally, each "specialized" 1316 tracer may require additional kwargs (which are listed). 1317 1318 android - Debug android apps through adb (adb must be in your path) 1319 avd=<name> (None will let adb decide) 1320 1321 vmware32 - Debug a 32bit VMWare target. 1322 host=<host> - Where is the gdb server listening? (default 127.0.0.1) 1323 port=<port> - What port (default: 8832) 1324 os=<osname> - On of "Windows", "Linux" (that's all we support now...) 1325 1326 vmware64 - Debug a 64bit VMWare target. 1327 host=<host> - Where is the gdb server listening? (default 127.0.0.1) 1328 port=<port> - What port (default: 8864) 1329 os=<osname> - On of "Windows", "Linux" (that's all we support now...) 1330 1331 Examples: 1332 t = vtrace.getTrace() # A tracer for *this* os 1333 1334 t = vtrace.getTrace(plat='android') # The default ADB device 1335 1336 t = vtrace.getTrace(plat='vmware32', host='localhost', port=31337) 1337 """ 1338 # FIXME make "remote" traces use plat="remote"! 1339 if plat == 'android': 1340 import vtrace.platforms.android as v_android 1341 return v_android.getTrace(**kwargs) 1342 1343 if remote: #We have a remote server! 1344 return getRemoteTrace() 1345 1346 # From here down, we're trying to build a trace for *this* platform! 1347 1348 os_name = platform.system() # Like "Linux", "Darwin","Windows" 1349 arch = envi.getCurrentArch() 1350 1351 if os_name == "Linux": 1352 import vtrace.platforms.linux as v_linux 1353 if arch == "amd64": 1354 return v_linux.LinuxAmd64Trace() 1355 1356 elif arch == "i386": 1357 return v_linux.Linuxi386Trace() 1358 1359 else: 1360 raise Exception("Sorry, no linux support for %s" % arch) 1361 1362 elif os_name == "FreeBSD": 1363 1364 import vtrace.platforms.freebsd as v_freebsd 1365 1366 if arch == "i386": 1367 return v_freebsd.FreeBSDi386Trace() 1368 1369 elif arch == "amd64": 1370 return v_freebsd.FreeBSDAmd64Trace() 1371 1372 else: 1373 raise Exception("Sorry, no FreeBSD support for %s" % arch) 1374 1375 #import vtrace.platforms.posix as v_posix 1376 #import vtrace.platforms.freebsd as v_freebsd 1377 #ilist.append(v_posix.PosixMixin) 1378 #ilist.append(v_posix.ElfMixin) 1379 #if arch == "i386": 1380 #import vtrace.archs.intel as v_intel 1381 #ilist.append(v_intel.i386Mixin) 1382 #ilist.append(v_freebsd.FreeBSDMixin) 1383 #ilist.append(v_freebsd.FreeBSDIntelRegisters) 1384 #else: 1385 #raise Exception("Sorry, no FreeBSD support for %s" % arch) 1386 1387 elif os_name == "sunos5": 1388 raise Exception("Solaris needs porting!") 1389 #import vtrace.platforms.posix as v_posix 1390 #import vtrace.platforms.solaris as v_solaris 1391 #ilist.append(v_posix.PosixMixin) 1392 #if arch == "i386": 1393 #import vtrace.archs.intel as v_intel 1394 #ilist.append(v_intel.i386Mixin) 1395 #ilist.append(v_solaris.SolarisMixin) 1396 #ilist.append(v_solaris.Solarisi386Mixin) 1397 1398 elif os_name == "Darwin": 1399 1400 #if 9 not in os.getgroups(): 1401 #print 'You MUST be in the procmod group....' 1402 #print 'Use: sudo dscl . append /Groups/procmod GroupMembership invisigoth' 1403 #print '(put your username in there unless you want to put me in too... ;)' 1404 #raise Exception('procmod group membership required') 1405 if os.getuid() != 0: 1406 print 'For NOW you *must* be root. There are some crazy MACH perms...' 1407 raise Exception('You must be root for now (on OSX)....') 1408 1409 print 'Also... the darwin port is not even REMOTELY working yet. Solid progress though...' 1410 1411 #'sudo dscl . append /Groups/procmod GroupMembership invisigoth' 1412 #'sudo dscl . read /Groups/procmod GroupMembership' 1413 import vtrace.platforms.darwin as v_darwin 1414 if arch == 'i386': 1415 return v_darwin.Darwini386Trace() 1416 elif arch == 'amd64': 1417 return v_darwin.DarwinAmd64Trace() 1418 else: 1419 raise Exception('Darwin not supported on %s (only i386...)' % arch) 1420 1421 elif os_name in ['Microsoft', 'Windows']: 1422 1423 import vtrace.platforms.win32 as v_win32 1424 1425 if arch == "i386": 1426 return v_win32.Windowsi386Trace() 1427 1428 elif arch == "amd64": 1429 return v_win32.WindowsAmd64Trace() 1430 1431 else: 1432 raise Exception("Windows with arch %s is not supported!" % arch) 1433 1434 else: 1435 1436 raise Exception("ERROR - OS %s not supported yet" % os_name)
1437
1438 -def interact(pid=0,server=None,trace=None):
1439 1440 """ 1441 Just a cute and dirty way to get a tracer attached to a pid 1442 and get a python interpreter instance out of it. 1443 """ 1444 1445 global remote 1446 remote = server 1447 1448 if trace == None: 1449 trace = getTrace() 1450 if pid: 1451 trace.attach(pid) 1452 1453 mylocals = {} 1454 mylocals["trace"] = trace 1455 1456 code.interact(local=mylocals)
1457