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

Source Code for Package vivisect

   1  """ 
   2   
   3  Yay!  It's NOT IDA!!!1!!1!one! 
   4   
   5  """ 
   6   
   7  import os 
   8  import sys 
   9  import time 
  10  import Queue 
  11  import string 
  12  import struct 
  13  import weakref 
  14  import hashlib 
  15  import itertools 
  16  import traceback 
  17  import threading 
  18  import collections 
  19   
  20  from StringIO import StringIO 
  21  from collections import deque 
  22  from ConfigParser import ConfigParser 
  23   
  24  import vivisect.contrib # This should go first 
  25   
  26  # The envi imports... 
  27  import vdb 
  28  import envi 
  29  import envi.bits as e_bits 
  30  import envi.memory as e_mem 
  31  import envi.config as e_config 
  32  import envi.bytesig as e_bytesig 
  33  import envi.symstore.resolver as e_resolv 
  34  import envi.symstore.symcache as e_symcache 
  35   
  36  import vstruct 
  37  import vstruct.cparse as vs_cparse 
  38  import vstruct.primitives as vs_prims 
  39   
  40  import vivisect.base as viv_base 
  41  import vivisect.parsers as viv_parsers 
  42  import vivisect.impemu.lookup as viv_imp_lookup 
  43   
  44  from vivisect.exc import * 
  45  from vivisect.const import * 
  46  from vivisect.defconfig import * 
  47   
