1 import os
2 import re
3 import sys
4 import pprint
5 import signal
6 import string
7 import traceback
8
9 from ConfigParser import *
10
11 from cmd import *
12 from struct import *
13 from getopt import getopt
14 from UserDict import *
15 from threading import *
16
17 import vtrace
18 import vtrace.util as v_util
19 import vtrace.snapshot as vs_snap
20 import vtrace.notifiers as v_notif
21
22 import vdb
23 import vdb.stalker as v_stalker
24 import vdb.extensions as v_ext
25
26 import envi
27 import envi.cli as e_cli
28 import envi.bits as e_bits
29 import envi.memory as e_mem
30 import envi.config as e_config
31 import envi.resolver as e_resolv
32 import envi.memcanvas as e_canvas
33
34 import vstruct
35 import vstruct.primitives as vs_prims
36
37 vdb.basepath = vdb.__path__[0] + "/"
38
48
51 Thread.__init__(self)
52 self.setDaemon(True)
53 self.cobj = cobj
54 self.locals = locals
55
57 try:
58 exec(self.cobj, self.locals)
59 except Exception, e:
60 traceback.print_exc()
61 print "Script Error: ",e
62
64 """
65 Used to hand thing that need a persistant reference to a trace
66 when using vdb to manage tracers.
67 """
70
75
76
79
82
83
84
90
93
94 defconfig = """
95 [Vdb]
96
97 [RegisterView]
98 i386=eax,ebx,ecx,edx,esi,edi,eip,esp,ebp,eflags,ds,es,cs,fs,gs,ss
99 x64=rax,rbx,rcx,rdx,rsi,rdi,rip,rsp,rbp,r8,r9,r10,r11,r12,r13,r14,r15
100
101 [Aliases]
102 <f1>=stepi
103 <f2>=go -I 1
104 <f5>=go
105 """
106
107 -class Vdb(e_cli.EnviMutableCli, v_notif.Notifier, v_util.TraceManager):
108
109 """
110 A VDB object is a debugger object which may be used to embed full
111 debugger like functionality into a python application. The
112 Vdb object contains a CLI impelementation which extends envi.cli>
113 """
114
163
165 cfgfile = None
166 if self.vdbhome != None:
167 if not os.path.exists(self.vdbhome):
168 os.mkdir(self.vdbhome)
169 cfgfile = os.path.join(self.vdbhome, "vdb.conf")
170
171 self.config = e_config.EnviConfig(filename=cfgfile, defaults=defconfig)
172
187
188 - def verror(self, msg, addnl=True):
189 if addnl:
190 msg += "\n"
191 sys.stderr.write(msg)
192
194 """
195 Load up any extensions which are relevant for the current tracer's
196 platform/arch/etc...
197 """
198 v_ext.loadExtensions(self, trace)
199
202
204 """
205 Generate a new trace for this vdb instance. This fixes many of
206 the new attach/exec data munging issues because tracer re-use is
207 *very* sketchy...
208 """
209 oldtrace = self.getTrace()
210 if oldtrace.isRunning():
211 oldtrace.sendBreak()
212 if oldtrace.isAttached():
213 oldtrace.detach()
214
215 self.trace = oldtrace.buildNewTrace()
216 oldtrace.release()
217
218 self.bpcmds = {}
219 self.manageTrace(self.trace)
220 return self.trace
221
223 self.siglookup = VdbLookup()
224
225 self.siglookup[0] = "None"
226
227 for name in dir(signal):
228 if name[:3] == "SIG" and "_" not in name:
229 self.siglookup[name] = getattr(signal, name)
230
232 """
233 If given an int, return the name, for a name, return the int ;)
234 """
235 return self.siglookup.get(sig,None)
236
239
245
247 """
248 Return a string representing the best known name for
249 the given address
250 """
251 if not address:
252 return "NULL"
253
254
255 sym = self.trace.getSymByAddr(address, exact=False)
256 if sym != None:
257 return "%s + %d" % (repr(sym),address-long(sym))
258
259
260 for tid,tinfo in self.trace.getThreads().items():
261 ctx = self.trace.getRegisterContext(tid)
262 sp = ctx.getStackCounter()
263 stack,size,perms,fname = self.trace.getMemoryMap(sp)
264 if address >= stack and address < (stack+size):
265 off = address - sp
266 op = "+"
267 if off < 0:
268 op = "-"
269 off = abs(off)
270 return "tid:%d sp%s%s (stack)" % (tid,op,off)
271
272 map = self.trace.getMemoryMap(address)
273 if map:
274 return map[3]
275
276 return "Who knows?!?!!?"
277
278 - def script(self, filename, args=[]):
279 """
280 Execute a vdb script.
281 """
282 text = file(filename).read()
283 self.scriptstring(text, filename, args)
284
286 """
287 Do the actual compile and execute for the script data
288 contained in script which was read from filename.
289 """
290 local = self.getExpressionLocals()
291 cobj = compile(script, filename, "exec")
292 sthr = ScriptThread(cobj, local)
293 sthr.start()
294
295 - def notify(self, event, trace):
296
297 pid = trace.getPid()
298 tid = trace.getCurrentThread()
299
300 if event == vtrace.NOTIFY_ATTACH:
301 self.vprint("Attached to : %d" % pid)
302 self.waitlib = None
303 self.difftracks = {}
304
305 if self.windows_jit_event:
306 trace._winJitEvent(self.windows_jit_event)
307 self.windows_jit_event = None
308
309 elif event == vtrace.NOTIFY_CONTINUE:
310 pass
311
312 elif event == vtrace.NOTIFY_DETACH:
313 self.difftracks = {}
314 self.vprint("Detached from %d" % pid)
315
316 elif event == vtrace.NOTIFY_SIGNAL:
317
318
319 thr = trace.getCurrentThread()
320 signo = trace.getCurrentSignal()
321
322 self.vprint("Process Recieved Signal %d (0x%.8x) (Thread: %d (0x%.8x))" % (signo, signo, thr, thr))
323
324 faddr,fperm = trace.getMemoryFault()
325 if faddr != None:
326 accstr = e_mem.getPermName(fperm)
327 self.vprint('Memory Fault: addr: 0x%.8x perm: %s' % (faddr, accstr))
328
329 elif event == vtrace.NOTIFY_BREAK:
330
331 trace.setMeta('PendingBreak', False)
332 bp = trace.getCurrentBreakpoint()
333 if bp:
334 self.vprint("Thread: %d Hit Break: %s" % (tid, repr(bp)))
335 cmdstr = self.bpcmds.get(bp.id, None)
336 if cmdstr != None:
337 self.onecmd(cmdstr)
338
339 else:
340 self.vprint("Thread: %d NOTIFY_BREAK" % tid)
341 if self.runagain:
342 trace.runAgain()
343 self.runagain = False
344
345 elif event == vtrace.NOTIFY_EXIT:
346 ecode = trace.getMeta('ExitCode')
347 self.vprint("PID %d exited: %d (0x%.8x)" % (pid,ecode,ecode))
348
349 elif event == vtrace.NOTIFY_LOAD_LIBRARY:
350 self.vprint("Loading Binary: %s" % trace.getMeta("LatestLibrary",None))
351 if self.waitlib != None:
352 normname = trace.getMeta('LatestLibraryNorm', None)
353 if self.waitlib == normname:
354 self.waitlib = None
355 trace.runAgain(False)
356
357 elif event == vtrace.NOTIFY_UNLOAD_LIBRARY:
358 self.vprint("Unloading Binary: %s" % trace.getMeta("LatestLibrary",None))
359
360 elif event == vtrace.NOTIFY_CREATE_THREAD:
361 self.vprint("New Thread: %d" % tid)
362
363 elif event == vtrace.NOTIFY_EXIT_THREAD:
364 ecode = trace.getMeta("ExitCode", 0)
365 self.vprint("Exit Thread: %d (ecode: 0x%.8x (%d))" % (tid,ecode,ecode))
366
367 elif event == vtrace.NOTIFY_DEBUG_PRINT:
368 s = "<unknown>"
369 win32 = trace.getMeta("Win32Event", None)
370 if win32:
371 s = win32.get("DebugString", "<unknown>")
372 self.vprint("DEBUG PRINT: %s" % s)
373
374
375
376
377
378
380 """
381 List the available structure modules and optionally
382 structure definitions from a particular module in the
383 current vstruct.
384
385 Usage: vstruct [modname]
386 """
387 if len(line) == 0:
388 self.vprint("\nVStruct Namespaces:")
389 plist = self.trace.getStructNames()
390 else:
391 self.vprint("\nKnown Structures (from %s):" % line)
392 plist = self.trace.getStructNames(namespace=line)
393
394 for n in plist:
395 self.vprint(str(n))
396 self.vprint("\n")
397
399 """
400 Print out the opcodes for a given address expression
401
402 Usage: dis <address expression> [<size expression>]
403 """
404
405 argv = e_cli.splitargs(line)
406
407 size = 20
408 argc = len(argv)
409 if argc == 0:
410 addr = self.trace.getProgramCounter()
411 else:
412 addr = self.parseExpression(argv[0])
413
414 if argc > 1:
415 size = self.parseExpression(argv[1])
416
417 self.vprint("Dissassembly:")
418 self.canvas.renderMemory(addr, size, rend=self.opcoderend)
419
421 """
422 Set a variable in the expression parsing context. This allows
423 for scratchspace names (python compatable names) to be used in
424 expressions.
425
426 Usage: var <name> <addr_expression>
427
428 NOTE: The address expression *must* resolve at the time you set it.
429 """
430 t = self.trace
431
432 if len(line):
433 argv = e_cli.splitargs(line)
434 if len(argv) == 1:
435 return self.do_help("var")
436 name = argv[0]
437 expr = " ".join(argv[1:])
438 addr = t.parseExpression(expr)
439 t.setVariable(name, addr)
440
441 vars = t.getVariables()
442 self.vprint("Current Variables:")
443 if not vars:
444 self.vprint("None.")
445 else:
446 vnames = vars.keys()
447 vnames.sort()
448 for n in vnames:
449 val = vars.get(n)
450 if type(val) in (int, long):
451 self.vprint("%20s = 0x%.8x" % (n,val))
452 else:
453 rstr = repr(val)
454 if len(rstr) > 30:
455 rstr = rstr[:30] + '...'
456 self.vprint("%20s = %s" % (n,rstr))
457
459
460
461
462
463
464
465 """
466 Allocate a chunk of memory in the target process. It will be
467 allocated with rwx permissions.
468
469 Usage: alloc <size expr>
470 """
471 if len(args) == 0:
472 return self.do_help("alloc")
473 t = self.trace
474
475 try:
476 size = t.parseExpression(args)
477 base = t.allocateMemory(size)
478 self.vprint("Allocated %d bytes at: 0x%.8x" % (size, base))
479 except Exception, e:
480 traceback.print_exc()
481 self.vprint("Allocation Error: %s" % e)
482
484 '''
485 Load a file into memory. (straight mapping, no parsing)
486
487 Usage: memload <filename>
488 '''
489 argv = e_cli.splitargs(line)
490 if len(argv) != 1:
491 return self.do_help('memload')
492
493 fname = argv[0]
494 if not os.path.isfile(fname):
495 self.vprint('Invalid File: %s' % fname)
496 return
497
498 fbytes = file(fname, 'rb').read()
499 memva = self.trace.allocateMemory(len(fbytes))
500 self.trace.writeMemory(memva, fbytes)
501
502 self.vprint('Loaded At: 0x%.8x (%d bytes)' % (memva, len(fbytes)))
503
505 """
506 Break out a strcuture from memory. You may use the command
507 "vstruct" to show the known structures in vstruct.
508
509 Usage: struct <StructName> <vtrace expression>
510 """
511 try:
512 clsname, vexpr = e_cli.splitargs(args)
513 except:
514 return self.do_help("struct")
515
516 t = self.trace
517
518 addr = t.parseExpression(vexpr)
519 s = t.getStruct(clsname, addr)
520 self.vprint(s.tree(va=addr))
521
523 """
524 Show the current pending signal/exception code.
525
526 Usage: signal
527 """
528
529 t = self.trace
530 t.requireAttached()
531 cursig = t.getCurrentSignal()
532 if cursig == None:
533 self.vprint('No Pending Signals/Exceptions!')
534 else:
535 self.vprint("Current signal: %d (0x%.8x)" % (cursig, cursig))
536
538 """
539 Take a process snapshot of the current (stopped) trace and
540 save it to the specified file.
541
542 Usage: snapshot <filename>
543 """
544 if len(line) == 0:
545 return self.do_help("snapshot")
546 alist = e_cli.splitargs(line)
547 if len(alist) != 1:
548 return self.do_help("snapshot")
549
550 t = self.trace
551 t.requireAttached()
552 self.vprint("Taking Snapshot...")
553 snap = vs_snap.takeSnapshot(t)
554 self.vprint("Saving To File")
555 snap.saveToFile(alist[0])
556 self.vprint("Done")
557 snap.release()
558
560 """
561 Add the specified signal id (exception id for windows) to the ignored
562 signals list for the current trace. This will make the smallest possible
563 performance impact for that particular signal but will also not alert
564 you that it has occured.
565
566 Usage: ignore [options] [-c | <sigcode>...]
567 -d - Remove the specified signal codes.
568 -c - Include the *current* signal in the sigcode list
569 -C - Clear the list of ignored signals
570
571 Example: ignore -c # Ignore the currently posted signal
572 ignore -d 0x80000001 # Remove 0x80000001 from the ignores
573 """
574 argv = e_cli.splitargs(args)
575 try:
576 opts,args = getopt(argv, 'Ccd')
577 except Exception, e:
578 return self.do_help('ignore')
579
580 remove = False
581 sigs = []
582
583 for opt,optarg in opts:
584 if opt == '-c':
585 sig = self.trace.getCurrentSignal()
586 if sig == None:
587 self.vprint('No current signal to ignore!')
588 return
589 sigs.append(sig)
590 elif opt == '-C':
591 self.vprint('Clearing ignore list...')
592 self.trace.setMeta('IgnoredSignals', [])
593 elif opt == '-d':
594 remove = True
595
596 for arg in args:
597 sigs.append(self.trace.parseExpression(arg))
598
599 for sig in sigs:
600 if remove:
601 self.vprint('Removing: 0x%.8x' % sig)
602 self.trace.delIgnoreSignal(sig)
603 else:
604 self.vprint('Adding: 0x%.8x' % sig)
605 self.trace.addIgnoreSignal(sig)
606
607 ilist = self.trace.getMeta("IgnoredSignals")
608 self.vprint("Currently Ignored Signals/Exceptions:")
609 for x in ilist:
610 self.vprint("0x%.8x (%d)" % (x, x))
611
613 """
614 Execute a program with the given command line and
615 attach to it.
616 Usage: exec </some/where and some args>
617 """
618 t = self.newTrace()
619 t.execute(cmd)
620
622 """
623 List the current threads in the target process or select
624 the current thread context for the target tracer.
625 Usage: threads [thread id]
626 """
627 self.trace.requireNotRunning()
628 if self.trace.isRunning():
629 self.vprint("Can't list threads while running!")
630 return
631
632 if len(line) > 0:
633 thrid = int(line, 0)
634 self.trace.selectThread(thrid)
635 if self.gui != None:
636 self.gui.setTraceWindowsActive(True)
637
638 self.vprint("Current Threads:")
639 self.vprint("[thrid] [thrinfo] [pc]")
640
641 curtid = self.trace.getMeta("ThreadId")
642 for tid,tinfo in self.trace.getThreads().items():
643 a = " "
644 if tid == curtid:
645 a = "*"
646
647 sus = ""
648 if self.trace.isThreadSuspended(tid):
649 sus = "(suspended)"
650 ctx = self.trace.getRegisterContext(tid)
651 pc = ctx.getProgramCounter()
652 self.vprint("%s%6d 0x%.8x 0x%.8x %s" % (a, tid, tinfo, pc, sus))
653
655 """
656 Suspend a thread.
657
658 Usage: suspend <-A | <tid>[ <tid>...]>
659 """
660 argv = e_cli.splitargs(line)
661 try:
662 opts,args = getopt(argv, "A")
663 except Exception, e:
664 return self.do_help("suspend")
665
666 for opt,optarg in opts:
667 if opt == "-A":
668
669 args = [str(tid) for tid in self.trace.getThreads().keys()]
670
671 if not len(args):
672 return self.do_help("suspend")
673
674 for arg in args:
675 tid = int(arg)
676 self.trace.suspendThread(tid)
677 self.vprint("Suspended Thread: %d" % tid)
678
680 '''
681 Restart the current process.
682
683 Usage: restart
684
685 NOTE: This only works if the process was exec'd to begin
686 with!
687
688 TODO: Plumb options for persisting bp's etc...
689 '''
690 t = self.trace
691 cmdline = t.getMeta('ExecCommand')
692 if cmdline == None:
693 self.vprint('This trace was not fired with exec! (cannot restart)')
694 return
695
696 if t.isRunning():
697 t.setMode("RunForever", False)
698 t.sendBreak()
699
700 if t.isAttached():
701 t.detach()
702
703 t = self.newTrace()
704 t.execute(cmdline)
705
707 """
708 Resume a thread.
709
710 Usage: resume <-A | <tid>[ <tid>...]>
711 """
712 argv = e_cli.splitargs(line)
713 try:
714 opts,args = getopt(argv, "A")
715 except Exception, e:
716 return self.do_help("suspend")
717
718 for opt,optarg in opts:
719 if opt == "-A":
720
721 args = [str(tid) for tid in self.trace.getThreads().keys()]
722
723 if not len(args):
724 return self.do_help("resume")
725
726 for arg in args:
727 tid = int(arg)
728 self.trace.resumeThread(tid)
729 self.vprint("Resumed Thread: %d" % tid)
730
731
732
734 """
735 Set modes in the tracers...
736 mode Foo=True/False
737 """
738 if args:
739 mode,val = args.split("=")
740 newmode = eval(val)
741 self.setMode(mode, newmode)
742 else:
743 for key,val in self.trace.modes.items():
744 self.vprint("%s -> %d" % (key,val))
745
747 """
748 Show the current register values. Additionally, you may specify
749 name=<expression> to set a register
750
751 Usage: reg [regname=vtrace_expression]
752 """
753 if len(args):
754 if args.find("=") == -1:
755 return self.do_help("reg")
756 regname,expr = args.split("=", 1)
757 val = self.trace.parseExpression(expr)
758 self.trace.setRegisterByName(regname, val)
759 self.vprint("%s = 0x%.8x" % (regname, val))
760 return
761
762 regs = self.trace.getRegisters()
763 rnames = regs.keys()
764 rnames.sort()
765 final = []
766 for r in rnames:
767
768
769 if r.lower() != r:
770 continue
771 val = regs.get(r)
772 vstr = e_bits.hex(val, 4)
773 final.append(("%12s:0x%.8x (%d)" % (r,val,val)))
774 self.columnize(final)
775
777 """
778 Single step the target tracer.
779 Usage: stepi [ options ]
780
781 -A <addr> - Step to <addr>
782 -B - Step past the next branch instruction
783 -C <count> - Step <count> instructions
784 -R - Step to return from this function
785 -V - Show operand values during single step (verbose!)
786
787 """
788 t = self.trace
789 argv = e_cli.splitargs(line)
790 try:
791 opts,args = getopt(argv, "A:BC:RV")
792 except Exception, e:
793 return self.do_help("stepi")
794
795 count = None
796 taddr = None
797 toret = False
798 tobrn = False
799 showop = False
800
801 for opt, optarg in opts:
802 if opt == '-A':
803 taddr = t.parseExpression(optarg)
804
805 elif opt == '-B':
806 tobrn = True
807
808 elif opt == '-C':
809 count = t.parseExpression(optarg)
810
811 elif opt == '-R':
812 toret = True
813
814 elif opt == '-V':
815 showop = True
816
817 if ( count == None
818 and taddr == None
819 and toret == False
820 and tobrn == False):
821 count = 1
822
823 oldmode = self.getMode('FastStep')
824 self.setMode('FastStep', True)
825
826 hits = 0
827 depth = 0
828 try:
829 while True:
830
831 pc = t.getProgramCounter()
832
833 if pc == taddr:
834 break
835
836 op = t.parseOpcode(pc)
837
838 sym = t.getSymByAddr(pc)
839
840 if sym != None:
841 self.canvas.addVaText(repr(sym), pc)
842 self.canvas.addText(':\n')
843
844 self.canvas.addText(' ' * max(depth,0))
845 self.canvas.addVaText('0x%.8x' % pc, pc)
846 self.canvas.addText(': ')
847 op.render(self.canvas)
848 if showop:
849 self.canvas.addText(' ; ')
850 for oper in op.opers:
851 try:
852 val = oper.getOperValue(op, emu=t)
853 self.canvas.addText('0x%.8x ' % val)
854 except Exception, e:
855 self.canvas.addText(str(e))
856 self.canvas.addText('\n')
857
858 if op.iflags & envi.IF_CALL:
859 depth += 1
860
861 elif op.iflags & envi.IF_RET:
862 depth -= 1
863
864 tid = t.getCurrentThread()
865
866 t.stepi()
867
868
869 if t.getCurrentThread() != tid:
870 break
871
872
873 if toret and depth < 0:
874 break
875
876 if depth < 0:
877 depth = 0
878
879 hits += 1
880
881
882 if tobrn == True and hits != 0:
883
884 if op.iflags & envi.IF_CALL:
885 break
886
887 if op.iflags & envi.IF_RET:
888 break
889
890 getout = False
891 for bva, bflags in op.getBranches():
892 if bflags & envi.BR_COND:
893 getout = True
894 break
895 if getout:
896 break
897
898
899 if count != None and hits >= count:
900 break
901
902 if t.getCurrentSignal() != None:
903 break
904
905 if t.getMeta('PendingSignal'):
906 break
907
908 finally:
909 self.setMode('FastStep', oldmode)
910
911 t.fireNotifiers(vtrace.NOTIFY_STEP)
912
914 """
915 Continue the target tracer.
916 -I go icount linear instructions forward (step over style)
917 -U go *out* of fcount frames (step out style)
918 <until addr> go until explicit address
919
920 Usage: go [-U <fcount> | -I <icount> | <until addr expression>]
921 """
922 until = None
923 icount = None
924 fcount = None
925
926 argv = e_cli.splitargs(line)
927 try:
928 opts,args = getopt(argv, "U:I:")
929 except:
930 return self.do_help("go")
931
932 for opt,optarg in opts:
933 if opt == "-U":
934 if len(optarg) == 0: return self.do_help("go")
935 fcount = self.trace.parseExpression(optarg)
936 elif opt == "-I":
937 if len(optarg) == 0: return self.do_help("go")
938 icount = self.trace.parseExpression(optarg)
939
940 if icount != None:
941 addr = self.trace.getProgramCounter()
942 for i in xrange(icount):
943 addr += len(self.arch.makeOpcode(self.trace.readMemory(addr, 16)))
944 until = addr
945
946 elif fcount != None:
947 until = self.trace.getStackTrace()[fcount][0]
948
949 elif len(args):
950 until = self.trace.parseExpression(" ".join(args))
951
952 if not until:
953 self.vprint("Running Tracer (use 'break' to stop it)")
954
955 self.trace.run(until=until)
956
958 '''
959 Attempt to spawn the VDB gui. Assuming GTK etc are all installed.
960 '''
961 if self.gui != None:
962 self.vprint('Gui already running!')
963 return
964 import vdb.gui
965 vdb.gui.main(self)
966
968 '''
969 Run the target process until the specified library
970 (by normalized name such as 'kernel32' or 'libc')
971 is loaded. Disable waiting with -D.
972
973 Usage: waitlib [ -D | <libname> ]
974 '''
975 t = self.trace
976 pid = t.getPid()
977
978 t.requireAttached()
979
980 argv = e_cli.splitargs(line)
981 try:
982 opts,args = getopt(argv, "D")
983 except:
984 return self.do_help("waitlib")
985
986 for opt, optarg in opts:
987 if opt == '-D':
988 self.vprint('Disabling Wait On: %s' % self.waitlib)
989 self.waitlib = None
990 return
991
992 if len(args) != 1:
993 return self.do_help('waitlib')
994
995 libname = args[0]
996
997 if t.getMeta('LibraryBases').get(libname) != None:
998 self.vprint('Library Already Loaded: %s' % libname)
999 return
1000
1001 self.vprint('Setting Waitlib: %s' % libname)
1002 self.waitlib = libname
1003
1005 """
1006 Start a vtrace server on the local box. If the server
1007 is already running, show which processes are being remotely
1008 debugged.
1009
1010 Usage: server
1011 """
1012 if port:
1013 vtrace.port = int(port)
1014
1015 if self.server == None:
1016 self.vprint('Starting vtrace server!')
1017 self.server = vtrace.startVtraceServer()
1018 return
1019
1020 self.vprint('Displaying remotely debugged traces:')
1021 shared = [ t for (n,t) in self.server.getSharedObjects() if isinstance(t, vtrace.Trace) ]
1022 if not shared:
1023 self.vprint('None.')
1024 return
1025
1026 for t in shared:
1027
1028 if not t.isAttached():
1029 continue
1030
1031 runmsg = 'stopped'
1032 if t.isRunning():
1033 runmsg = 'running'
1034
1035 pid = t.getPid()
1036 name = t.getMeta('ExeName', 'Unknown')
1037 self.vprint('%6d %.8s - %s' % (pid, runmsg, name))
1038
1040 """
1041 List symbols and by file.
1042
1043 Usage: syms [-s <pattern>] [filename]
1044
1045 With no arguments, syms will self.vprint(the possible
1046 libraries with symbol resolvers. Specify a library
1047 to see all the symbols for it.
1048 """
1049
1050 argv = e_cli.splitargs(line)
1051 try:
1052 opts,args = getopt(argv, "s:")
1053 except:
1054 return self.do_help("syms")
1055
1056 pattern = None
1057 for opt,optarg in opts:
1058 if opt == "-s":
1059 pattern = optarg.lower()
1060
1061 libs = self.trace.getNormalizedLibNames()
1062 libs.sort()
1063 if len(args) == 0:
1064 self.vprint("Current Library Symbol Resolvers:")
1065
1066 if pattern == None:
1067 for libname in libs:
1068 self.vprint(" %s" % libname)
1069 else:
1070 for libname in libs:
1071 for sym in self.trace.getSymsForFile(libname):
1072 r = repr(sym)
1073 if pattern != None:
1074 if r.lower().find(pattern) == -1:
1075 continue
1076 self.vprint("0x%.8x %s" % (sym.value, r))
1077
1078 else:
1079 libname = args[0]
1080 if libname not in libs:
1081 self.vprint("Unknown libname: %s" % libname)
1082 return
1083 if pattern:
1084 self.vprint("Matching Symbols From %s:" % libname)
1085 else:
1086 self.vprint("Symbols From %s:" % libname)
1087
1088 for sym in self.trace.getSymsForFile(libname):
1089 r = repr(sym)
1090 if pattern != None:
1091 if r.lower().find(pattern) == -1:
1092 continue
1093 self.vprint("0x%.8x %s" % (sym.value, r))
1094
1096 """
1097 Allows a C-like syntax for calling functions inside
1098 the target process (from his context).
1099 Example: call printf("yermom %d", 10)
1100 """
1101 self.trace.requireAttached()
1102 ind = string.index("(")
1103 if ind == -1:
1104 raise Exception('ERROR - call wants c-style syntax: ie call printf("yermom")')
1105 funcaddr = self.trace.parseExpression(string[:ind])
1106
1107 try:
1108 args = eval(string[ind:])
1109 except:
1110 raise Exception('ERROR - call wants c-style syntax: ie call printf("yermom")')
1111
1112 self.vprint("calling %s -> 0x%.8x" % (string[:ind], funcaddr))
1113 self.trace.call(funcaddr, args)
1114
1116 """
1117 Return the "best name" string for an address.
1118
1119 Usage: bestname <vtrace expression>
1120 """
1121 if len(args) == 0:
1122 return self.do_help("bestname")
1123 addr = self.trace.parseExpression(args)
1124 self.vprint(self.reprPointer(addr))
1125
1127 self.vprint("No.. this is NOT a python interpreter... use quit ;)")
1128
1130 """
1131 Quit VDB
1132
1133 use "quit force" to hard-force a quit regardless of everything.
1134 """
1135
1136 if args == 'force':
1137 print 'Quitting by force!'
1138 os._exit(0)
1139
1140 try:
1141 if self.trace.isRunning():
1142 self.trace.setMode("RunForever", False)
1143 self.trace.sendBreak()
1144
1145 if self.trace.isAttached():
1146 self.vprint("Detaching...")
1147 self.trace.detach()
1148
1149 self.vprint("Exiting...")
1150 e_cli.EnviMutableCli.do_quit(self, args)
1151
1152 self.trace.release()
1153
1154 except Exception, e:
1155 self.vprint('Exception during quit (may need: quite force): %s' % e)
1156
1166
1168 """
1169 Attach to a process by PID or by process name. In
1170 the event of more than one process by a given name,
1171 attach to the last (most recently created) one in
1172 the list.
1173
1174 Usage: attach [<pid>,<name>]
1175
1176 NOTE: This is *not* a regular expression. The given
1177 string must be found as a substring of the process
1178 name...
1179 """
1180 pid = None
1181 try:
1182 pid = int(args)
1183 except ValueError, e:
1184
1185 for mypid, pname in self.trace.ps():
1186 if pname.find(args) != -1:
1187 pid = mypid
1188
1189 if pid == None:
1190 return self.do_help('attach')
1191
1192 self.vprint("Attaching to %d" % pid)
1193 self.newTrace().attach(pid)
1194
1196 """
1197 Manipulate the auto-continue behavior for the trace. This
1198 will cause particular event types to automagically continue
1199 execution.
1200
1201 Usage: autocont [event name]
1202 """
1203 argv = e_cli.splitargs(line)
1204 acnames = ["attach",
1205 "signal",
1206 "break",
1207 "loadlib",
1208 "unloadlib",
1209 "createthread",
1210 "exitthread",
1211 "dbgprint"]
1212
1213 acvals = [ vtrace.NOTIFY_ATTACH,
1214 vtrace.NOTIFY_SIGNAL,
1215 vtrace.NOTIFY_BREAK,
1216 vtrace.NOTIFY_LOAD_LIBRARY,
1217 vtrace.NOTIFY_UNLOAD_LIBRARY,
1218 vtrace.NOTIFY_CREATE_THREAD,
1219 vtrace.NOTIFY_EXIT_THREAD,
1220 vtrace.NOTIFY_DEBUG_PRINT]
1221
1222 c = self.trace.getAutoContinueList()
1223
1224 if len(line):
1225 try:
1226 index = acnames.index(line)
1227 except ValueError, e:
1228 self.vprint("Unknown event name: %s" % line)
1229 return
1230 sig = acvals[index]
1231 if sig in c:
1232 self.trace.disableAutoContinue(sig)
1233 c.remove(sig)
1234 else:
1235 self.trace.enableAutoContinue(sig)
1236 c.append(sig)
1237
1238 self.vprint("Auto Continue Status:")
1239 for i in range(len(acnames)):
1240 name = acnames[i]
1241 sig = acvals[i]
1242 acont = False
1243 if sig in c:
1244 acont = True
1245 self.vprint("%s %s" % (name.rjust(14),repr(acont)))
1246
1249
1251 """
1252 Show a stack backtrace for the currently selected thread.
1253
1254 Usage: bt
1255 """
1256 self.vprint(" [ PC ] [ Frame ] [ Location ]")
1257 idx = 0
1258 for pc,frame in self.trace.getStackTrace():
1259 self.vprint("[%3d] 0x%.8x 0x%.8x %s" % (idx,pc,frame,self.reprPointer(pc)))
1260 idx += 1
1261
1263 """
1264 Show the loaded libraries and their base addresses.
1265
1266 Usage: lm [libname]
1267 """
1268 bases = self.trace.getMeta("LibraryBases")
1269 paths = self.trace.getMeta("LibraryPaths")
1270 if len(args):
1271 base = bases.get(args)
1272 path = paths.get(base, "unknown")
1273 if base == None:
1274 self.vprint("Library %s is not found!" % args)
1275 else:
1276 self.vprint("0x%.8x - %s %s" % (base, args, path))
1277 else:
1278 self.vprint("Loaded Libraries:")
1279 names = self.trace.getNormalizedLibNames()
1280 names.sort()
1281 names = e_cli.columnstr(names)
1282 for libname in names:
1283 base = bases.get(libname.strip(), -1)
1284 path = paths.get(base, "unknown")
1285 self.vprint("0x%.8x - %.30s %s" % (base, libname, path))
1286
1288 """
1289 Parse and display a Global Unique Identifier (GUID) from memory
1290 (eventually, use GUID db to lookup the name/meaning of the GUID).
1291
1292 Usage: guid <addr_exp>
1293 """
1294 self.trace.requireNotRunning()
1295 if not line:
1296 return self.do_help("guid")
1297
1298 addr = self.parseExpression(line)
1299 guid = vs_prims.GUID()
1300 bytes = self.trace.readMemory(addr, len(guid))
1301 guid.vsSetValue(bytes)
1302 self.vprint("GUID 0x%.8x %s" % (addr, repr(guid)))
1303
1305 """
1306 Set the python code for a breakpoint from the contents
1307 of a file.
1308
1309 Usage: bpfile <bpid> <filename>
1310 """
1311 argv = e_cli.splitargs(line)
1312 if len(argv) != 2:
1313 return self.do_help("bpfile")
1314
1315 bpid = int(argv[0])
1316 pycode = file(argv[1], "rU").read()
1317
1318 self.trace.setBreakpointCode(bpid, pycode)
1319
1321 """
1322 Manipulcate the python code that will be run for a given
1323 breakpoint by ID. (Also the way to view the code).
1324
1325 Usage: bpedit <id> ["optionally new code"]
1326
1327 NOTE: Your code must be surrounded by "s and may not
1328 contain any "s
1329 """
1330 argv = e_cli.splitargs(line)
1331 if len(argv) == 0:
1332 return self.do_help("bpedit")
1333 bpid = int(argv[0])
1334
1335 if len(argv) == 2:
1336 self.trace.setBreakpointCode(bpid, argv[1])
1337
1338 pystr = self.trace.getBreakpointCode(bpid)
1339 self.vprint("[%d] Breakpoint code: %s" % (bpid,pystr))
1340
1342 """
1343 Show, add, and enable/disable breakpoints
1344 USAGE: bp [-d <addr>] [-a <addr>] [-o <addr>] [[-c pycode] <address> [vdb cmds]]
1345 -C - Clear All Breakpoints
1346 -c "py code" - Set the breakpoint code to the given python string
1347 -d <id> - Disable Breakpoint
1348 -e <id> - Enable Breakpoint
1349 -r <id> - Remove Breakpoint
1350 -o <addr> - Create a OneTimeBreak
1351 -L <libname> - Add bp's to all functions in <libname>
1352 -F <filename> - Load bpcode from file
1353 -W perms:size - Set a hardware Watchpoint with perms/size (ie -W rw:4)
1354 -f - Make added breakpoints from this command into "fastbreaks"
1355 -S <libname>:<regex> - Add bp's to all matching funcs in <libname>
1356
1357 <address>... - Create Breakpoint
1358
1359 [vdb cmds].. - (optional) vdb cli comand to run on BP hit (seperate
1360 multiple commands with ;; )
1361
1362 NOTE: -c adds python code to the breakpoint. The python code will
1363 be run with the following objects mapped into it's namespace
1364 automagically:
1365 vtrace - the vtrace package
1366 trace - the tracer
1367 bp - the breakpoint object
1368 """
1369 self.trace.requireNotRunning()
1370
1371 argv = e_cli.splitargs(line)
1372 try:
1373 opts,args = getopt(argv, "fF:e:d:o:r:L:Cc:S:W:")
1374 except Exception, e:
1375 return self.do_help('bp')
1376
1377 pycode = None
1378 wpargs = None
1379 fastbreak = False
1380 libsearch = None
1381
1382 for opt,optarg in opts:
1383 if opt == "-e":
1384 self.trace.setBreakpointEnabled(eval(optarg), True)
1385
1386 elif opt == "-c":
1387 pycode = optarg
1388 test = compile(pycode, "test","exec")
1389
1390 elif opt == "-F":
1391 pycode = file(optarg, "rU").read()
1392
1393 elif opt == '-f':
1394 fastbreak = True
1395
1396 elif opt == "-r":
1397 self.bpcmds.pop(eval(optarg), None)
1398 self.trace.removeBreakpoint(eval(optarg))
1399
1400 elif opt == "-C":
1401 for bp in self.trace.getBreakpoints():
1402 self.bpcmds.pop(bp.id, None)
1403 self.trace.removeBreakpoint(bp.id)
1404
1405 elif opt == "-d":
1406 self.trace.setBreakpointEnabled(eval(optarg), False)
1407
1408 elif opt == "-o":
1409 self.trace.addBreakpoint(vtrace.OneTimeBreak(None, expression=optarg))
1410
1411 elif opt == "-L":
1412 for sym in self.trace.getSymsForFile(optarg):
1413 if not isinstance(sym, e_resolv.FunctionSymbol):
1414 continue
1415 try:
1416 bp = vtrace.Breakpoint(None, expression=str(sym))
1417 bp.setBreakpointCode(pycode)
1418 self.trace.addBreakpoint(bp)
1419 self.vprint("Added: %s" % str(sym))
1420 except Exception, msg:
1421 self.vprint("WARNING: %s" % str(msg))
1422
1423 elif opt == "-W":
1424 wpargs = optarg.split(":")
1425
1426 elif opt == '-S':
1427 libname, regex = optarg.split(':')
1428
1429 try:
1430 for sym in self.trace.searchSymbols(regex, libname=libname):
1431
1432 symstr = str(sym)
1433 symval = long(sym)
1434 if self.trace.getBreakpointByAddr(symval) != None:
1435 self.vprint('Duplicate (0x%.8x) %s' % (symval, symstr))
1436 continue
1437 bp = vtrace.Breakpoint(None, expression=symstr)
1438 self.trace.addBreakpoint(bp)
1439 self.vprint('Added: %s' % symstr)
1440
1441 except re.error, e:
1442 self.vprint('Invalid Regular Expression: %s' % regex)
1443 return
1444
1445 cmdstr = None
1446 if len(args) > 1:
1447 cmdstr = ' '.join(args[1:])
1448
1449 if len(args) >= 1:
1450 arg = args[0]
1451
1452 if wpargs != None:
1453 size = int(wpargs[1])
1454 bp = vtrace.Watchpoint(None, expression=arg, size=size, perms=wpargs[0])
1455 else:
1456 bp = vtrace.Breakpoint(None, expression=arg)
1457
1458 bp.setBreakpointCode(pycode)
1459 bp.fastbreak = fastbreak
1460 bpid = self.trace.addBreakpoint(bp)
1461 if cmdstr:
1462 self.bpcmds[bpid] = cmdstr.replace(';;', '&&')
1463
1464 self.vprint(" [ Breakpoints ]")
1465 for bp in self.trace.getBreakpoints():
1466 cmdstr = self.bpcmds.get(bp.id, '')
1467 self.vprint("%s enabled: %s fast: %s %s" % (bp, bp.isEnabled(), bp.fastbreak, cmdstr))
1468
1470 """
1471 Show all the open Handles/FileDescriptors for the target process.
1472 The "typecode" shown in []'s is the vtrace typecode for that kind of
1473 fd/handle.
1474
1475 Usage: fds
1476 """
1477 self.trace.requireAttached()
1478 for id,fdtype,fname in self.trace.getFds():
1479 self.vprint("0x%.8x [%d] %s" % (id,fdtype,fname))
1480
1482 """
1483 Show the current process list.
1484
1485 Usage: ps
1486 """
1487 self.vprint("[Pid]\t[ Name ]")
1488 for ps in self.trace.ps():
1489 self.vprint("%s\t%s" % (ps[0],ps[1]))
1490
1492 """
1493 Send the break signal to the target tracer to stop
1494 it's execution.
1495
1496 Usage: break
1497 """
1498 if self.trace.getMeta('PendingBreak'):
1499 self.vprint('Break already sent...')
1500 return
1501 self.trace.setMeta('PendingBreak', True)
1502 self.trace.setMode("RunForever", False)
1503 self.trace.sendBreak()
1504
1520
1522 """
1523 Save and compare snapshots of memory to enumerate changes.
1524
1525 Usage: memdiff [options]
1526 -C Clear all current memory diff snapshots.
1527 -A <va:size> Add the given virtual address to the list.
1528 -M <va> Add the entire memory map which contains VA to the list.
1529 -D Compare currently tracked memory with the target process
1530 and show any differences.
1531 """
1532 argv = e_cli.splitargs(line)
1533 opts,args = getopt(argv, "A:CDM:")
1534
1535 if len(opts) == 0:
1536 return self.do_help('memdiff')
1537
1538 self.trace.requireNotRunning()
1539
1540 for opt,optarg in opts:
1541
1542 if opt == "-A":
1543 if optarg.find(':') == -1:
1544 return self.do_help('memdiff')
1545
1546 vastr,sizestr = optarg.split(':')
1547 va = self.parseExpression(vastr)
1548 size = self.parseExpression(sizestr)
1549 bytes = self.trace.readMemory(va,size)
1550 self.difftracks[va] = bytes
1551
1552 elif opt == '-C':
1553 self.difftracks = {}
1554
1555 elif opt == '-D':
1556 difs = self._getDiffs()
1557 if len(difs) == 0:
1558 self.vprint('No Differences!')
1559 else:
1560 for va,thenbytes,nowbytes in difs:
1561 self.vprint('0x%.8x: %s %s' %
1562 (va,
1563 thenbytes.encode('hex'),
1564 nowbytes.encode('hex')))
1565
1566 elif opt == '-M':
1567 va = self.parseExpression(optarg)
1568 map = self.trace.getMemoryMap(va)
1569 if map == None:
1570 self.vprint('No Memory Map At: 0x%.8x' % va)
1571 return
1572 mva,msize,mperm,mfile = map
1573 bytes = self.trace.readMemory(mva, msize)
1574 self.difftracks[mva] = bytes
1575
1576
1578
1579 ret = []
1580 for va, bytes in self.difftracks.items():
1581 nowbytes = self.trace.readMemory(va, len(bytes))
1582
1583 i = 0
1584 while i < len(bytes):
1585 thendiff = ""
1586 nowdiff = ""
1587 iva = va+i
1588 while (i < len(bytes) and
1589 bytes[i] != nowbytes[i]):
1590 thendiff += bytes[i]
1591 nowdiff += nowbytes[i]
1592 i += 1
1593
1594 if thendiff:
1595 ret.append((iva, thendiff, nowdiff))
1596 continue
1597
1598 i += 1
1599
1600 return ret
1601
1603 '''
1604 Cli interface to the "stack doping" api inside recon. *BETA*
1605
1606 (Basically, set all un-initialized stack memory to V's to tease
1607 out uninitialized stack bugs)
1608
1609 Usage: dope [ options ]
1610 -E Enable automagic thread stack doping on all continue events
1611 -D Disable automagic thread stack doping on all continue events
1612 -A Dope all current thread stacks
1613 '''
1614 import vdb.recon.dopestack as vr_dopestack
1615
1616 argv = e_cli.splitargs(line)
1617
1618 if len(argv) == 0:
1619 return self.do_help('dope')
1620
1621 opts,args = getopt(argv, 'ADE')
1622
1623 if len(opts) == 0:
1624 return self.do_help('dope')
1625
1626 for opt, optarg in opts:
1627
1628 if opt == '-A':
1629 self.vprint('Doping all thread stacks...')
1630 vr_dopestack.dopeAllThreadStacks(self.trace)
1631 self.vprint('...complete!')
1632
1633 elif opt == '-D':
1634 self.vprint('Disabling thread doping...')
1635 vr_dopestack.disableEventDoping(self.trace)
1636 self.vprint('...complete!')
1637
1638 elif opt == '-E':
1639 self.vprint('Enabling thread doping on CONTINUE events...')
1640 vr_dopestack.enableEventDoping(self.trace)
1641 self.vprint('...complete!')
1642
1643
1645 '''
1646 Cli front end to the vdb recon subsystem which allows runtime
1647 analysis of known API calls.
1648
1649 Usage: recon [options]
1650 -A <sym_expr>:<recon_fmt> - Add a recon breakpoint with the given format
1651 -C - Clear the current list of recon breakpoint hits.
1652 -H - Print the current list of recon breakpoint hits.
1653 -Q - Toggle "quiet" mode which prints nothing on bp hits.
1654 -S <sym_expr>:<argidx> - Add a sniper break for arg index
1655
1656 NOTE: A "recon format" is a special format sequence which tells the
1657 recon subsystem how to present the argument data for a given
1658 breakpoint hit.
1659
1660 Recon Format:
1661 C - A character
1662 I - A decimal integer
1663 P - A pointer (display symbol if possible)
1664 S - An ascii string (up to 260 chars)
1665 U - A unicode string (up to 260 chars)
1666 X - A hex number
1667
1668 '''
1669 import vdb.recon as v_recon
1670 import vdb.recon.sniper as v_sniper
1671 argv = e_cli.splitargs(line)
1672
1673 if len(argv) == 0:
1674 return self.do_help('recon')
1675
1676 if self.trace.getMeta('Architecture') != 'i386':
1677 self.vprint('FIXME: recon only works on i386 right now...')
1678 return
1679
1680 opts,args = getopt(argv, 'A:CHQS:')
1681 for opt, optarg in opts:
1682 if opt == '-A':
1683 symname, reconfmt = optarg.split(':', 1)
1684 v_recon.addReconBreak(self.trace, symname, reconfmt)
1685
1686 elif opt == '-C':
1687 v_recon.clearReconHits(self.trace)
1688
1689 elif opt == '-H':
1690 self.vprint('Recon Hits:')
1691 hits = v_recon.getReconHits(self.trace)
1692 for hit in hits:
1693 thrid, savedeip, symname, args, argrep = hit
1694 argstr = '(%s)' % ', '.join(argrep)
1695 self.vprint('[%6d] 0x%.8x %s%s' % (thrid, savedeip, symname, argstr))
1696 self.vprint('%d total hits' % len(hits))
1697
1698 elif opt == '-Q':
1699 newval = not self.trace.getMeta('recon_quiet', False)
1700 self.trace.setMeta('recon_quiet', newval)
1701 self.vprint('Recon Quiet: %s' % newval)
1702
1703 elif opt == '-S':
1704 symname, idxstr = optarg.split(':')
1705 argidx = self.trace.parseExpression(idxstr)
1706 v_sniper.snipeDynArg(self.trace, symname, argidx)
1707
1709 '''
1710 Cli front end to the VDB code coverage subsystem. FIXME MORE DOCS!
1711
1712 Usage: stalker [options]
1713 -C - Cleanup stalker breaks and hit info
1714 -c - Clear the current hits (so you can make more ;)
1715 -E <addr_expr> - Add the specified entry point for tracking
1716 -H - Show the current hits
1717 -L <lib>:<regex> - Add stalker breaks to all matching library symbols
1718 -R - Reset all breakpoints to enabled and clear hit info
1719 '''
1720
1721 argv = e_cli.splitargs(line)
1722
1723 if len(argv) == 0:
1724 return self.do_help('stalker')
1725
1726 try:
1727 opts,args = getopt(argv, 'cCE:HIL:R')
1728 except Exception ,e:
1729 return self.do_help('stalker')
1730
1731 trace = self.trace
1732 for opt, optarg in opts:
1733 if opt == '-c':
1734 v_stalker.clearStalkerHits(trace)
1735 self.vprint('Clearing Stalker Hits...')
1736
1737 elif opt == '-C':
1738 v_stalker.clearStalkerBreaks(trace)
1739 v_stalker.clearStalkerHits(trace)
1740 self.vprint('Cleaning up stalker breaks and hits')
1741
1742
1743 elif opt == '-E':
1744 addr = trace.parseExpression(optarg)
1745 v_stalker.addStalkerEntry(trace, addr)
1746 self.vprint('Added 0x%.8x' % addr)
1747
1748 elif opt == '-H':
1749 self.vprint('Current Stalker Hits:')
1750 for hitva in v_stalker.getStalkerHits(trace):
1751 self.vprint('0x%.8x' % hitva)
1752
1753 elif opt == '-L':
1754 libname, regex = optarg.split(':', 1)
1755 for sym in trace.searchSymbols(regex, libname=libname):
1756 v_stalker.addStalkerEntry(trace, long(sym))
1757 self.vprint('Stalking %s' % str(sym))
1758
1759 elif opt == '-R':
1760 self.vprint('Resetting all breaks and hit info')
1761 v_stalker.clearStalkerHits(trace)
1762 v_stalker.resetStalkerBreaks(trace)
1763
1765 '''
1766 Print out the status of the debugger / trace...
1767 '''
1768 t = self.getTrace()
1769 if not t.isAttached():
1770 self.vprint('Trace Not Attached...')
1771
1772 running = t.isRunning()
1773 runmsg = 'stopped'
1774 if running:
1775 runmsg = 'running'
1776 pid = t.getPid()
1777 self.vprint('Attached to pid: %d (%s)' % (pid, runmsg))
1778
1780 """
1781 Act as a remote debugging client to the server running on
1782 the specified host/ip.
1783
1784 Usage: remote <host>
1785 """
1786 vtrace.remote = line
1787
1788