Package envi :: Module cli
[hide private]
[frames] | no frames]

Source Code for Module envi.cli

  1  """ 
  2  Unified CLI code for things like vivisect and vdb. 
  3  """ 
  4   
  5  import code 
  6  import traceback 
  7  import threading 
  8   
  9  import envi.bits as e_bits 
 10  import envi.memory as e_mem 
 11  import envi.config as e_config 
 12  import envi.resolver as e_resolv 
 13  import envi.memcanvas as e_canvas 
 14  import envi.expression as e_expr 
 15   
 16  from cmd import Cmd 
 17  from getopt import getopt 
 18   
 19  import re                                                                                                           
 20   
21 -def splitargs(cmdline):
22 cmdline = cmdline.replace('\\\\"', '"').replace('\\"', '') 23 patt = re.compile('\".+?\"|\S+') 24 for item in cmdline.split('\n'): 25 return [s.strip('"') for s in patt.findall(item)]
26
27 -def columnstr(slist):
28 msize = 0 29 for s in slist: 30 if len(s) > msize: 31 msize = len(s) 32 return [x.ljust(msize) for x in slist]
33 34
35 -class CliExtMeth:
36 """ 37 This is used to work around the difference 38 between functions and bound methods for extended 39 command modules 40 """
41 - def __init__(self, cli, func):
42 self.cli = cli 43 self.func = func 44 self.__doc__ = func.__doc__
45
46 - def __call__(self, line):
47 return self.func(self.cli, line)
48 49 cfgdefs = """ 50 51 [Aliases] 52 53 """ 54
55 -class EnviCli(Cmd):
56
57 - def __init__(self, memobj, config=None, symobj=None):
58 59 self.extcmds = {} 60 61 Cmd.__init__(self, stdout=self) 62 63 self.shutdown = threading.Event() 64 65 # If they didn't give us a resolver, make one. 66 if symobj == None: 67 symobj = e_resolv.SymbolResolver() 68 69 if config == None: 70 config = e_config.EnviConfig(defaults=cfgdefs) 71 72 self.config = config 73 self.memobj = memobj 74 self.symobj = symobj 75 self.canvas = e_canvas.MemoryCanvas(memobj, syms=symobj)
76
77 - def setCanvas(self, canvas):
78 """ 79 Set a new canvas for the CLI and add all the current renderers 80 to the new one. 81 """ 82 for name in self.canvas.getRendererNames(): 83 canvas.addRenderer(name, self.canvas.getRenderer(name)) 84 self.canvas = canvas
85
86 - def write(self, data):
87 # For stdout/stderr 88 self.canvas.write(data)
89
90 - def get_names(self):
91 ret = [] 92 ret.extend(Cmd.get_names(self)) 93 ret.extend(self.extcmds.keys()) 94 return ret
95
96 - def getExpressionLocals(self):
97 """ 98 Over-ride this to have things like the eval command 99 and the python command use more locals than the sybolic 100 defaults. 101 """ 102 return e_expr.MemoryExpressionLocals(self.memobj, symobj=self.symobj)
103
104 - def registerCmdExtension(self, func):
105 self.extcmds["do_%s" % func.__name__] = CliExtMeth(self, func)
106
107 - def vprint(self, msg, addnl=True):
108 ''' 109 Print output to the CLI's output handler. This allows routines to 110 print to the terminal or the GUI depending on which mode we're in. 111 112 Example: 113 vprint('hi mom!') 114 ''' 115 if addnl: 116 msg = msg+"\n" 117 self.canvas.write(msg)
118
119 - def __getattr__(self, name):
120 func = self.extcmds.get(name, None) 121 if func == None: 122 raise AttributeError(name) 123 return func
124
125 - def doAlias(self, line):
126 for opt in self.config.options("Aliases"): 127 if line.startswith(opt): 128 line = line.replace(opt, self.config.get("Aliases", opt), 1) 129 return line
130
131 - def cmdloop(self, intro=None):
132 if intro != None: 133 self.vprint(intro) 134 135 while not self.shutdown.isSet(): 136 try: 137 Cmd.cmdloop(self, intro=intro) 138 except: 139 traceback.print_exc()
140
141 - def onecmd(self, line):
142 lines = line.split("&&") 143 try: 144 for line in lines: 145 line = self.doAlias(line) 146 Cmd.onecmd(self, line) 147 except SystemExit: 148 raise 149 except Exception, msg: 150 self.vprint(traceback.format_exc()) 151 self.vprint("\nERROR: (%s) %s" % (msg.__class__.__name__,msg)) 152 153 if self.shutdown.isSet(): 154 return True
155
156 - def do_EOF(self, line):
157 self.vprint("Use quit")
158
159 - def do_quit(self, line):
160 """ 161 Quit 162 163 Usage: quit 164 """ 165 self.shutdown.set()
166
167 - def do_config(self, line):
168 """ 169 Show or edit a config option from the command line 170 171 Usage: config [-S section] [option=value] 172 """ 173 argv = splitargs(line) 174 secname = None 175 try: 176 opts,args = getopt(argv, "S:") 177 for opt,optarg in opts: 178 if opt == "-S": 179 secname = optarg 180 181 except Exception, e: 182 print e 183 return self.do_help("config") 184 185 if len(args) > 1: 186 return self.do_help("config") 187 188 if len(args) == 0: 189 if secname != None: 190 secs = [secname,] 191 else: 192 secs = self.config.sections() 193 194 for secname in secs: 195 self.vprint("") 196 self.vprint("[%s]" % secname) 197 for oname in self.config.options(secname): 198 val = self.config.get(secname, oname) 199 self.vprint("%s=%s" % (oname, val)) 200 201 else: 202 if secname == None: 203 secname = "" 204 key,val = args[0].split("=",1) 205 self.config.set(secname, key, val) 206 self.vprint("[%s] %s = %s" % (secname,key,val))
207
208 - def do_alias(self, line):
209 """ 210 Add an alias to the command line interpreter's aliases dictionary 211 212 Usage: alias <alias_word> rest of the alias command 213 To delete an alias: 214 Usage: alias <alias_word> 215 """ 216 if len(line): 217 row = line.split(None, 1) 218 if len(row) == 1: 219 self.config.remove_option("Aliases",row[0]) 220 else: 221 self.config.set("Aliases",row[0],row[1]) 222 223 self.vprint("Current Aliases:\n") 224 225 opts = self.config.options("Aliases") 226 opts.sort() 227 228 for opt in opts: 229 val = self.config.get("Aliases", opt) 230 self.vprint("%s -> %s" % (opt,val)) 231 self.vprint("") 232 return
233
234 - def do_python(self, line):
235 """ 236 Start an interactive python interpreter. The namespace of the 237 interpreter is updated with expression nicities. You may also 238 specify a line of python code as an argument to be exec'd without 239 beginning an interactive python interpreter on the controlling 240 terminal. 241 242 Usage: python [pycode] 243 """ 244 locals = self.getExpressionLocals() 245 if len(line) != 0: 246 cobj = compile(line, 'cli_input', 'exec') 247 exec(cobj, locals) 248 else: 249 code.interact(local=locals)
250
251 - def parseExpression(self, expr):
252 l = self.getExpressionLocals() 253 return long(e_expr.evaluate(expr, l))
254
255 - def do_binstr(self, line):
256 ''' 257 Display a binary representation of the given value expression 258 (padded to optional width in bits) 259 260 Usage: binstr <val_expr> [<bitwidth_expr>] 261 ''' 262 argv = splitargs(line) 263 if len(argv) == 0: 264 return self.do_help('binstr') 265 bitwidth = None 266 value = self.parseExpression(argv[0]) 267 if len(argv) > 1: 268 bitwidth = self.parseExpression(argv[1]) 269 binstr = e_bits.binrepr(value, bitwidth=bitwidth) 270 self.canvas.addText("0x%.8x (%d) %s\n" % (value, value, binstr))
271
272 - def do_eval(self, line):
273 """ 274 Evaluate an expression on the CLI to show it's value. 275 276 Usage: eval (ecx+edx)/2 277 """ 278 if not line: 279 return self.do_help("eval") 280 value = self.parseExpression(line) 281 282 self.canvas.addText("%s = " % line) 283 if self.memobj.isValidPointer(value): 284 self.canvas.addVaText("0x%.8x" % value, value) 285 sym = self.symobj.getSymByAddr(value, exact=False) 286 if sym != None: 287 self.canvas.addText(" ") 288 self.canvas.addVaText("%s + %d" % (repr(sym),value-long(sym)), value) 289 else: 290 self.canvas.addText("0x%.8x (%d)" % (value, value)) 291 292 self.canvas.addText("\n")
293
294 - def do_script(self, line):
295 """ 296 Execute a python file. 297 298 The script file is arbitrary python code which is run with the 299 full compliment of expression extensions mapped in as locals. 300 301 NOTE: additional command line arguments may be passed in and will 302 appear as the list "argv" in the script namespace! (They will 303 all be strings) 304 305 Usage: script <scriptfile> [<argv[0]>, ...] 306 """ 307 if len(line) == 0: 308 return self.do_help("script") 309 310 argv = splitargs(line) 311 locals = self.getExpressionLocals() 312 locals['argv'] = argv 313 script = file(argv[0]).read() 314 cobj = compile(script, argv[0], "exec") 315 exec(cobj, locals)
316
317 - def do_maps(self, line):
318 """ 319 Display either a list of all the memory maps or the memory map 320 details for the given address expression. 321 322 Usage: maps [addr_expression] 323 """ 324 argv = splitargs(line) 325 if len(argv): 326 expr = " ".join(argv) 327 va = self.parseExpression(expr) 328 map = self.memobj.getMemoryMap(va) 329 if map == None: 330 self.vprint("Memory Map Not Found For: 0x%.8x"%va) 331 332 else: 333 addr,size,perm,fname = map 334 pname = e_mem.reprPerms(perm) 335 self.canvas.addText("Memory Map For: ") 336 self.canvas.addVaText("0x%.8x" % va, va) 337 self.canvas.addText("\n") 338 self.canvas.addVaText("0x%.8x" % addr, addr) 339 self.canvas.addText("\t%d\t%s\t%s\n" % (size,pname,fname)) 340 else: 341 totsize = 0 342 self.vprint("[ address ] [ size ] [ perms ] [ File ]") 343 for addr,size,perm,fname in self.memobj.getMemoryMaps(): 344 pname = e_mem.reprPerms(perm) 345 totsize += size 346 self.canvas.addVaText("0x%.8x" % addr, addr) 347 sizestr = ("%dK" % (size/1024,)).rjust(8) 348 self.canvas.addText("%s\t%s\t%s\n" % (sizestr,pname,fname)) 349 self.vprint("Total Virtual Memory: %.2f MB" % ((float(totsize)/1024)/1024))
350
351 - def do_search(self, line):
352 """ 353 Search memory for patterns. 354 355 Usage: search [options] <pattern> 356 -e Encode the pattern with a codec (ie utf-16le, hex, etc) 357 -E The specified pattern is an expression (search for numeric values) 358 -r The specified pattern is a regular expression 359 -R <baseexpr:sizeexpr> Search a specific range only. 360 -X The specified pattern is in hex (ie. 414141424242 is AAABBB) 361 """ 362 if len(line) == 0: 363 return self.do_help("search") 364 365 range = None 366 dohex = False 367 doexpr = False 368 regex = False 369 encode = None 370 371 argv = splitargs(line) 372 try: 373 opts,args = getopt(argv, "e:ER:rX") 374 except: 375 return self.do_help("search") 376 377 for opt,optarg in opts: 378 if opt == '-E': 379 doexpr = True 380 elif opt == '-e': 381 encode = optarg 382 elif opt == '-R': 383 range = optarg 384 elif opt == '-r': 385 regex = True 386 elif opt == '-X': 387 dohex = True 388 389 pattern = " ".join(args) 390 if doexpr: 391 import struct #FIXME see below 392 sval = self.parseExpression(pattern) 393 pattern = struct.pack("<L", sval) # FIXME 64bit (and alt arch) 394 if dohex: pattern = pattern.decode('hex') 395 if encode: pattern = pattern.encode(encode) 396 if range: 397 try: 398 addrexpr, sizeexpr = range.split(":") 399 except Exception, e: 400 return self.do_help("search") 401 addr = self.parseExpression(addrexpr) 402 size = self.parseExpression(sizeexpr) 403 self.canvas.addText("Searching from ") 404 self.canvas.addVaText("0x%.8x", addr) 405 self.canvas.addText(" for %d bytes\n" % size) 406 res = self.memobj.searchMemoryRange(pattern, addr, size, regex=regex) 407 else: 408 self.vprint("Searching all memory...") 409 res = self.memobj.searchMemory(pattern, regex=regex) 410 411 if len(res) == 0: 412 self.vprint('Pattern Not Found: %s' % pattern.encode('hex')) 413 return 414 415 self.vprint('Matches for: %s' % pattern.encode('hex')) 416 for r in res: 417 self.canvas.addVaText("0x%.8x" % r, r) 418 self.canvas.addText(": ") 419 420 mbase,msize,mperm,mfile = self.memobj.getMemoryMap(r) 421 pname = e_mem.reprPerms(mperm) 422 423 self.canvas.addText("%s " % pname) 424 425 sname = self.reprPointer(r) 426 427 self.canvas.addText(sname) 428 self.canvas.addText("\n")
429
430 - def reprPointer(self, va):
431 """ 432 Do your best to create a humon readable name for the 433 value of this pointer. 434 """ 435 if va == 0: 436 return "NULL" 437 438 mbase,msize,mperm,mfile = self.memobj.getMemoryMap(va) 439 ret = mfile 440 sym = self.symobj.getSymByAddr(va, exact=False) 441 if sym != None: 442 ret = "%s + %d" % (repr(sym),va-long(sym)) 443 return ret
444
445 - def do_memdump(self, line):
446 """ 447 Dump memory out to a file. 448 449 Usage: memdump <addr_expression> <size_expression> <filename> 450 """ 451 if len(line) == 0: 452 return self.do_help("memdump") 453 454 argv = splitargs(line) 455 if len(argv) != 3: 456 return self.do_help("memdump") 457 458 addr = self.parseExpression(argv[0]) 459 size = self.parseExpression(argv[1]) 460 461 mem = self.memobj.readMemory(addr, size) 462 file(argv[2], "wb").write(mem) 463 self.vprint("Wrote %d bytes!" % len(mem))
464
465 - def do_memcmp(self, line):
466 ''' 467 Compare memory at the given locations. Outputs a set of 468 differences showing bytes at their given offsets.... 469 470 Usage: memcmp <addr_expr1> <addr_expr2> <size_expr> 471 ''' 472 if len(line) == 0: 473 return self.do_help('memcmp') 474 475 argv = splitargs(line) 476 if len(argv) != 3: 477 return self.do_help('memcmp') 478 479 addr1 = self.parseExpression(argv[0]) 480 addr2 = self.parseExpression(argv[1]) 481 size = self.parseExpression(argv[2]) 482 483 bytes1 = self.memobj.readMemory(addr1, size) 484 bytes2 = self.memobj.readMemory(addr2, size) 485 486 res = e_mem.memdiff(bytes1, bytes2) 487 if len(res) == 0: 488 self.vprint('No Differences!') 489 return 490 491 for offset, offsize in res: 492 diff1 = addr1+offset 493 diff2 = addr2+offset 494 self.canvas.addText('==== %d byte difference at offset %d\n' % (offsize,offset)) 495 self.canvas.addVaText("0x%.8x" % diff1, diff1) 496 self.canvas.addText(":") 497 self.canvas.addText(bytes1[offset:offset+offsize].encode('hex')) 498 self.canvas.addText('\n') 499 self.canvas.addVaText("0x%.8x" % diff2, diff2) 500 self.canvas.addText(":") 501 self.canvas.addText(bytes2[offset:offset+offsize].encode('hex')) 502 self.canvas.addText('\n')
503
504 - def do_mem(self, line):
505 """ 506 Show some memory (with optional formatting and size) 507 508 Usage: mem [-F <format>] <addr expression> [size] 509 510 NOTE: use -F ? for a list of the formats 511 """ 512 fmtname = "bytes" 513 514 if len(line) == 0: 515 return self.do_help("mem") 516 517 argv = splitargs(line) 518 try: 519 opts,args = getopt(argv, "F:") 520 except: 521 return self.do_help("mem") 522 523 for opt,optarg in opts: 524 if opt == "-F": 525 fmtname = optarg 526 fnames = self.canvas.getRendererNames() 527 528 if fmtname == "?": 529 self.vprint("Registered renderers:") 530 for name in fnames: 531 self.vprint(name) 532 return 533 534 if fmtname not in fnames: 535 self.vprint("Unknown renderer: %s" % fmtname) 536 return 537 538 if len(args) == 0: 539 return self.do_help("mem") 540 541 size = 256 542 addr = self.parseExpression(args[0]) 543 if len(args) == 2: 544 size = self.parseExpression(args[1]) 545 546 self.canvas.setRenderer(fmtname) 547 self.canvas.renderMemory(addr, size)
548
549 -class EnviMutableCli(EnviCli):
550 """ 551 Cli extensions which require a mutable memory object 552 (emulator/trace) rather than a static one (viv workspace) 553 """ 554
555 - def do_memcpy(self, line):
556 ''' 557 Copy memory from one location to another... 558 559 Usage: memcpy <dest_expr> <src_expr> <size_expr> 560 ''' 561 argv = splitargs(line) 562 if len(argv) != 3: 563 return self.do_help('memcpy') 564 565 566 dst = self.parseExpression(argv[0]) 567 src = self.parseExpression(argv[1]) 568 siz = self.parseExpression(argv[2]) 569 570 mem = self.memobj.readMemory(src, siz) 571 self.memobj.writeMemory(dst, mem)
572
573 - def do_memprotect(self, line):
574 """ 575 Change the memory permissions of a given page/map. 576 577 Usage: memprotect [options] <addr_expr> <perms> 578 -S <size> Specify the size of the region to change (default == whole memory map) 579 <perms> = "rwx" string "rw", "rx" "rwx" etc... 580 """ 581 if len(line) == 0: 582 return self.do_help("memprotect") 583 584 size = None 585 argv = splitargs(line) 586 try: 587 opts, args = getopt(argv, "S:") 588 except Exception, e: 589 return self.do_help("memprotect") 590 591 for opt,optarg in opts: 592 if opt == "-S": 593 size = self.parseExpression(optarg) 594 595 if len(args) != 2: 596 return self.do_help("memprotect") 597 598 599 addr = self.parseExpression(args[0]) 600 perm = e_mem.parsePerms(args[1]) 601 602 if size == None: 603 map = self.memobj.getMemoryMap(addr) 604 if map == None: 605 raise Exception("Unknown memory map for 0x%.8x" % addr) 606 size = map[1] 607 608 self.memobj.protectMemory(addr, size, perm)
609
610 - def do_writemem(self, args):
611 """ 612 Over-write some memory in the target address space. 613 Usage: writemem [options] <addr expression> <string> 614 -X The specified string is in hex (ie 414141 = AAA) 615 -U The specified string needs to be unicode in mem (AAA -> 410041004100) 616 """ 617 dohex = False 618 douni = False 619 620 try: 621 argv = splitargs(args) 622 opts,args = getopt(argv, "XU") 623 except: 624 return self.do_help("writemem") 625 626 if len(args) != 2: 627 return self.do_help("writemem") 628 629 for opt,optarg in opts: 630 if opt == "-X": 631 dohex = True 632 elif opt == "-U": 633 douni = True 634 635 exprstr, memstr = args 636 if dohex: memstr = memstr.decode('hex') 637 if douni: memstr = ("\x00".join(memstr)) + "\x00" 638 639 addr = self.parseExpression(exprstr) 640 self.memobj.writeMemory(addr, memstr)
641