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
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
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
36 """
37 This is used to work around the difference
38 between functions and bound methods for extended
39 command modules
40 """
42 self.cli = cli
43 self.func = func
44 self.__doc__ = func.__doc__
45
47 return self.func(self.cli, line)
48
49 cfgdefs = """
50
51 [Aliases]
52
53 """
54
56
57 - def __init__(self, memobj, config=None, symobj=None):
76
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
89
91 ret = []
92 ret.extend(Cmd.get_names(self))
93 ret.extend(self.extcmds.keys())
94 return ret
95
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
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
120 func = self.extcmds.get(name, None)
121 if func == None:
122 raise AttributeError(name)
123 return func
124
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
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
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
158
160 """
161 Quit
162
163 Usage: quit
164 """
165 self.shutdown.set()
166
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
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
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
254
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
293
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
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
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
392 sval = self.parseExpression(pattern)
393 pattern = struct.pack("<L", sval)
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
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
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
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
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
550 """
551 Cli extensions which require a mutable memory object
552 (emulator/trace) rather than a static one (viv workspace)
553 """
554
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
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
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