Package vivisect :: Module vector
[hide private]
[frames] | no frames]

Source Code for Module vivisect.vector

  1   
  2  """ 
  3  A module full of utils for vectored input tracking and code 
  4  flow analysis. (when a scalpel finds something you need to be 
  5  able to figgure out how to get to it right?) 
  6  """ 
  7   
  8  import vivisect.exc as viv_exc 
  9  import vivisect.impemu.monitor as viv_imp_monitor 
 10   
 11  import visgraph.pathcore as vg_path 
 12   
 13  from vivisect.const import * 
 14   
 15  import envi 
 16   
17 -class InputMonitor(viv_imp_monitor.EmulationMonitor):
18
19 - def __init__(self):
20 viv_imp_monitor.EmulationMonitor.__init__(self) 21 self.res = []
22
23 - def apicall(self, emu, op, pc, api, argv):
24 self.res.append( (op.va, argv) )
25
26 -def getEmuAtVa(vw, va):
27 """ 28 Build and run an emulator to the given virtual address 29 from the function entry point. 30 31 (most useful for state analysis. kinda heavy though...) 32 """ 33 fva = vw.getFunction(va) 34 if fva == None: 35 return None 36 37 emu = vw.getEmulator() 38 emu.runFunction(fva, stopva=va) 39 return emu
40
41 -def trackImportInputs(vw, iname):
42 """ 43 Works just like trackFunctionInputs but finds calls to 44 imports by name instead... 45 """ 46 mon = InputMonitor() 47 for va in vw.getImportCallers(iname): 48 emu = getEmuAtVa(vw, va) 49 if emu == None: 50 continue 51 # Set an emulation monitor and step over the call 52 emu.setEmulationMonitor(mon) 53 emu.stepi() 54 55 return mon.res
56
57 -def trackFunctionInputs(vw, fva):
58 """ 59 Find all the callers to the given function and return a list 60 of (callva, [ (argval, magic), ...]) tuples. 61 """ 62 mon = InputMonitor() 63 for va in vw.getCallers(fva): 64 65 if not vw.getFunction(va): 66 vw.vprint('code at 0x%.8x is not part of a function!' % va) 67 continue 68 69 emu = getEmuAtVa(vw, va) 70 # Set an emulation monitor and step over the call 71 emu.setEmulationMonitor(mon) 72 emu.stepi() 73 74 return mon.res
75
76 -def trackArgOrigin(vw, fva, argidx):
77 """ 78 Return an input tree (visgraph path tree) of the trackable inputs 79 to the specified function. 80 81 Each node in the list will be a leaf node for a path leading 82 down toward a call to the target function. Each node will have 83 the following path node properties: 84 85 fva - The function 86 argidx - The index of the argument input with this call 87 cva - The address of the call (to our next) (None on root node) 88 argv - A list of (<val>,<magic>) tuples for the call args (None on root node) 89 """ 90 91 rootpath = vg_path.newPathNode(fva=fva, cva=None, trackidx=argidx, argidx=None, argv=None) 92 93 todo = [rootpath, ] 94 95 while len(todo): 96 97 path = todo.pop() 98 99 fva = vg_path.getNodeProp(path, 'fva') 100 trackidx = vg_path.getNodeProp(path, 'trackidx') 101 102 # Get all of our callers and their arguments to us 103 for callva, argv in trackFunctionInputs(vw, fva): 104 105 newfva = vw.getFunction(callva) 106 pargs = dict(parent=path, fva=newfva, cva=callva, argidx=trackidx, argv=argv) 107 newpath = vg_path.newPathNode(**pargs) 108 109 aval, amagic = argv[trackidx] 110 if isinstance(amagic, viv_magic.StackArg) and newfva: 111 vg_path.setNodeProp(newpath, 'trackidx', amagic.index) 112 todo.append(newpath) 113 114 return vg_path.getLeafNodes(rootpath)
115
116 -def getCodeFlow(vw, cbva):
117 """ 118 Get a list of the code blocks which are known to flow 119 into this one. This *will* cross function boundaries. 120 """ 121 ret = [] 122 # Get our actual xrefs 123 for fromva, tova, xtype, xdata in vw.getXrefsTo(cbva, REF_CODE): 124 xcb = vw.getCodeBlock(fromva) 125 if xcb != None: 126 ret.append(xcb) 127 128 # Lets see if the instruction before this was a fallthrough 129 ploc = vw.getPrevLocation(cbva) 130 if ploc != None: 131 pva, psize, ptype, pinfo = ploc 132 # If it's an opcode with fallthrough, count this one too... 133 if ptype == LOC_OP and not pinfo & envi.IF_NOFALL: 134 pblock = vw.getCodeBlock(pva) 135 if pblock != None: 136 ret.append(pblock) 137 138 return ret
139
140 -def getCodePaths(vw, fromva, tova, trim=True):
141 """ 142 Return a list of paths, where each path is a list 143 of code blocks from fromva to tova. 144 145 Usage: getCodePaths(vw, <fromva>, <tova>) -> [ [frblock, ..., toblock], ...] 146 147 NOTE: "trim" causes an optimization which may not reveal *all* the paths, 148 but is much faster to run. It will never return no paths when there 149 are some, but may not return all of them... (based on path overlap) 150 """ 151 152 done = {} 153 res = [] 154 155 frcb = vw.getCodeBlock(fromva) 156 tocb = vw.getCodeBlock(tova) 157 if frcb == None: 158 raise viv_exc.InvalidLocation(fromva) 159 if tocb == None: 160 raise viv_exc.InvalidLocation(tova) 161 162 frva = frcb[0] # For compare speed 163 164 root = vg_path.newPathNode(cb=tocb, cbva=tocb[0]) 165 todo = [root, ] 166 done[tova] = tocb 167 168 cbcache = {} 169 170 while len(todo): 171 172 path = todo.pop() 173 cbva = vg_path.getNodeProp(path, 'cbva') 174 175 codeblocks = cbcache.get(cbva) 176 if codeblocks == None: 177 codeblocks = getCodeFlow(vw, cbva) 178 cbcache[cbva] = codeblocks 179 180 for cblock in codeblocks: 181 182 bva,bsize,bfva = cblock 183 184 # Don't follow loops... 185 if vg_path.isPathLoop(path, 'cbva', bva): 186 continue 187 188 # If we have been here before and it's *not* the answer, 189 # skip out... 190 if trim and done.get(bva) != None: continue 191 192 done[bva] = cblock 193 194 newpath = vg_path.newPathNode(parent=path, cb=cblock, cbva=bva) 195 196 # If this one is a match, we don't need to 197 # track past it. Also, put it in the results list 198 # so we don't have to do it later.... 199 if bva == frva: 200 res.append(newpath) 201 else: 202 todo.append(newpath) 203 204 205 # Now... if we have some results, lets build the block list. 206 ret = [] 207 for cpath in res: 208 fullpath = vg_path.getPathToNode(cpath) 209 # We actually do it by inbound references, so reverse the result! 210 fullpath.reverse() 211 ret.append([vg_path.getNodeProp(path, 'cb') for path in fullpath]) 212 213 return ret
214