48 -class VivWorkspace(e_mem.MemoryObject, viv_base.VivWorkspaceCore):
49
50 - def __init__(self):
51 52 e_mem.MemoryObject.__init__(self) 53 viv_base.VivWorkspaceCore.__init__(self) 54 55 self.vivhome = e_config.gethomedir(".viv") 56 self._viv_gui = None # If a gui is running, he will put a ref here... 57 58 self.saved = True 59 self.rchan = None 60 self.server = None 61 self.verbose = False 62 self.chanids = itertools.count() 63 64 self.arch = None # The placeholder for the Envi architecture module 65 self.psize = None # Used so much, optimization is appropriate 66 67 cfgpath = os.path.join(self.vivhome,'viv.json') 68 self.config = e_config.EnviConfig( filename=cfgpath, defaults=defconfig, docs=docconfig ) 69 70 # Ideally, *none* of these are modified except by _handleFOO funcs... 71 self.segments = [] 72 self.exports = [] 73 self.imports = [] 74 self.codeblocks = [] 75 self.relocations = [] 76 77 self.xrefs = [] 78 self.xrefs_by_to = {} 79 self.xrefs_by_from = {} 80 81 # XXX - make config option 82 self.greedycode = 0 83 84 self.metadata = {} 85 self.comments = {} # Comment by VA. 86 self.symhints = {} 87 88 self.filemeta = {} # Metadata Dicts stored by filename 89 self.transmeta = {} # Metadata that is *not* saved/evented 90 91 self.cfctx = viv_base.VivCodeFlowContext(self) 92 93 self.va_by_name = {} 94 self.name_by_va = {} 95 self.codeblocks_by_funcva = {} 96 self.exports_by_va = {} 97 self.colormaps = {} 98 self.vasetdefs = {} 99 self.vasets = {} 100 self.reloc_by_va = {} 101 102 self.func_args = {} 103 self.funcmeta = {} # Function metadata stored in the workspace 104 self.frefs = {} 105 106 # Extended analysis modules 107 self.amods = {} 108 self.amodlist = [] 109 # Extended *function* analysis modules 110 self.fmods = {} 111 self.fmodlist = [] 112 113 self.chan_lookup = {} 114 self.nextchanid = 1 115 116 # The function entry signature decision tree 117 # FIXME add to export 118 self.sigtree = e_bytesig.SignatureTree() 119 self.siglist = [] 120 121 self._initEventHandlers() 122 123 # Some core meta types that exist 124 self.setMeta('NoReturnApis', {}) 125 self.setMeta('SymbolikImportEmulation', None) 126 127 # Default to basic file storage 128 self.setMeta("StorageModule", "vivisect.storage.basicfile") 129 130 # There are a few default va sets for use in analysis 131 self.addVaSet('EntryPoints', (('va',VASET_ADDRESS),)) 132 self.addVaSet('NoReturnCalls', (('va',VASET_ADDRESS),)) 133 self.addVaSet("Emulation Anomalies", (("va",VASET_ADDRESS),("Message",VASET_STRING))) 134 self.addVaSet("Bookmarks", (("va",VASET_ADDRESS),("Bookmark Name", VASET_STRING)))
135
136 - def verbprint(self, msg):
137 if self.verbose: 138 return self.vprint(msg)
139
140 - def vprint(self, msg):
141 print msg
142
143 - def getVivGui(self):
144 ''' 145 Return a reference to the vivisect GUI object for this workspace. If 146 the GUI is not running (aka, the workspace is being used programatically) 147 this routine returns None. 148 149 Example: 150 vwgui = vw.getVivGui() 151 if vwgui: 152 vwgui.doStuffAndThings() 153 ''' 154 return self._viv_gui
155
156 - def loadWorkspace(self, wsname):
157 mname = self.getMeta("StorageModule") 158 mod = self.loadModule(mname) 159 mod.loadWorkspace(self, wsname) 160 self.setMeta("StorageName", wsname) 161 # The event list thusfar came *only* from the load... 162 self._createSaveMark() 163 # Snapin our analysis modules 164 self._snapInAnalysisModules()
165
166 - def addFref(self, fva, va, idx, val):
167 """ 168 Add a reference from the operand at virtual address 'va' 169 index 'idx' to a function local offset. Positive values 170 (beginning with 0) are considered argument references. Negative 171 values are considered function local storage and are relative to 172 the stack pointer at function entry. 173 """ 174 # FIXME this should probably be an argument 175 r = (va,idx,val) 176 self._fireEvent(VWE_ADDFREF, r)
177
178 - def getFref(self, va, idx):
179 """ 180 Get back the fref value (or None) for the given operand index 181 from the instruction at va. 182 """ 183 return self.frefs.get((va,idx))
184
185 - def getEmulator(self, logwrite=False, logread=False):
186 """ 187 Get an instance of a WorkspaceEmulator for this workspace. 188 189 Use logread/logwrite to enable memory access tracking. 190 """ 191 plat = self.getMeta('Platform') 192 arch = self.getMeta('Architecture') 193 194 eclass = viv_imp_lookup.workspace_emus.get( (plat,arch) ) 195 if eclass == None: 196 eclass = viv_imp_lookup.workspace_emus.get(arch) 197 198 if eclass == None: 199 raise Exception("WorkspaceEmulation not supported on %s yet!" % arch) 200 201 return eclass(self, logwrite=logwrite, logread=logread)
202
203 - def addLibraryDependancy(self, libname):
204 """ 205 Add a *normalized* library name to the import search 206 chain for this binary. This is only needed for formats 207 whose imports don't explicitly state their library name. 208 """ 209 # FIXME this needs to be event enabled... either plumb it special, 210 # or allow the get/append/set race... 211 dl = self.getMeta("DepLibs", None) 212 if dl == None: 213 dl = [] 214 dl.append(libname) 215 self.setMeta("DepLibs", dl)
216
217 - def getLibraryDependancies(self):
218 ''' 219 Retrieve the list of *normalized* library dependancies. 220 ''' 221 dl = self.getMeta("DepLibs", None) 222 if dl == None: 223 return [] 224 return list(dl)
225
226 - def setComment(self, va, comment, check=False):
227 ''' 228 Set the humon readable comment for a given virtual. 229 Comments will be displayed by the code renderer, and 230 are an important part of this balanced breakfast. 231 232 Example: 233 vw.setComment(callva, "This actually calls FOO...") 234 ''' 235 if check and self.comments.get(va): 236 return 237 self._fireEvent(VWE_COMMENT, (va,comment))
238
239 - def getComment(self, va):
240 ''' 241 Returns the comment string (or None) for a given 242 virtual address. 243 244 Example: 245 cmnt = vw.getComment(va) 246 print('COMMENT: %s' % cmnt) 247 ''' 248 return self.comments.get(va)
249
250 - def getComments(self):
251 ''' 252 Retrieve all the comments in the viv workspace as 253 (va, cmnt) tuples. 254 255 Example: 256 for va,cmnt in vw.getComments(): 257 print 'Comment at 0x%.8x: %s' % (va, cmnt) 258 ''' 259 return self.comments.items()
260
261 - def addRelocation(self, va, rtype):
262 """ 263 Add a relocation entry for tracking. 264 """ 265 self._fireEvent(VWE_ADDRELOC, (va, rtype))
266
267 - def getRelocations(self):
268 """ 269 Get the current list of relocation entries. 270 """ 271 return self.relocations
272
273 - def getRelocation(self, va):
274 """ 275 Return the type of relocation at the specified 276 VA or None if there isn't a relocation entry for 277 the address. 278 """ 279 return self.reloc_by_va.get(va)
280
281 - def pointerString(self, va):
282 return self.arch.pointerString(va)
283
284 - def getAnalysisModuleNames(self):
285 return list(self.amodlist)
286
287 - def getFuncAnalysisModuleNames(self):
288 return list(self.fmodlist)
289
290 - def addFunctionSignatureBytes(self, bytes, mask=None):
291 """ 292 Add a function signature entry by bytes. This is mostly used by 293 file parsers/loaders to manually tell the workspace about known 294 entry signature types. 295 296 see envi.bytesig for details. 297 """ 298 self.sigtree.addSignature(bytes, mask) 299 self.siglist.append((bytes,mask))
300
301 - def isFunctionSignature(self, va):
302 """ 303 Check if the specified va is a function entry signature 304 according to the current entry point signature tree... 305 """ 306 if not self.isValidPointer(va): 307 return False 308 offset, bytes = self.getByteDef(va) 309 return self.sigtree.isSignature(bytes, offset=offset)
310
311 - def addNoReturnApi(self, funcname):
312 """ 313 Inform vivisect code-flow disassembly that any call target 314 which matches the specified name ("funcname" or "libname.funcname" 315 for imports) does *not* exit and code-flow should be stopped... 316 """ 317 funcname = funcname.lower() 318 m = self.getMeta('NoReturnApis', {}) 319 m[funcname] = True 320 self.setMeta('NoReturnApis', m) 321 322 # If we already have an import entry, we need to update codeflow 323 for lva,lsize,ltype,linfo in self.getImports(): 324 if linfo.lower() != funcname: 325 continue 326 self.cfctx.addNoReturnAddr( lva )
327
328 - def addAnalysisModule(self, modname):
329 """ 330 Add an analysis module by python import path 331 """ 332 if self.amods.has_key(modname): 333 return 334 mod = self.loadModule(modname) 335 self.amods[modname] = mod 336 self.amodlist.append(modname)
337
338 - def delAnalysisModule(self, modname):
339 """ 340 Remove an analysis module from the list used during analysis() 341 """ 342 if not self.amods.has_key(modname): 343 raise Exception("Unknown Module in delAnalysisModule: %s" % modname) 344 x = self.amods.pop(modname, None) 345 if x != None: 346 self.amodlist.remove(modname)
347
348 - def loadModule(self, modname):
349 __import__(modname) 350 return sys.modules[modname]
351
352 - def addFuncAnalysisModule(self, modname):
353 """ 354 Snap in a per-function analysis module (by name) which 355 will be triggered during the creation of a new function 356 (makeFunction). 357 """ 358 if self.fmods.has_key(modname): 359 return 360 mod = self.loadModule(modname) 361 self.fmods[modname] = mod 362 self.fmodlist.append(modname)
363
364 - def delFuncAnalysisModule(self, modname):
365 ''' 366 Remove a currently registered function analysis module. 367 368 Example: 369 vw.delFuncAnalysisModule('mypkg.mymod') 370 ''' 371 if not self.fmods.has_key(modname): 372 raise Exception("Unknown Module in delAnalysisModule: %s" % modname) 373 x = self.fmods.pop(modname, None) 374 if x != None: 375 self.fmodlist.remove(modname)
376
377 - def createEventChannel(self):
378 chanid = self.chanids.next() 379 self.chan_lookup[chanid] = Queue.Queue() 380 return chanid
381
382 - def importWorkspace(self, wsevents):
383 """ 384 Import and initialize data from the given vivisect workspace 385 export. 386 """ 387 # During import, if we have a server, be sure not to notify 388 # the server about the events he just gave us... 389 local = False 390 if self.server != None: 391 local = True 392 393 # Process the events from the import data... 394 fe = self._fireEvent 395 for event, einfo in wsevents: 396 fe(event, einfo, local=local) 397 return
398
399 - def exportWorkspace(self):
400 ''' 401 Return the (probably big) list of events which define this 402 workspace. 403 ''' 404 return self._event_list
405
406 - def exportWorkspaceChanges(self):
407 ''' 408 Export the list of events which have been applied to the 409 workspace since the last save. 410 ''' 411 return self._event_list[self._event_saved:]
412
413 - def initWorkspaceClient(self, remotevw):
414 """ 415 Initialize this workspace as a workspace 416 client to the given (potentially cobra remote) 417 workspace object. 418 """ 419 uname = e_config.getusername() 420 self.server = remotevw 421 self.rchan = remotevw.createEventChannel() 422 423 self.server.vprint('%s connecting...' % uname) 424 wsevents = self.server.exportWorkspace() 425 self.importWorkspace(wsevents) 426 self.server.vprint('%s connection complete!' % uname) 427 428 thr = threading.Thread(target=self._clientThread) 429 thr.setDaemon(True) 430 thr.start()
431
432 - def _clientThread(self):
433 """ 434 The thread that monitors events on a server to stay 435 in sync. 436 """ 437 if self.server == None: 438 raise Exception("_clientThread() with no server?!?!") 439 440 while self.server != None: 441 event, einfo = self.server.waitForEvent(self.rchan) 442 self._fireEvent(event, einfo, local=True)
443
444 - def waitForEvent(self, chanid, timeout=None):
445 """ 446 Return an event,eventinfo tuple. 447 """ 448 q = self.chan_lookup.get(chanid) 449 if q == None: 450 raise Exception("Invalid Channel") 451 return q.get(timeout=timeout)
452
453 - def deleteEventChannel(self, chanid):
454 """ 455 Remove a previously allocated event channel from 456 the workspace. 457 """ 458 self.chan_lookup.pop(chanid)
459
460 - def reprVa(self, va):
461 """ 462 A quick way for scripts to get a string for a given virtual address. 463 """ 464 loc = self.getLocation(va) 465 if loc != None: 466 return self.reprLocation(loc) 467 return "None"
468
469 - def reprLocation(self, loctup):
470 lva,lsize,ltype,tinfo = loctup 471 if ltype == LOC_OP: 472 op = self.parseOpcode(lva) 473 return repr(op) 474 475 elif ltype == LOC_STRING: 476 return repr(self.readMemory(lva, lsize)) 477 478 elif ltype == LOC_UNI: 479 #FIXME super ghetto "simple" unicode handling for now 480 bytes = self.readMemory(lva, lsize) 481 return "u'%s'" % string.join(bytes.split("\x00"),sep="") 482 483 elif ltype == LOC_STRUCT: 484 lstruct = self.getStructure(lva, tinfo) 485 return repr(lstruct) 486 487 elif ltype == LOC_NUMBER: 488 value = self.parseNumber(lva, lsize) 489 hexstr = "0x%%.%dx" % lsize 490 hexstr = hexstr % value 491 if lsize == 1: 492 return "BYTE: %d (%s)" % (value, hexstr) 493 else: 494 return "%d BYTES: %d (%s)" % (lsize, value, hexstr) 495 496 elif ltype == LOC_IMPORT: 497 return "IMPORT: %s" % tinfo 498 499 elif ltype == LOC_POINTER: 500 return "PTR: %s" % self.arch.pointerString(self.getXrefsFrom(lva)[0][XR_TO]) 501 502 else: 503 n = self.getName(lva) 504 if n != None: 505 return n 506 return self.readMemory(lva, lsize).encode('hex')
507
508 - def followPointer(self, va):
509 """ 510 Do pointer analysis and folllow up the recomendation 511 by creating locations etc... 512 """ 513 ltype = self.analyzePointer(va) 514 if ltype == None: 515 return False 516 517 # Note, we only implement the types possibly 518 # returned from analyzePointer... 519 if ltype == LOC_OP: 520 # NOTE: currently analyzePointer returns LOC_OP 521 # based on function entries, lets make a func too... 522 self.makeFunction(va) 523 return True 524 525 elif ltype == LOC_STRING: 526 self.makeString(va) 527 return True 528 529 elif ltype == LOC_UNI: 530 self.makeUnicode(va) 531 return True 532 533 return False
534
535 - def analyze(self):
536 """ 537 Call this to ask any available analysis modules 538 to do their thing... 539 """ 540 if self.verbose: self.vprint('Beginning analysis...') 541 if self.verbose: self.vprint('...analyzing exports.') 542 543 starttime = time.time() 544 for eva in self.getEntryPoints(): 545 if self.isFunction(eva): 546 continue 547 if not self.probeMemory(eva, 1, e_mem.MM_EXEC): 548 continue 549 self.makeFunction(eva) 550 551 # Now lets engage any extended analysis modules. If any modules return 552 # true, they managed to change things and we should run again... 553 for mname in self.amodlist: 554 mod = self.amods.get(mname) 555 if self.verbose: self.vprint("Extended Analysis: %s" % mod.__name__) 556 try: 557 mod.analyze(self) 558 except Exception, e: 559 if self.verbose: 560 traceback.print_exc() 561 self.verbprint("Extended Analysis Exception %s: %s" % (mod.__name__,e)) 562 563 endtime = time.time() 564 if self.verbose: self.vprint('...analysis complete! (%d sec)' % (endtime-starttime)) 565 self._fireEvent(VWE_AUTOANALFIN, (endtime, starttime))
566
567 - def getImports(self):
568 """ 569 Return a list of imports in location tuple format. 570 """ 571 return self.getLocations(LOC_IMPORT)
572
573 - def makeImport(self, va, libname, impname):
574 """ 575 Add an import entry. 576 """ 577 if libname != '*': 578 libname = self.normFileName(libname) 579 tinfo = "%s.%s" % (libname, impname) 580 self.makeName(va, "%s_%.8x" % (tinfo, va)) 581 return self.addLocation(va, self.psize, LOC_IMPORT, tinfo=tinfo)
582
583 - def getExports(self):
584 """ 585 Return a list of exports in (va,etype,name,filename) tuples. 586 """ 587 return list(self.exports)
588
589 - def addExport(self, va, etype, name, filename):
590 """ 591 Add an already created export object. 592 """ 593 rname = "%s.%s" % (filename,name) 594 if self.vaByName(rname) != None: 595 raise Exception("Duplicate Name: %s" % rname) 596 self._fireEvent(VWE_ADDEXPORT, (va,etype,name,filename))
597
598 - def getExport(self, va):
599 """ 600 Get a reference to the export object at the given va 601 (or none). 602 """ 603 return self.exports_by_va.get(va)
604
605 - def findPointers(self, cache=True):
606 """ 607 Search through all currently "undefined" space and see 608 if you can find pointers there... Returns a list of tuples 609 where the tuple is (<ptr at>,<pts to>). 610 """ 611 if cache: 612 ret = self.getTransMeta('findPointers') 613 if ret != None: 614 # Filter locations added since last run... 615 ret = [ (va,x) for (va,x) in ret if self.getLocation(va) == None ] 616 self.setTransMeta('findPointers',ret) 617 return ret 618 619 ret = [] 620 size = self.psize 621 622 for mva, msize, mperm, mname in self.getMemoryMaps(): 623 624 offset, bytes = self.getByteDef(mva) 625 maxsize = len(bytes) - size 626 627 while offset + size < maxsize: 628 va = mva + offset 629 loctup = self.getLocation(va) 630 if loctup != None: 631 offset += loctup[L_SIZE] 632 continue 633 634 x = e_bits.parsebytes(bytes, offset, size) 635 if self.isValidPointer(x): 636 ret.append((va, x)) 637 offset += size 638 continue 639 640 offset += 1 641 642 if cache: 643 self.setTransMeta('findPointers', ret) 644 645 return ret
646
647 - def isProbablyString(self, va):
648 offset, bytes = self.getByteDef(va) 649 maxlen = len(bytes) - offset 650 count = 0 651 while count < maxlen: 652 # If we hit another thing, then probably not... 653 if self.getLocation(va+count) != None: 654 return False 655 c = bytes[offset+count] 656 # The "strings" algo basically says 4 or more... 657 if ord(c) == 0 and count >= 4: 658 return True 659 if c not in string.printable: 660 return False 661 count += 1 662 return False
663
664 - def isProbablyUnicode(self, va):
665 """ 666 This will return true if the memory location is likely 667 *simple* UTF16-LE unicode (<ascii><0><ascii><0><0><0>). 668 """ 669 #FIXME this totally sucks... 670 671 offset, bytes = self.getByteDef(va) 672 maxlen = len(bytes) + offset 673 count = 0 674 while count < maxlen: 675 if self.getLocation(va+count) != None: 676 return False 677 678 c0 = bytes[offset+count] 679 c1 = bytes[offset+count+1] 680 681 # If it's not null,char,null,char then it's 682 # not simple unicode... 683 if ord(c1) != 0: 684 return False 685 686 # If we find our null terminator after more 687 # than 4 chars, we're probably a real string 688 if ord(c0) == 0: 689 if count > 8: 690 return True 691 return False 692 693 # If the first byte char isn't printable, then 694 # we're probably not a real "simple" ascii string 695 if c0 not in string.printable: 696 return False 697 698 count += 2
699
700 - def isProbablyCode(self, va):
701 """ 702 Most of the time, absolute pointes which point to code 703 point to the function entry, so test it for the sig. 704 """ 705 if not self.isExecutable(va): 706 return False 707 return self.isFunctionSignature(va)
708 709 ################################################################# 710 # 711 # Opcode API 712 #
713 - def parseOpcode(self, va, arch=envi.ARCH_DEFAULT):
714 ''' 715 Parse an opcode from the specified virtual address. 716 717 Example: op = m.parseOpcode(0x7c773803) 718 719 note: differs from the IMemory interface by checking loclist 720 ''' 721 b = self.readMemory(va, 16) 722 if arch == envi.ARCH_DEFAULT: 723 loctup = self.getLocation(va) 724 # XXX - in the case where we've set a location on what should be an 725 # opcode lets make sure L_LTYPE == LOC_OP if not lets reset L_TINFO = original arch param 726 # so that at least parse opcode wont fail 727 if loctup != None and loctup[ L_TINFO ] and loctup[ L_LTYPE ] == LOC_OP: 728 arch = loctup[ L_TINFO ] 729 730 return self.imem_archs[ (arch & envi.ARCH_MASK) >> 16 ].archParseOpcode(b, 0, va)
731
732 - def makeOpcode(self, va, op=None, arch=envi.ARCH_DEFAULT):
733 """ 734 Create a single opcode location. If you have already parsed the 735 opcode object, you may pass it in. 736 """ 737 if op == None: 738 try: 739 740 op = self.parseOpcode(va, arch=arch) 741 742 except envi.InvalidInstruction, msg: 743 #FIXME something is just not right about this... 744 bytes = self.readMemory(va, 16) 745 print "Invalid Instruct Attempt At:",hex(va),bytes.encode("hex") 746 raise InvalidLocation(va,msg) 747 748 except Exception, msg: 749 traceback.print_exc() 750 raise InvalidLocation(va,msg) 751 752 # Add our opcode location first (op flags become ldata) 753 loc = self.addLocation(va, op.size, LOC_OP, op.iflags) 754 755 # This takes care of all normal indirect immediates 756 757 brdone = {} 758 brlist = op.getBranches() 759 for tova,bflags in brlist: 760 761 # If there were unresolved dynamic branches, oh well... 762 if tova == None: continue 763 if not self.isValidPointer(tova): continue 764 765 brdone[tova] = True 766 767 # Special case, if it's a table branch, lets resolve it now. 768 if bflags & envi.BR_TABLE: 769 ptrbase = tova 770 rdest = self.castPointer(ptrbase) 771 772 i = 0 773 tabdone = {} 774 while self.isValidPointer(rdest): 775 776 if not tabdone.get(rdest): 777 tabdone[rdest] = True 778 self.addXref(va, rdest, REF_CODE, envi.BR_COND) 779 if self.getName(rdest) == None: 780 self.makeName(rdest, "case%d_%.8x" % (i,rdest)) 781 782 ptrbase += self.psize 783 if len(self.getXrefsTo(ptrbase)): 784 break # Another xref means not our table anymore 785 i += 1 786 rdest = self.castPointer(ptrbase) 787 788 # This must be second (len(xrefsto)) 789 self.addXref(va, tova, REF_PTR, None) 790 791 elif bflags & envi.BR_DEREF: 792 793 self.addXref(va, tova, REF_DATA) 794 ptrdest = None 795 if self.getLocation(tova) == None: 796 ptrdest = self.makePointer(tova, follow=False) 797 798 # If the actual dest is executable, make a code ref fixup 799 # which *removes* the deref flag... 800 if ptrdest and self.probeMemory(ptrdest, 1, e_mem.MM_EXEC): 801 self.addXref(va, ptrdest, REF_CODE, bflags & ~envi.BR_DEREF) 802 else: 803 self.addXref(va, tova, REF_CODE, bflags) 804 805 else: 806 # vivisect does NOT create REF_CODE entries for 807 # instruction fall through 808 if bflags & envi.BR_FALL: continue 809 810 self.addXref(va, tova, REF_CODE, bflags) 811 812 # Check the instruction for static d-refs 813 for o in op.opers: 814 # FIXME it would be nice if we could just do this one time 815 # in the emulation pass (or hint emulation that some have already 816 # been done. 817 818 # Does the operand touch memory ? 819 if o.isDeref(): 820 821 ref = o.getOperAddr(op, None) 822 823 if brdone.get(ref, False): 824 continue 825 826 if ref != None and self.isValidPointer(ref): 827 828 # It's a data reference. lets also check if the data is 829 # a pointer. 830 831 self.addXref(va, ref, REF_DATA) 832 833 # If we don't already know what type this location is, 834 # lets make it either a pointer or a number... 835 if self.getLocation(ref) == None: 836 837 offset, bytes = self.getByteDef(ref) 838 839 val = e_bits.parsebytes(bytes, offset, o.tsize) 840 841 if (self.psize == o.tsize and self.isValidPointer(val)): 842 self.makePointer(ref, tova=val) 843 else: 844 self.makeNumber(ref, o.tsize) 845 846 else: 847 ref = o.getOperValue(op) 848 if brdone.get(ref, False): 849 continue 850 if ref != None and self.isValidPointer(ref): 851 self.addXref(va, ref, REF_PTR) 852 853 return loc
854
855 - def makeCode(self, va, arch=envi.ARCH_DEFAULT):
856 """ 857 Attempt to begin code-flow based disassembly by 858 starting at the given va. The va will be made into 859 an OpcodeLoc and refs will be walked continuing to 860 make code where possible. 861 """ 862 # If this is already a location, bail. 863 if self.isLocation(va): 864 return 865 866 calls_from = self.cfctx.addCodeFlow(va, arch=arch)
867 868 ################################################################# 869 # 870 # Function API 871 # 872
873 - def isFunction(self, funcva):
874 """ 875 Return True if funcva is a function entry point. 876 """ 877 return self.funcmeta.get(funcva) != None
878
879 - def getFunctions(self):
880 """ 881 Return a list of the function virtual addresses 882 defined in the workspace. 883 """ 884 return self.funcmeta.keys()
885
886 - def getFunction(self, va):
887 """ 888 Return the VA for this function. This will search code blocks 889 and check for a function va. 890 """ 891 if self.funcmeta.get(va) != None: 892 return va 893 cbtup = self.getCodeBlock(va) 894 if cbtup != None: 895 return cbtup[CB_FUNCVA] 896 return None
897
898 - def makeFunction(self, va, meta=None, arch=envi.ARCH_DEFAULT):
899 """ 900 Do parsing for function information and add a new function doodad. 901 This function should probably only be called once code-flow for the 902 area is complete. 903 """ 904 if self.isFunction(va): 905 return 906 907 if meta == None: 908 meta = {} 909 910 if not self.isValidPointer(va): 911 raise InvalidLocation(va) 912 913 loc = self.getLocation(va) 914 if loc != None and loc[L_TINFO] != None and loc[L_LTYPE] == LOC_OP: 915 arch = loc[L_TINFO] 916 917 self.cfctx.addEntryPoint(va, arch=arch)
918
919 - def delFunction(self, funcva):
920 """ 921 Remove a function, it's code blocks and all associated meta 922 """ 923 if self.funcmeta.get(funcva) == None: 924 raise InvalidLocation(funcva) 925 926 self._fireEvent(VWE_DELFUNCTION, funcva)
927
928 - def setFunctionArg(self, fva, idx, atype, aname):
929 ''' 930 Set the name and type information for a single function arguemnt by index. 931 932 Example: 933 # If we were setting up main... 934 vw.setFunctionArg(fva, 0, 'int','argc') 935 vw.setFunctionArg(fva, 1, 'char **','argv') 936 ''' 937 rettype,retname,callconv,callname,callargs = self.getFunctionApi(fva) 938 while len(callargs) <= idx: 939 callargs.append( ('int','arg%d' % len(callargs)) ) 940 941 callargs[idx] = (atype,aname) 942 self.setFunctionApi(fva, (rettype,retname,callconv,callname,callargs))
943
944 - def getFunctionArgs(self, fva):
945 ''' 946 Returns the list of (typename,argname) tuples which define the 947 arguments for the specified function. 948 949 Example: 950 for typename,argname in vw.getFunctionArgs(fva): 951 print('Takes: %s %s' % (typename,argname)) 952 ''' 953 rettype,retname,callconv,callname,callargs = self.getFunctionApi(fva) 954 return list(callargs)
955
956 - def getFunctionApi(self, fva):
957 ''' 958 Retrieve the API definition for the given function address. 959 960 Returns: an API tuple (similar to impapi subsystem) or None 961 ( rettype, retname, callconv, funcname, ( (argtype, argname), ...) ) 962 ''' 963 ret = self.getFunctionMeta(fva, 'api') 964 if ret != None: 965 return ret 966 967 defcall = self.getMeta('DefaultCall') 968 return ('void',None,defcall,None,())
969
970 - def setFunctionApi(self, fva, apidef):
971 ''' 972 Set a function's API definition. 973 NOTE: apidef is a tuple similar to the impapi subsystem 974 ( rettype, retname, callconv, funcname, ( (argtype, argname), ...) ) 975 976 Example: 977 apidef = ('int','size','stdcall','getThingSize', ( ('void *','thing'), )) 978 vw.setFunctionApi(fva, apidef) 979 ''' 980 self.setFunctionMeta(fva, 'api', apidef)
981
982 - def getFunctionLocals(self, fva):
983 ''' 984 Retrieve the list of (fva,spdelta,symtype,syminfo) tuples which 985 represent the given function's local memory offsets. 986 ''' 987 if not self.isFunction(fva): 988 raise InvalidFunction(fva) 989 return self.localsyms[fva].values()
990
991 - def getFunctionLocal(self, fva, spdelta):
992 ''' 993 Retrieve a function local symbol definition as a 994 (typename,symname) tuple or None if not found. 995 996 NOTE: If the local symbol references a LSYM_FARG, this API 997 will resolve the argument name/type from the function API 998 definition. 999 1000 Example: 1001 locsym = vw.getFunctionLocal(fva, 8) 1002 if locsym: 1003 symtype,symname = locsym 1004 print('%s %s;' % (symtype,symname)) 1005 ''' 1006 locsym = self.localsyms[fva].get(spdelta) 1007 if locsym == None: 1008 return None 1009 1010 fva,spdelta,symtype,syminfo = locsym 1011 if symtype == LSYM_NAME: 1012 return syminfo 1013 1014 if symtype == LSYM_FARG: 1015 1016 apidef = self.getFunctionApi(fva) 1017 if apidef == None: 1018 return None 1019 1020 funcargs = apidef[-1] 1021 if syminfo >= len(funcargs): 1022 return None 1023 1024 return funcargs[syminfo] 1025 1026 raise Exception('Unknown Local Symbol Type: %d' % symtype)
1027
1028 - def setFunctionLocal(self, fva, spdelta, symtype, syminfo):
1029 ''' 1030 Assign a local symbol within a function (addressed 1031 by delta from initial sp). For each symbol, a "symtype" 1032 and "syminfo" field are used to specify the details. 1033 1034 Example: 1035 # Setup a regular local integer 1036 vw.setFunctionLocal(fva, -4, LSYM_NAME, ('int','x')) 1037 1038 # Setup a link to a stack argument... (ie. i386 cdecl) 1039 vw.setFunctionLocal(fva, 4, LSYM_FARG, 0) 1040 1041 # Setup amd64 style shadow space 1042 vw.setFunctionLocal(fva, 8, LSYM_NAME, ('void *','shadow0')) 1043 ''' 1044 metaname = 'LocalSymbol:%d' % spdelta 1045 metavalue = (fva,spdelta,symtype,syminfo) 1046 self.setFunctionMeta(fva, metaname, metavalue)
1047
1048 - def setFunctionMeta(self, funcva, key, value):
1049 """ 1050 Set meta key,value pairs that describe a particular 1051 function (by funcva). 1052 1053 Example: vw.setFunctionMeta(fva, "WootKey", 10) 1054 """ 1055 if not self.isFunction(funcva): 1056 raise InvalidFunction(funcva) 1057 self._fireEvent(VWE_SETFUNCMETA, (funcva, key, value))
1058
1059 - def getFunctionMeta(self, funcva, key, default=None):
1060 m = self.funcmeta.get(funcva) 1061 if m == None: 1062 raise InvalidFunction(funcva) 1063 return m.get(key, default)
1064
1065 - def getFunctionMetaDict(self, funcva):
1066 """ 1067 Return the entire dictionary of function metadata 1068 for the function specified at funcva 1069 """ 1070 return self.funcmeta.get(funcva)
1071
1072 - def getFunctionBlocks(self, funcva):
1073 """ 1074 Return the code-block objects for the given function va 1075 """ 1076 ret = self.codeblocks_by_funcva.get(funcva) 1077 if ret == None: 1078 ret = [] 1079 return ret
1080
1081 - def makeFunctionThunk(self, fva, thname):
1082 """ 1083 Inform the workspace that a given function is considered a "thunk" to another. 1084 This allows the workspace to process argument inheritance and several other things. 1085 1086 Usage: vw.makeFunctionThunk(0xvavavava, "kernel32.CreateProcessA") 1087 """ 1088 self.setFunctionMeta(fva, "Thunk", thname) 1089 n = self.getName(fva) 1090 1091 base = thname.split(".")[-1] 1092 self.makeName(fva, "%s_%.8x" % (base,fva)) 1093 1094 api = self.getImpApi(thname) 1095 if api: 1096 # Set any argument names that are None 1097 rettype,retname,callconv,callname,callargs = api 1098 callargs = [ callargs[i] if callargs[i][1] else (callargs[i][0],'arg%d' % i) for i in xrange(len(callargs)) ] 1099 self.setFunctionApi(fva, (rettype,retname,callconv,callname,callargs))
1100
1101 - def getCallers(self, va):
1102 ''' 1103 Get the va for all the callers of the given function/import. 1104 1105 Example: 1106 for va in vw.getCallers( importva ): 1107 dostuff(va) 1108 ''' 1109 ret = [] 1110 for fromva, tova, rtype, rflags in self.getXrefsTo(va, rtype=REF_CODE): 1111 if rflags & envi.BR_PROC: 1112 ret.append(fromva) 1113 return ret
1114
1115 - def getCallGraph(self):
1116 ''' 1117 Retrieve a visgraph Graph object representing all known inter procedural 1118 branches in the workspace. Each node has an ID that is the same as the 1119 function va. 1120 1121 Example: 1122 graph = vw.getCallGraph() 1123 ''' 1124 return self._call_graph
1125
1126 - def getImportCallers(self, name):
1127 """ 1128 Get a list of all the callers who reference the specified import 1129 by name. (If we detect that the name is actually *in* our workspace, 1130 return those callers too... 1131 """ 1132 ret = [] 1133 1134 # If it's a local function, do that too.. 1135 fva = self.vaByName(name) 1136 if fva != None and self.isFunction(fva): 1137 ret = self.getCallers(fva) 1138 1139 for fva in self.getFunctions(): 1140 if self.getFunctionMeta(fva, 'Thunk') == name: 1141 ret.extend( self.getCallers( fva ) ) 1142 1143 for lva,lsize,ltype,tinfo in self.getLocations(LOC_IMPORT): 1144 if tinfo == name: 1145 ret.extend( self.getCallers( lva ) ) 1146 1147 return ret
1148 1149 ################################################################# 1150 # 1151 # Xref API 1152 # 1153
1154 - def getXrefs(self):
1155 """ 1156 Return the entire list of XREF tuples for this workspace. 1157 """ 1158 return self.xrefs
1159
1160 - def getXrefsFrom(self, va, rtype=None):
1161 """ 1162 Return a list of tuples for the xrefs whose origin is the 1163 specified va. Optionally, only return xrefs whose type 1164 field is rtype if specified. 1165 1166 example: 1167 for fromva, tova, rtype, rflags in vw.getXrefsFrom(0x41414141): 1168 dostuff(tova) 1169 """ 1170 ret = [] 1171 xrefs = self.xrefs_by_from.get(va, None) 1172 if xrefs == None: 1173 return ret 1174 if rtype == None: 1175 return xrefs 1176 return [ xtup for xtup in xrefs if xtup[XR_RTYPE] == rtype ]
1177
1178 - def getXrefsTo(self, va, rtype=None):
1179 """ 1180 Get a list of xrefs which point to the given va. Optionally, 1181 specify an rtype to get only xrefs of that type. 1182 """ 1183 # FIXME make xrefs use MapLookup! 1184 ret = [] 1185 xrefs = self.xrefs_by_to.get(va, None) 1186 if xrefs == None: 1187 return ret 1188 if rtype == None: 1189 return xrefs 1190 return [ xtup for xtup in xrefs if xtup[XR_RTYPE] == rtype ]
1191
1192 - def addMemoryMap(self, va, perms, fname, bytes):
1193 """ 1194 Add a memory map to the workspace. This is the *only* way to 1195 get memory backings into the workspace. 1196 """ 1197 self._fireEvent(VWE_ADDMMAP, (va, perms, fname, bytes))
1198
1199 - def delMemoryMap(self, va):
1200 raise "OMG"
1201
1202 - def addSegment(self, va, size, name, filename):
1203 """ 1204 Add a "segment" to the workspace. A segment is generally some meaningful 1205 area inside of a memory map. For PE binaries, a segment and a memory map 1206 are synonymous. However, some platforms (Elf) specify their memory maps 1207 (program headers) and segments (sectons) seperately. 1208 """ 1209 self._fireEvent(VWE_ADDSEGMENT, (va,size,name,filename))
1210
1211 - def getSegment(self, va):
1212 """ 1213 Return the tuple representation of a segment. With the 1214 following format: 1215 1216 (va, size, name, filename) 1217 """ 1218 for seg in self.segments: 1219 sva, ssize, sname, sfile = seg 1220 if va >= sva and va < (sva + ssize): 1221 return seg 1222 return None
1223
1224 - def getSegments(self):
1225 """ 1226 Return a list of segment tuples (see getSegment) for all 1227 the segments defined in the current worksace 1228 """ 1229 return list(self.segments)
1230
1231 - def addCodeBlock(self, va, size, funcva):
1232 """ 1233 Add a region of code which belongs to a function. Code-block boundaries 1234 are at all logical branches and have more in common with a logical 1235 graph view than function chunks. 1236 """ 1237 loc = self.getLocation( va ) 1238 if loc == None: 1239 raise Exception('Adding Codeblock on *non* location?!?: 0x%.8x' % va) 1240 self._fireEvent(VWE_ADDCODEBLOCK, (va,size,funcva))
1241
1242 - def getCodeBlock(self, va):
1243 """ 1244 Return the codeblock which contains the given va. A "codeblock" 1245 is a location compatable tuple: (va, size, funcva) 1246 """ 1247 return self.blockmap.getMapLookup(va)
1248
1249 - def delCodeBlock(self, va):
1250 """ 1251 Remove a code-block definition from the codeblock namespace. 1252 """ 1253 cb = self.getCodeBlock(va) 1254 if cb == None: 1255 raise Exception("Unknown Code Block: 0x%x" % va) 1256 self._fireEvent(VWE_DELCODEBLOCK, cb)
1257
1258 - def getCodeBlocks(self):
1259 """ 1260 Return a list of all the codeblock objects. 1261 """ 1262 return list(self.codeblocks)
1263
1264 - def addXref(self, fromva, tova, reftype, rflags=0):
1265 """ 1266 Add an xref with the specified fromva, tova, and reftype 1267 (see REF_ macros). This will *not* trigger any analysis. 1268 Callers are expected to do their own xref analysis (ie, makeCode() etc) 1269 """ 1270 ref = (fromva,tova,reftype,rflags) 1271 if ref in self.getXrefsFrom(fromva): 1272 return 1273 self._fireEvent(VWE_ADDXREF, (fromva, tova, reftype, rflags))
1274
1275 - def delXref(self, ref):
1276 """ 1277 Remove the given xref. This *will* exception if the 1278 xref doesn't already exist... 1279 """ 1280 if ref not in self.getXrefsFrom(ref[XR_FROM]): 1281 raise Exception("Unknown Xref: %x %x %d" % ref) 1282 self._fireEvent(VWE_DELXREF, ref)
1283
1284 - def analyzePointer(self, va):
1285 """ 1286 Assume that a new pointer has been created. Check if it's 1287 target has a defined location and if not, try to figgure out 1288 wtf is there... Will return the location type of the location 1289 it recommends or None if a location is already there or it has 1290 no idea. 1291 """ 1292 if self.getLocation(va) != None: 1293 return None 1294 if self.isProbablyString(va): 1295 return LOC_STRING 1296 elif self.isProbablyUnicode(va): 1297 return LOC_UNI 1298 elif self.isProbablyCode(va): 1299 return LOC_OP 1300 return None
1301
1302 - def getMeta(self, name, default=None):
1303 return self.metadata.get(name, default)
1304
1305 - def setMeta(self, name, value):
1306 """ 1307 Set a meta key,value pair for this workspace. 1308 """ 1309 self._fireEvent(VWE_SETMETA, (name,value))
1310
1311 - def initMeta(self, name, value):
1312 """ 1313 Set a metakey ONLY if it is not already set. Either 1314 way return the value of the meta key. 1315 """ 1316 m = self.getMeta(name) 1317 if m == None: 1318 self.setMeta(name, value) 1319 m = value 1320 return m
1321
1322 - def getTransMeta(self, mname, default=None):
1323 ''' 1324 Retrieve a piece of "transient" metadata which is *not* 1325 stored across runs or pushed through the event subsystem. 1326 ''' 1327 return self.transmeta.get(mname,default)
1328
1329 - def setTransMeta(self, mname, value):
1330 ''' 1331 Store a piece of "transient" metadata which is *not* 1332 stored across runs or pushed through the event subsystem. 1333 ''' 1334 self.transmeta[mname] = value
1335
1336 - def castPointer(self, va):
1337 """ 1338 Return the value for a pointer in memory at 1339 the given location. This method does NOT 1340 create a location object or do anything other 1341 than parse memory. 1342 """ 1343 offset, bytes = self.getByteDef(va) 1344 return e_bits.parsebytes(bytes, offset, self.psize)
1345
1346 - def makePointer(self, va, tova=None, follow=True):
1347 """ 1348 Create a new pointer location in the workspace. If you have already 1349 parsed out the pointers value, you may specify tova to speed things 1350 up. 1351 """ 1352 psize = self.psize 1353 1354 # Get and document the xrefs created for the new location 1355 if tova == None: 1356 offset, bytes = self.getByteDef(va) 1357 tova = e_bits.parsebytes(bytes, offset, psize) 1358 1359 self.addXref(va, tova, REF_PTR) 1360 1361 ploc = self.addLocation(va, psize, LOC_POINTER) 1362 1363 if follow and self.isValidPointer(tova): 1364 self.followPointer(tova) 1365 1366 return ploc
1367
1368 - def makePad(self, va, size):
1369 """ 1370 A special utility for making a pad of a particular size. 1371 """ 1372 return self.addLocation(va, size, LOC_PAD, None)
1373
1374 - def makeNumber(self, va, size, val=None):
1375 """ 1376 Create a number location in memory of the given size. 1377 1378 (you may specify val if you have already parsed the value 1379 from memory and would like to save CPU cycles) 1380 """ 1381 return self.addLocation(va, size, LOC_NUMBER, None)
1382
1383 - def parseNumber(self, va, size):
1384 ''' 1385 Parse a <size> width numeric value from memory at <va>. 1386 1387 Example: 1388 val = vw.parseNumber(0x41414140, 4) 1389 ''' 1390 offset, bytes = self.getByteDef(va) 1391 return e_bits.parsebytes(bytes, offset, size)
1392
1393 - def makeString(self, va, size=None):
1394 """ 1395 Create a new string location at the given VA. You may optionally 1396 specify size. If size==None, the string will be parsed as a NULL 1397 terminated ASCII string. 1398 """ 1399 if size == None: 1400 size = self.asciiStringSize(va) 1401 1402 if size <= 0: 1403 raise Exception("Invalid String Size: %d" % size) 1404 1405 if self.getName(va) == None: 1406 m = self.readMemory(va, size-1).replace("\n","") 1407 self.makeName(va, "str_%s_%.8x" % (m[:16],va)) 1408 return self.addLocation(va, size, LOC_STRING)
1409
1410 - def makeUnicode(self, va, size=None):
1411 if size == None: 1412 size = self.uniStringSize(va) 1413 1414 if size <= 0: 1415 raise Exception("Invalid Unicode Size: %d" % size) 1416 1417 if self.getName(va) == None: 1418 self.makeName(va, "wstr_%.8x" % va) 1419 return self.addLocation(va, size, LOC_UNI)
1420
1421 - def addConstModule(self, modname):
1422 ''' 1423 Add constants declared within the named module 1424 to the constants resolver namespace. 1425 1426 Example: vw.addConstModule('vstruct.constants.ntstatus') 1427 ''' 1428 mod = self.loadModule(modname) 1429 self.vsconsts.addModule(mod)
1430
1431 - def addStructureModule(self, namespace, modname):
1432 ''' 1433 Add a vstruct structure module to the workspace with the given 1434 namespace. 1435 1436 Example: vw.addStructureModule('ntdll', 'vstruct.defs.windows.win_5_1_i386.ntdll') 1437 1438 This allows subsequent struct lookups by names like 1439 ''' 1440 1441 mod = self.loadModule(modname) 1442 self.vsbuilder.addVStructNamespace(namespace, mod)
1443
1444 - def getStructure(self, va, vstructname):
1445 """ 1446 Parse and return a vstruct object for the given name. This 1447 (like parseOpcode) does *not* require that the location be a struct 1448 and will not create one (use makeStructure). 1449 """ 1450 s = vstruct.getStructure(vstructname) 1451 if s == None: 1452 s = self.vsbuilder.buildVStruct(vstructname) 1453 if s != None: 1454 bytes = self.readMemory(va, len(s)) 1455 s.vsParse(bytes) 1456 return s
1457
1458 - def makeStructure(self, va, vstructname, vs=None):
1459 """ 1460 Make a location which is a structure and will be parsed/accessed 1461 by vstruct. You must specify the vstruct name for the structure 1462 you wish to have at the location. Returns a vstruct from the 1463 location. 1464 """ 1465 if vs == None: 1466 vs = self.getStructure(va, vstructname) 1467 self.addLocation(va, len(vs), LOC_STRUCT, vstructname) 1468 1469 # Determine if there are any pointers we need make 1470 # xrefs for... 1471 offset = 0 1472 for p in vs.vsGetPrims(): 1473 if isinstance(p, vs_prims.v_ptr): 1474 vptr = p.vsGetValue() 1475 if self.isValidPointer(vptr): 1476 self.addXref(va+offset, vptr, REF_PTR) 1477 1478 offset += len(p) 1479 1480 return vs
1481
1482 - def getUserStructNames(self):
1483 ''' 1484 Retrive the list of the existing user-defined structure 1485 names. 1486 1487 Example: 1488 for name in vw.getUserStructNames(): 1489 print 'Structure Name: %s' % name 1490 ''' 1491 return self.vsbuilder.getVStructCtorNames()
1492
1493 - def getUserStructSource(self, sname):
1494 ''' 1495 Get the source code (as a string) for the given user 1496 defined structure. 1497 1498 Example: 1499 ssrc = vw.getUserStructSource('MyStructureThing') 1500 ''' 1501 return self.getMeta('ustruct:%s' % sname)
1502
1503 - def setUserStructSource(self, ssrc):
1504 ''' 1505 Save the input string as a C structure definition for the 1506 workspace. User-defined structures may then be applied 1507 to locations, or further edited in the future. 1508 1509 Example: 1510 src = "struct woot { int x; int y; };" 1511 vw.saveUserStructureSource( src ) 1512 ''' 1513 # First, we make sure it compiles... 1514 ctor = vs_cparse.ctorFromCSource( ssrc ) 1515 # Then, build one to get the name from it... 1516 vs = ctor() 1517 cname = vs.vsGetTypeName() 1518 self.setMeta('ustruct:%s' % cname, ssrc) 1519 return cname
1520
1521 - def asciiStringSize(self, va):
1522 """ 1523 Return the size (in bytes) of the ascii string 1524 at the specified location (or -1 if no terminator 1525 is found in the memory map) 1526 """ 1527 offset,bytes = self.getByteDef(va) 1528 foff = bytes.find('\x00', offset) 1529 if foff == -1: 1530 return foff 1531 return (foff - offset) + 1
1532
1533 - def uniStringSize(self, va):
1534 """ 1535 Return the size (in bytes) of the unicode string 1536 at the specified location (or -1 if no terminator 1537 is found in the memory map) 1538 """ 1539 offset,bytes = self.getByteDef(va) 1540 foff = bytes.find('\x00\x00', offset) 1541 if foff == -1: 1542 return foff 1543 return (foff - offset) + 2
1544
1545 - def addLocation(self, va, size, ltype, tinfo=None):
1546 """ 1547 Add a location tuple. 1548 """ 1549 1550 #loc = self.locmap.getMapLookup(va) 1551 ltup = (va, size, ltype, tinfo) 1552 #if loc != None: 1553 #raise Exception('Duplicate Location: (is: %r wants: %r)' % (loc,ltup)) 1554 1555 self._fireEvent(VWE_ADDLOCATION, ltup) 1556 return ltup
1557
1558 - def getLocations(self, ltype=None, linfo=None):
1559 """ 1560 Return a list of location objects from the workspace 1561 of a particular type. 1562 """ 1563 if ltype == None: 1564 return list(self.loclist) 1565 1566 if linfo == None: 1567 return [ loc for loc in self.loclist if loc[2] == ltype ] 1568 1569 return [ loc for loc in self.loclist if (loc[2] == ltype and loc[3] == linfo) ]
1570
1571 - def isLocation(self, va, range=False):
1572 """ 1573 Return True if the va represents a location already. 1574 """ 1575 if self.getLocation(va, range=range) != None: 1576 return True 1577 return False
1578
1579 - def isLocType(self, va, ltype):
1580 """ 1581 You may use this to test if a given VA represents 1582 a location of the specified type. 1583 1584 example: 1585 if vw.isLocType(0x41414141, LOC_STRING): 1586 print "string at: 0x41414141" 1587 """ 1588 tup = self.getLocation(va) 1589 if tup == None: 1590 return False 1591 return tup[L_LTYPE] == ltype
1592
1593 - def getLocation(self, va, range=False):
1594 """ 1595 Return the va,size,ltype,tinfo tuple for the given location. 1596 (specify range=True to potentially match a va that is inside 1597 a location rather than the beginning of one) 1598 """ 1599 return self.locmap.getMapLookup(va)
1600
1601 - def getLocationRange(self, va, size):
1602 """ 1603 A "location range" is a list of location tuples where 1604 undefined space *will* be represented by LOC_UNDEF tuples 1605 to provide a complete accounting of linear workspace. 1606 """ 1607 ret = [] 1608 endva = va+size 1609 undefva = None 1610 while va < endva: 1611 ltup = self.getLocation(va) 1612 if ltup == None: 1613 if undefva == None: 1614 undefva = va 1615 va += 1 1616 else: 1617 if undefva != None: 1618 ret.append((undefva, va-undefva, LOC_UNDEF, None)) 1619 undefva = None 1620 ret.append(ltup) 1621 va += ltup[L_SIZE] 1622 1623 # Mop up any hanging udefs 1624 if undefva != None: 1625 ret.append((undefva, va-undefva, LOC_UNDEF, None)) 1626 1627 return ret
1628
1629 - def delLocation(self, va):
1630 """ 1631 Delete the given Location object from the binary 1632 (removes any xrefs/etc for the location as well) 1633 1634 This will raise InvalidLocation if the va is not 1635 an exact match for the beginning of a location. 1636 """ 1637 loc = self.getLocation(va) 1638 if loc == None: 1639 raise InvalidLocation(va) 1640 self._fireEvent(VWE_DELLOCATION, loc)
1641
1642 - def getRenderInfo(self, va, size):
1643 """ 1644 Get nearly everything needed to render a workspace area 1645 to a display. This function *greatly* speeds up interface 1646 code and is considered "tightly coupled" with the asmview 1647 code. (and is therefore subject to change). 1648 """ 1649 locs = [] 1650 funcs = {} 1651 names = {} 1652 comments = {} 1653 extras = {} 1654 1655 for loc in self.getLocationRange(va, size): 1656 lva, lsize, ltype, tinfo = loc 1657 locs.append(loc) 1658 1659 name = self.getName(lva) 1660 isfunc = self.isFunction(lva) 1661 cmnt = self.getComment(lva) 1662 1663 if name != None: 1664 names[lva] = name 1665 if isfunc == True: 1666 funcs[lva] = True 1667 if cmnt != None: 1668 comments[lva] = cmnt 1669 1670 if ltype == LOC_UNDEF: 1671 # Expand out all undefs so we can send all the info 1672 endva = lva + lsize 1673 while lva < endva: 1674 uname = self.getName(lva) 1675 ucmnt = self.getComment(lva) 1676 if uname != None: 1677 names[lva] = uname 1678 if ucmnt != None: 1679 comments[lva] = ucmnt 1680 #ret.append(((lva, 1, LOC_UNDEF, None), self.getName(lva), False, self.getComment(lva))) 1681 lva += 1 1682 1683 elif ltype == LOC_OP: 1684 extras[lva] = self.parseOpcode(lva) 1685 1686 elif ltype == LOC_STRUCT: 1687 extras[lva] = self.getStructure(lva, tinfo) 1688 1689 return locs, funcs, names, comments, extras
1690
1691 - def getPrevLocation(self, va, adjacent=True):
1692 """ 1693 Get the previous location behind this one. If adjacent 1694 is true, only return a location which is IMMEDIATELY behind 1695 the given va, otherwise search backward for a location until 1696 you find one or hit the edge of the segment. 1697 """ 1698 va -= 1 1699 ret = self.locmap.getMapLookup(va) 1700 if ret != None: 1701 return ret 1702 if adjacent: 1703 return None 1704 va -= 1 1705 while va > 0: 1706 ret = self.locmap.getMapLookup(va) 1707 if ret != None: 1708 return ret 1709 va -= 1 1710 return None
1711
1712 - def vaByName(self, name):
1713 return self.va_by_name.get(name, None)
1714
1715 - def getLocationByName(self, name):
1716 """ 1717 Return a location object by the name of the 1718 location. 1719 """ 1720 va = self.vaByName(name) 1721 if va == None: 1722 raise InvalidLocation(0, "Unknown Name: %s" % name) 1723 return self.getLocation(va)
1724
1725 - def getNames(self):
1726 """ 1727 Return a list of tuples containing (va, name) 1728 """ 1729 return self.name_by_va.items()
1730
1731 - def getName(self, va):
1732 ''' 1733 Returns the name of the specified virtual address (or None). 1734 ''' 1735 return self.name_by_va.get(va, None)
1736
1737 - def makeName(self, va, name, filelocal=False):
1738 """ 1739 Set a readable name for the given location by va. There 1740 *must* be a Location defined for the VA before you may name 1741 it. You may set a location's name to None to remove a name. 1742 """ 1743 if filelocal: 1744 segtup = self.getSegment(va) 1745 if segtup == None: 1746 print "Failed to find file for 0x%.8x (%s) (and filelocal == True!)" % (va, name) 1747 if segtup != None: 1748 fname = segtup[SEG_FNAME] 1749 if fname != None: 1750 name = "%s.%s" % (fname, name) 1751 1752 oldva = self.vaByName(name) 1753 # If that's already the name, ignore the event 1754 if oldva == va: 1755 return 1756 1757 if oldva != None: 1758 raise DuplicateName(oldva, va, name) 1759 1760 self._fireEvent(VWE_SETNAME, (va,name))
1761
1762 - def saveWorkspace(self, fullsave=True):
1763 1764 if self.server != None: 1765 return 1766 1767 modname = self.getMeta("StorageModule") 1768 filename = self.getMeta("StorageName") 1769 if modname == None: 1770 raise Exception("StorageModule not specified!") 1771 if filename == None: 1772 raise Exception("StorageName not specified!") 1773 1774 # Usually this is "vivisect.storage.basicfile 1775 mod = self.loadModule(modname) 1776 1777 # If they specified a full save, *or* this event list 1778 # has never been saved before, do a full save. 1779 if fullsave: 1780 mod.saveWorkspace(self, filename) 1781 else: 1782 mod.saveWorkspaceChanges(self, filename) 1783 1784 self._createSaveMark()
1785 1786 1787
1788 - def loadFromFd(self, fd, fmtname=None):
1789 """ 1790 Read the first bytes of the file descriptor and see if we can identify the type. 1791 If so, load up the parser for that file type, otherwise raise an exception. 1792 1793 Returns file md5 1794 """ 1795 mod = None 1796 fd.seek(0) 1797 if fmtname == None: 1798 bytes = fd.read(32) 1799 fmtname = viv_parsers.guessFormat(bytes) 1800 1801 mod = viv_parsers.getParserModule(fmtname) 1802 if hasattr(mod, "config"): 1803 self.mergeConfig(mod.config) 1804 1805 fd.seek(0) 1806 filename = hashlib.md5( fd.read() ).hexdigest() 1807 fname = mod.parseFd(self, fd, filename) 1808 1809 self.initMeta("StorageName", filename+".viv") 1810 1811 # Snapin our analysis modules 1812 self._snapInAnalysisModules() 1813 1814 return fname
1815
1816 - def _saveSymbolCaches(self):
1817 1818 if not self.config.vdb.SymbolCacheActive: 1819 return 1820 1821 pathstr = self.config.vdb.SymbolCachePath 1822 symcache = e_symcache.SymbolCachePath(pathstr) 1823 1824 symsbyfile = collections.defaultdict(list) 1825 1826 # Get the image base addresses 1827 imgbases = {} 1828 for fname in self.getFiles(): 1829 imgbases[ fname ] = self.getFileMeta(fname,'imagebase') 1830 1831 for va,name in self.name_by_va.items(): 1832 map = self.getMemoryMap(va) 1833 if map == None: 1834 continue 1835 1836 symva = va - imgbases.get( map[3], va ) 1837 if symva: 1838 1839 symtype = e_resolv.SYMSTOR_SYM_SYMBOL 1840 if self.isFunction(va): 1841 symtype = e_resolv.SYMSTOR_SYM_FUNCTION 1842 1843 symsbyfile[ map[3] ].append( (symva, 0, name, symtype) ) 1844 1845 for filenorm, symtups in symsbyfile.items(): 1846 symhash = self.getFileMeta(filenorm, 'SymbolCacheHash') 1847 if symhash == None: 1848 continue 1849 1850 self.vprint('Saving Symbol Cache: %s (%d syms)' % (symhash,len(symtups))) 1851 symcache.setCacheSyms( symhash, symtups )
1852
1853 - def loadFromFile(self, filename, fmtname=None):
1854 """ 1855 Read the first bytes of the file and see if we can identify the type. 1856 If so, load up the parser for that file type, otherwise raise an exception. 1857 ( if it's a workspace, trigger loadWorkspace() as a convenience ) 1858 1859 Returns the basename the file was given on load. 1860 """ 1861 mod = None 1862 if fmtname == None: 1863 fmtname = viv_parsers.guessFormatFilename(filename) 1864 1865 mod = viv_parsers.getParserModule(fmtname) 1866 fname = mod.parseFile(self, filename) 1867 1868 self.initMeta("StorageName", filename+".viv") 1869 1870 # Snapin our analysis modules 1871 self._snapInAnalysisModules() 1872 1873 return fname
1874
1875 - def loadFromMemory(self, memobj, baseaddr, fmtname=None):
1876 """ 1877 Load a memory map (or potentially a mapped binary file) 1878 from the memory object's map at baseaddr. 1879 """ 1880 mod = None 1881 if fmtname == None: 1882 bytes = memobj.readMemory(baseaddr, 32) 1883 fmtname = viv_parsers.guessFormat(bytes) 1884 1885 mod = viv_parsers.getParserModule(fmtname) 1886 mod.parseMemory(self, memobj, baseaddr) 1887 1888 mapva, mapsize, mapperm, mapfname = memobj.getMemoryMap(baseaddr) 1889 if not mapfname: 1890 mapfname = 'mem_map_%.8x' % mapva 1891 1892 self.initMeta('StorageName', mapfname+".viv") 1893 # Snapin our analysis modules 1894 self._snapInAnalysisModules()
1895
1896 - def getFiles(self):
1897 """ 1898 Return the current list of file objects in this 1899 workspace. 1900 """ 1901 return self.filemeta.keys()
1902
1903 - def normFileName(self, filename):
1904 normname = os.path.basename(filename).lower() 1905 1906 # Strip off an extension 1907 if normname.find('.') != -1: 1908 parts = normname.split('.') 1909 normname = '_'.join(parts[:-1]) 1910 1911 ok = string.letters + string.digits + '_' 1912 1913 chars = list(normname) 1914 for i in xrange(len(chars)): 1915 if chars[i] not in ok: 1916 chars[i] = '_' 1917 1918 normname = ''.join(chars) 1919 #if normname[0].isdigit(): 1920 #normname = '_' + normname 1921 1922 return normname
1923
1924 - def addFile(self, filename, imagebase, md5sum):
1925 """ 1926 Create and add a new vivisect File object for the 1927 specified information. This will return the file 1928 object which you may then use to do things like 1929 add imports/exports/segments etc... 1930 """ 1931 nname = self.normFileName(filename) 1932 if self.filemeta.has_key(nname): 1933 raise Exception("Duplicate File Name: %s" % nname) 1934 self._fireEvent(VWE_ADDFILE, (nname, imagebase, md5sum)) 1935 return nname
1936
1937 - def addEntryPoint(self, va):
1938 ''' 1939 Add an entry point to the definition for the given file. This 1940 will hint the analysis system to create functions when analysis 1941 is run. 1942 1943 NOTE: No analysis is triggered by this function. 1944 ''' 1945 self.setVaSetRow('EntryPoints', (va,))
1946
1947 - def getEntryPoints(self):
1948 ''' 1949 Get all the parsed entry points for all the files loaded into the 1950 workspace. 1951 1952 Example: for va in vw.getEntryPoints(): 1953 ''' 1954 return [ x for x, in self.getVaSetRows('EntryPoints') ]
1955
1956 - def setFileMeta(self, fname, key, value):
1957 """ 1958 Store a piece of file specific metadata (python primatives are best for values) 1959 """ 1960 if not self.filemeta.has_key(fname): 1961 raise Exception("Invalid File: %s" % fname) 1962 self._fireEvent(VWE_SETFILEMETA, (fname, key, value))
1963
1964 - def getFileMeta(self, filename, key, default=None):
1965 """ 1966 Retrieve a piece of file specific metadata 1967 """ 1968 d = self.filemeta.get(filename) 1969 if d == None: 1970 raise Exception("Invalid File: %s" % filename) 1971 return d.get(key, default)
1972
1973 - def getFileMetaDict(self, filename):
1974 ''' 1975 Retrieve the file metadata for this file as a key:val dict. 1976 ''' 1977 d = self.filemeta.get(filename) 1978 if d == None: 1979 raise Exception('Invalid File: %s' % filename) 1980 return d
1981
1982 - def getFileByVa(self, va):
1983 segtup = self.getSegment(va) 1984 if segtup == None: 1985 return None 1986 return segtup[SEG_FNAME]
1987
1988 - def getLocationDistribution(self):
1989 # NOTE: if this changes, don't forget the report module! 1990 totsize = float(0) 1991 for mapva, mapsize, mperm, mname in self.getMemoryMaps(): 1992 totsize += mapsize 1993 loctot = 0 1994 ret = {} 1995 for i in xrange(LOC_MAX): 1996 cnt = 0 1997 size = 0 1998 for lva,lsize,ltype,tinfo in self.getLocations(i): 1999 cnt += 1 2000 size += lsize 2001 loctot += size 2002 2003 tname = loc_type_names.get(i, 'Unknown') 2004 ret[i] = (tname, cnt, size, int((size/totsize)*100)) 2005 2006 # Update the undefined based on totals... 2007 undeftot = totsize-loctot 2008 ret[LOC_UNDEF] = ('Undefined', 0, undeftot, int((undeftot/totsize)*100)) 2009 2010 return ret
2011 2012 ################################################################# 2013 # 2014 # VA Set API 2015 # 2016
2017 - def getVaSetNames(self):
2018 """ 2019 Get a list of the names of the current VA lists. 2020 """ 2021 return self.vasets.keys()
2022
2023 - def getVaSetDef(self, name):
2024 """ 2025 Get the list of (name, type) pairs which make up the 2026 rows for this given VA set (the first one *always* the VA, but 2027 you can name it as you like...) 2028 """ 2029 x = self.vasetdefs.get(name) 2030 if x == None: raise InvalidVaSet(name) 2031 return x
2032
2033 - def getVaSetRows(self, name):
2034 """ 2035 Get a list of the rows in this VA set. 2036 """ 2037 x = self.vasets.get(name) 2038 if x == None: InvalidVaSet(name) 2039 return x.values()
2040
2041 - def getVaSet(self, name):
2042 """ 2043 Get the dictionary of va:<rowdata> entries. 2044 """ 2045 x = self.vasets.get(name) 2046 if x == None: raise InvalidVaSet(name) 2047 return x
2048
2049 - def addVaSet(self, name, defs, rows=()):
2050 """ 2051 Add a va set: 2052 2053 name - The name for this VA set 2054 defs - List of (<name>,<type>) tuples for the rows (va is always first) 2055 rows - An initial set of rows for values in this set. 2056 """ 2057 self._fireEvent(VWE_ADDVASET, (name, defs, rows))
2058
2059 - def delVaSet(self, name):
2060 """ 2061 Delete a VA set by name. 2062 """ 2063 if not self.vasets.has_key(name): 2064 raise Exception("Unknown VA Set: %s" % name) 2065 self._fireEvent(VWE_DELVASET, name)
2066
2067 - def setVaSetRow(self, name, rowtup):
2068 """ 2069 Use this API to update the row data for a particular 2070 entry in the VA set. Create a new empty set if one 2071 does not already exist. 2072 """ 2073 self._fireEvent(VWE_SETVASETROW, (name, rowtup))
2074
2075 - def getVaSetRow(self, name, va):
2076 ''' 2077 Retrieve the va set row for va in the va set named name. 2078 2079 Example: 2080 row = vw.getVaSetRow('WootFunctions', fva) 2081 ''' 2082 vaset = self.vasets.get( name ) 2083 if vaset == None: 2084 return None 2085 return vaset.get( va )
2086
2087 - def delVaSetRow(self, name, va):
2088 """ 2089 Use this API to delete the rowdata associated 2090 with the specified VA from the set. 2091 """ 2092 if not self.vasets.has_key(name): 2093 raise Exception("Unknown VA Set: %s" % name) 2094 self._fireEvent(VWE_DELVASETROW, (name, va))
2095 2096 ################################################################# 2097 # 2098 # Shared Workspace APIs 2099 #
2100 - def chat(self, msg):
2101 uname = e_config.getusername() 2102 # FIXME this should be part of a UI event model. 2103 self._fireEvent(VWE_CHAT, (uname, msg))
2104
2105 - def iAmLeader(self, winname):
2106 ''' 2107 Announce that your workspace is leading a window with the 2108 specified name. This allows others to opt-in to following 2109 the nav events for the given window name. 2110 2111 Example: 2112 vw.iAmLeader('WindowTitle') 2113 ''' 2114 if not self.server: 2115 raise Exception('iAmLeader() requires being connected to a server.') 2116 2117 user = e_config.getusername() 2118 self.server._fireEvent(VTE_MASK | VTE_IAMLEADER, (user,winname))
2119
2120 - def followTheLeader(self, winname, expr):
2121 ''' 2122 Announce a new memory expression to navigate to if if a given window 2123 is following the specified user/winname 2124 2125 Example: 2126 vw.followTheLeader('FunExample', 'sub_08042323') 2127 ''' 2128 if not self.server: 2129 raise Exception('followTheLeader() requires being connected to a server.') 2130 user = e_config.getusername() 2131 self.server._fireEvent(VTE_MASK | VTE_FOLLOWME, (user,winname, expr))
2132 2133 ################################################################# 2134 # 2135 # Color Map API 2136 # 2137
2138 - def getColorMaps(self):
2139 """ 2140 Return a list of the names of the given color maps 2141 """ 2142 return self.colormaps.keys()
2143
2144 - def addColorMap(self, mapname, colormap):
2145 """ 2146 Add a colormap dictionary with the given name for the map. 2147 (A colormap dictionary is va:color entries) 2148 """ 2149 self._fireEvent(VWE_ADDCOLOR, (mapname, colormap))
2150
2151 - def delColorMap(self, mapname):
2152 self._fireEvent(VWE_DELCOLOR, mapname)
2153
2154 - def getColorMap(self, mapname):
2155 """ 2156 Return the colormap dictionary for the given map name. 2157 """ 2158 return self.colormaps.get(mapname)
2159 2160 ########################################################## 2161 # 2162 # The envi.symstore.resolver.SymbolResolver API... 2163 #
2164 - def getSymByName(self, name):
2165 2166 # Check for a sym 2167 va = self.vaByName(name) 2168 if va != None: 2169 return e_resolv.Symbol(name, va, 0) 2170 2171 # check for the need for a deref. 2172 d = self.filemeta.get(name) 2173 if d != None: 2174 return VivFileSymbol(self, name, d.get("imagebase"), 0, self.psize)
2175
2176 - def getSymByAddr(self, addr, exact=True):
2177 name = self.getName(addr) 2178 if name == None: 2179 if self.isValidPointer(addr): 2180 name = "loc_%.8x" % addr 2181 2182 if name != None: 2183 #FIXME fname 2184 #FIXME functions/segments/etc... 2185 return e_resolv.Symbol(name, addr, 0)
2186
2187 - def setSymHint(self, va, idx, hint):
2188 ''' 2189 Set a symbol hint which will be used in place of operand 2190 values during disassembly among other things... 2191 2192 You may also set hint=None to delete sym hints. 2193 ''' 2194 self._fireEvent(VWE_SYMHINT, (va,idx,hint))
2195
2196 - def getSymHint(self, va, idx):
2197 h = self.getFref(va, idx) 2198 if h != None: 2199 f = self.getFunction(va) 2200 loctup = self.getFunctionLocal(f, h) 2201 if loctup: 2202 return loctup[1] 2203 2204 return self.symhints.get((va,idx), None)
2205
2206 -class VivFileSymbol(e_resolv.FileSymbol):
2207 # A namespace tracker thingie...
2208 - def __init__(self, vw, fname, base, size, width=4):
2209 self.vw = vw 2210 e_resolv.FileSymbol.__init__(self, fname, base, size, width)
2211
2212 - def getSymByName(self, name):
2213 return self.vw.getSymByName("%s.%s" % (self.name, name))
2214
2215 -def getVivPath(*pathents):
2216 dname = os.path.dirname(__file__) 2217 dname = os.path.abspath(dname) 2218 return os.path.join(dname, *pathents)
2219