def trackArgOrigin(vw, fva, argidx): """ Return an input tree (visgraph path tree) of the trackable inputs to the specified function. Each node in the list will be a leaf node for a path leading down toward a call to the target function. Each node will have the following path node properties: fva - The function argidx - The index of the argument input with this call cva - The address of the call (to our next) (None on root node) argv - A list of (<val>,<magic>) tuples for the call args (None on root node) """ rootpath = vg_path.newPathNode(fva=fva, cva=None, trackidx=argidx, argidx=None, argv=None) todo = [ rootpath, ] while len(todo): path = todo.pop() fva = vg_path.getNodeProp(path, 'fva') trackidx = vg_path.getNodeProp(path, 'trackidx') # Get all of our callers and their arguments to us for callva, argv in trackFunctionInputs(vw, fva): newfva = vw.getFunction(callva) pargs = dict(parent=path, fva=newfva, cva=callva, argidx=trackidx, argv=argv) newpath = vg_path.newPathNode(**pargs) aval, amagic = argv[trackidx] if isinstance(amagic, viv_magic.StackArg) and newfva: vg_path.setNodeProp(newpath, 'trackidx', amagic.index) todo.append(newpath) return vg_path.getLeafNodes(rootpath)
def getCodePathsTo(fgraph, tocbva, loopcnt=0, maxpath=None): ''' Yields all the paths through the hierarchical graph starting at the "root nodes" and ending at tocbva. Specify a loopcnt to allow loop paths to be generated with the given "loop iteration count" Example: for path in getCodePathsTo(fgraph, tocbva): for node,edge in path: ...etc... ''' pathcnt = 0 looptrack = [] pnode = vg_pathcore.newPathNode(nid=tocbva, eid=None) #rootnodes = fgraph.getHierRootNodes() cbnode = fgraph.getNode(tocbva) todo = [ (cbnode, pnode), ] while todo: node, cpath = todo.pop() refsto = fgraph.getRefsTo(node) # Is this is the root node? if node[1].get('rootnode'): path = vg_pathcore.getPathToNode(cpath) path.reverse() yield [_nodeedge(n) for n in path] vg_pathcore.trimPath(cpath) pathcnt += 1 if maxpath and pathcnt >= maxpath: return for eid, n1, n2, einfo in refsto: # Skip loops if they are "deeper" than we are allowed loops = vg_pathcore.getPathLoopCount(cpath, 'nid', n1) if loops > loopcnt: continue vg_pathcore.setNodeProp(cpath, 'eid', eid) npath = vg_pathcore.newPathNode(parent=cpath, nid=n1, eid=None) node1 = fgraph.getNode(n1) todo.append((node1, npath))
def getCodePathsTo(fgraph, tocbva, loopcnt=0, maxpath=None): ''' Yields all the paths through the hierarchical graph starting at the "root nodes" and ending at tocbva. Specify a loopcnt to allow loop paths to be generated with the given "loop iteration count" Example: for path in getCodePathsTo(fgraph, tocbva): for node,edge in path: ...etc... ''' pathcnt = 0 looptrack = [] pnode = vg_pathcore.newPathNode(nid=tocbva, eid=None) #rootnodes = fgraph.getHierRootNodes() cbnode = fgraph.getNode(tocbva) todo = [(cbnode,pnode), ] while todo: node,cpath = todo.pop() refsto = fgraph.getRefsTo(node) # Is this is the root node? if node[1].get('rootnode'): path = vg_pathcore.getPathToNode(cpath) path.reverse() yield [ _nodeedge(n) for n in path ] vg_pathcore.trimPath(cpath) pathcnt += 1 if maxpath and pathcnt >= maxpath: return for eid, n1, n2, einfo in refsto: # Skip loops if they are "deeper" than we are allowed loops = vg_pathcore.getPathLoopCount(cpath, 'nid', n1) if loops > loopcnt: continue vg_pathcore.setNodeProp(cpath, 'eid', eid) npath = vg_pathcore.newPathNode(parent=cpath, nid=n1, eid=None) node1 = fgraph.getNode(n1) todo.append((node1,npath))
def trackArgOrigin(vw, fva, argidx): """ Return an input tree (visgraph path tree) of the trackable inputs to the specified function. Each node in the list will be a leaf node for a path leading down toward a call to the target function. Each node will have the following path node properties: fva - The function argidx - The index of the argument input with this call cva - The address of the call (to our next) (None on root node) argv - A list of (<val>,<magic>) tuples for the call args (None on root node) """ rootpath = vg_path.newPathNode(fva=fva, cva=None, trackidx=argidx, argidx=None, argv=None) todo = [rootpath, ] while len(todo): path = todo.pop() fva = vg_path.getNodeProp(path, 'fva') trackidx = vg_path.getNodeProp(path, 'trackidx') # Get all of our callers and their arguments to us for callva, argv in trackFunctionInputs(vw, fva): newfva = vw.getFunction(callva) pargs = dict(parent=path, fva=newfva, cva=callva, argidx=trackidx, argv=argv) newpath = vg_path.newPathNode(**pargs) aval, amagic = argv[trackidx] if isinstance(amagic, viv_magic.StackArg) and newfva: vg_path.setNodeProp(newpath, 'trackidx', amagic.index) todo.append(newpath) return vg_path.getLeafNodes(rootpath)
def codewalker(ppath, edge, path): # first, test for the "entry" case if ppath is None and edge is None: emu = self.getFuncEmu(fva) for fname, funccb in self.funccb.items(): emu.addFunctionCallback(fname, funccb) patheffs = emu.applyEffects(self.preeffects) pathcons = emu.applyEffects(self.preconstraints) node = graph.getNode(fva) effects = node[1].get('symbolik_effects',()) patheffs.extend(emu.applyEffects(effects)) vg_pathcore.setNodeProp(path, 'pathemu', emu) vg_pathcore.setNodeProp(path, 'pathcons', pathcons) vg_pathcore.setNodeProp(path, 'patheffs', patheffs) return True # we are now in the "walking" case emu = self.getFuncEmu(fva) pemu = vg_pathcore.getNodeProp(ppath, 'pathemu') emu.setSymSnapshot( pemu.getSymSnapshot() ) patheffs = list( vg_pathcore.getNodeProp(ppath, 'patheffs') ) pathcons = list( vg_pathcore.getNodeProp(ppath, 'pathcons') ) vg_pathcore.setNodeProp(path,'pathemu',emu) vg_pathcore.setNodeProp(path,'patheffs',patheffs) vg_pathcore.setNodeProp(path,'pathcons',pathcons) # pick up the edge constraints newcons = graph.getEdgeProps(edge[0]).get('symbolik_constraints', ()) newcons = emu.applyEffects(newcons) [ c.reduce(emu=emu) for c in newcons ] for coneff in newcons: # bail if the constraint is dorked if coneff.cons.isDiscrete(): if not coneff.cons._solve(): print('TRIM: %s' % (str(coneff.cons),)) return False continue # bail if the constraint is mutex # FIXME #if any([ oldconeff.isMutualExclusion( coneff ) for oldconeff in pathcons ]): #return False # TODO: collective failed constraints # (previous constraint "var > 3" and this constraint "var == 2") patheffs.append( coneff ) pathcons.append( coneff ) # We have survived constraints! node2 = graph.getNode(edge[2]) neweffs = node2[1].get('symbolik_effects',()) neweffs = emu.applyEffects(neweffs) patheffs.extend(neweffs) return True
def getFuncCbRoutedPaths_genback(self, fromva, tova, loopcnt=0, maxpath=None, maxsec=None): ''' Yields all the paths through the hierarchical graph starting at the "root nodes" and ending at tocbva. Specify a loopcnt to allow loop paths to be generated with the given "loop iteration count" Example: for path in getCodePathsTo(fgraph, tocbva): for node,edge in path: ...etc... ''' fgraph = self.graph self.__update = 0 self.__go__ = True pathcnt = 0 tocbva = getGraphNodeByVa(fgraph, tova) frcbva = getGraphNodeByVa(fgraph, fromva) preRouteGraph(fgraph, fromva, tova) pnode = vg_pathcore.newPathNode(nid=tocbva, eid=None) todo = [(tocbva,pnode), ] if maxsec: self.watchdog(maxsec) while todo: if not self.__go__: raise PathForceQuitException() nodeid,cpath = todo.pop() refsto = fgraph.getRefsTo((nodeid, None)) # This is the root node! if nodeid == frcbva: path = vg_pathcore.getPathToNode(cpath) path.reverse() self.__steplock.acquire() yield [ viv_graph._nodeedge(n) for n in path ] vg_pathcore.trimPath(cpath) pathcnt += 1 self.__update = 1 self.__steplock.release() if maxpath and pathcnt >= maxpath: return for eid, fromid, toid, einfo in refsto: if fgraph.getNodeProps(fromid).get('up') != True: # TODO: drop the bad edges from graph in preprocessing? instead of "if" here vg_pathcore.trimPath(cpath) continue # Skip loops if they are "deeper" than we are allowed loops = vg_pathcore.getPathLoopCount(cpath, 'nid', fromid) if loops > loopcnt: continue vg_pathcore.setNodeProp(cpath, 'eid', eid) npath = vg_pathcore.newPathNode(parent=cpath, nid=fromid, eid=None) todo.append((fromid,npath))
def codewalker(ppath, edge, path): # first, test for the "entry" case if ppath == None and edge == None: emu = self.getFuncEmu(fva) for fname, funccb in self.funccb.items(): emu.addFunctionCallback(fname, funccb) patheffs = emu.applyEffects(self.preeffects) pathcons = emu.applyEffects(self.preconstraints) node = graph.getNode(fva) effects = node[1].get('symbolik_effects',()) patheffs.extend(emu.applyEffects(effects)) vg_pathcore.setNodeProp(path, 'pathemu', emu) vg_pathcore.setNodeProp(path, 'pathcons', pathcons ) vg_pathcore.setNodeProp(path, 'patheffs', patheffs ) return True # we are now in the "walking" case emu = self.getFuncEmu(fva) pemu = vg_pathcore.getNodeProp(ppath, 'pathemu') emu.setSymSnapshot( pemu.getSymSnapshot() ) patheffs = list( vg_pathcore.getNodeProp(ppath, 'patheffs') ) pathcons = list( vg_pathcore.getNodeProp(ppath, 'pathcons') ) vg_pathcore.setNodeProp(path,'pathemu',emu) vg_pathcore.setNodeProp(path,'patheffs',patheffs) vg_pathcore.setNodeProp(path,'pathcons',pathcons) # pick up the edge constraints newcons = graph.getEdgeProps(edge[0]).get('symbolik_constraints', ()) newcons = emu.applyEffects(newcons) [ c.reduce(emu=emu) for c in newcons ] for coneff in newcons: # bail if the constraint is dorked if coneff.cons.isDiscrete(): if not coneff.cons.prove(): print('TRIM: %s' % (str(coneff.cons),)) return False continue # bail if the constraint is mutex # FIXME #if any([ oldconeff.isMutualExclusion( coneff ) for oldconeff in pathcons ]): #return False # TODO: collective failed constraints # (previous constraint "var > 3" and this constraint "var == 2") patheffs.append( coneff ) pathcons.append( coneff ) # We have survived constraints! node2 = graph.getNode(edge[2]) neweffs = node2[1].get('symbolik_effects',()) neweffs = emu.applyEffects(neweffs) patheffs.extend(neweffs) return True
def runFunction(self, funcva, stopva=None, maxhit=None, maxloop=None, tmode=None): """ This is a utility function specific to WorkspaceEmulation (and impemu) that will emulate, but only inside the given function. You may specify a stopva to return once that location is hit. """ logger.debug( '=== emu.runFunction(0x%x, stopva=%r, maxhit=%r, maxloop=%r, tmode=%r)', funcva, stopva, maxhit, maxloop, tmode) funcva = self._prep(funcva, tmode) # Let the current (should be base also) path know where we are starting vg_path.setNodeProp(self.curpath, 'bva', funcva) hits = {} todo = [(funcva, self.getEmuSnap(), self.path)] vw = self.vw # Save a dereference many many times while len(todo): va, esnap, self.curpath = todo.pop() self.setEmuSnap(esnap) self.setProgramCounter(va) tmode = self.getFlag(PSR_T_bit) # Check if we are beyond our loop max... if maxloop is not None: lcount = vg_path.getPathLoopCount(self.curpath, 'bva', va) if lcount > maxloop: continue while True: starteip = self.getProgramCounter() if not vw.isValidPointer(starteip): break if starteip == stopva: return # Check straight hit count... if maxhit is not None: h = hits.get(starteip, 0) h += 1 if h > maxhit: break hits[starteip] = h # If we ran out of path (branches that went # somewhere that we couldn't follow)? if self.curpath is None: break try: # FIXME unify with stepi code... op = self.parseOpcode(starteip | tmode) self.op = op if self.emumon: try: self.emumon.prehook(self, op, starteip) except v_exc.BadOpBytes as e: logger.debug(str(e)) break except v_exc.BadOutInstruction: pass except Exception as e: logger.log( self._log_level, "funcva: 0x%x opva: 0x%x: %r (%r) (in emumon prehook: %r)", funcva, starteip, op, e, self.emumon) if self.emustop: return # Execute the opcode self.executeOpcode(op) vg_path.getNodeProp(self.curpath, 'valist').append(starteip) endeip = self.getProgramCounter() if self.emumon: try: self.emumon.posthook(self, op, endeip) except v_exc.BadOpBytes as e: logger.debug(str(e)) break except v_exc.BadOutInstruction: pass except Exception as e: logger.log( self._log_level, "funcva: 0x%x opva: 0x%x: %r (%r) (in emumon posthook: %r)", funcva, starteip, op, e, self.emumon) if self.emustop: return iscall = self.checkCall(starteip, endeip, op) if self.emustop: return # If it wasn't a call, check for branches, if so, add them to # the todo list and go around again... if not iscall: blist = self.checkBranches(starteip, endeip, op) if len(blist): # pc in the snap will be wrong, but over-ridden at restore esnap = self.getEmuSnap() for bva, bpath in blist: todo.append((bva, esnap, bpath)) break else: # check if we've blx'd to a different thumb state. if so, # be sure to return to the original tmode before continuing emulation pass newtmode = self.getFlag(PSR_T_bit) if newtmode != tmode: self.setFlag(PSR_T_bit, tmode) # If we enounter a procedure exit, it doesn't # matter what EIP is, we're done here. if op.iflags & envi.IF_RET: vg_path.setNodeProp(self.curpath, 'cleanret', True) break # TODO: hook things like error(...) when they have a param that indicates to # exit. Might be a bit hairy since we'll possibly have to fix up codeblocks # Make sure we can at least get past the first instruction in certain functions if self.vw.isNoReturnVa(op.va) and op.va != funcva: vg_path.setNodeProp(self.curpath, 'cleanret', False) break except envi.BadOpcode: break except envi.UnsupportedInstruction as e: if self.strictops: logger.debug( 'runFunction breaking after unsupported instruction: 0x%08x %s', e.op.va, e.op.mnem) raise e else: logger.debug( 'runFunction continuing after unsupported instruction: 0x%08x %s', e.op.va, e.op.mnem) self.setProgramCounter(e.op.va + e.op.size) except v_exc.BadOutInstruction: break except Exception as e: if self.emumon is not None and not isinstance( e, e_exc.BreakpointHit): self.emumon.logAnomaly(self, starteip, str(e)) logger.debug( 'runFunction breaking after exception (fva: 0x%x): %s', funcva, e) break # If we exc during execution, this branch is dead.
def runFunction(self, funcva, stopva=None, maxhit=None, maxloop=None): """ This is a utility function specific to WorkspaceEmulation (and impemu) that will emulate, but only inside the given function. You may specify a stopva to return once that location is hit. """ self.funcva = funcva # Let the current (should be base also) path know where we are starting vg_path.setNodeProp(self.curpath, 'bva', funcva) hits = {} todo = [(funcva, self.getEmuSnap(), self.path)] vw = self.vw # Save a dereference many many times while len(todo): va, esnap, self.curpath = todo.pop() self.setEmuSnap(esnap) self.setProgramCounter(va) # Check if we are beyond our loop max... if maxloop is not None: lcount = vg_path.getPathLoopCount(self.curpath, 'bva', va) if lcount > maxloop: continue while True: starteip = self.getProgramCounter() if not vw.isValidPointer(starteip): break if starteip == stopva: return # Check straight hit count... if maxhit is not None: h = hits.get(starteip, 0) h += 1 if h > maxhit: break hits[starteip] = h # If we ran out of path (branches that went # somewhere that we couldn't follow)? if self.curpath is None: break try: # FIXME unify with stepi code... op = self.parseOpcode(starteip) self.op = op if self.emumon: try: self.emumon.prehook(self, op, starteip) except Exception as e: if not self.getMeta('silent'): logger.warn( "funcva: 0x%x opva: 0x%x: %r (%r) (in emumon prehook)", funcva, starteip, op, e) if self.emustop: return # Execute the opcode self.executeOpcode(op) vg_path.getNodeProp(self.curpath, 'valist').append(starteip) endeip = self.getProgramCounter() if self.emumon: try: self.emumon.posthook(self, op, endeip) except Exception as e: if not self.getMeta('silent'): logger.warn( "funcva: 0x%x opva: 0x%x: %r (%r) (in emumon posthook)", funcva, starteip, op, e) if self.emustop: return iscall = self.checkCall(starteip, endeip, op) if self.emustop: return # If it wasn't a call, check for branches, if so, add them to # the todo list and go around again... if not iscall: blist = self.checkBranches(starteip, endeip, op) if len(blist): # pc in the snap will be wrong, but over-ridden at restore esnap = self.getEmuSnap() for bva, bpath in blist: todo.append((bva, esnap, bpath)) break # If we enounter a procedure exit, it doesn't # matter what EIP is, we're done here. if op.iflags & envi.IF_RET: vg_path.setNodeProp(self.curpath, 'cleanret', True) break except envi.UnsupportedInstruction as e: if self.strictops: logger.debug( 'runFunction failed: unsupported instruction: 0x%08x %s', e.op.va, e.op.mnem) break else: logger.debug( 'runFunction continuing after unsupported instruction: 0x%08x %s', e.op.va, e.op.mnem) self.setProgramCounter(e.op.va + e.op.size) except Exception as e: if self.emumon is not None and not isinstance( e, e_exc.BreakpointHit): self.emumon.logAnomaly(self, starteip, str(e)) break # If we exc during execution, this branch is dead.
def setPathProp(self, key, value): """ Set a named value which is only relevant for the current code path. """ return vg_path.setNodeProp(self.curpath, key, value)
def getFuncCbRoutedPaths_genback(self, fromva, tova, loopcnt=0, maxpath=None, timeout=None): ''' Yields all the paths through the hierarchical graph starting at the "root nodes" and ending at tocbva. Specify a loopcnt to allow loop paths to be generated with the given "loop iteration count" Example: for path in getCodePathsTo(fgraph, tocbva): for node,edge in path: ...etc... ''' fgraph = self.graph self.__update = 0 self.__go__ = True pathcnt = 0 tocbva = getGraphNodeByVa(fgraph, tova) frcbva = getGraphNodeByVa(fgraph, fromva) preRouteGraph(fgraph, fromva, tova) pnode = vg_pathcore.newPathNode(nid=tocbva, eid=None) todo = [ (tocbva, pnode), ] maxtime = None if timeout: maxtime = time.time() + timeout while todo: if maxtime and time.time() > maxtime: raise PathForceQuitException() if not self.__go__: raise PathForceQuitException() nodeid, cpath = todo.pop() refsto = fgraph.getRefsTo((nodeid, None)) # This is the root node! if nodeid == frcbva: path = vg_pathcore.getPathToNode(cpath) path.reverse() self.__steplock.acquire() yield [viv_graph._nodeedge(n) for n in path] vg_pathcore.trimPath(cpath) pathcnt += 1 self.__update = 1 self.__steplock.release() if maxpath and pathcnt >= maxpath: return for eid, fromid, toid, einfo in refsto: if fgraph.getNodeProps(fromid).get('up') != True: # TODO: drop the bad edges from graph in preprocessing? instead of "if" here vg_pathcore.trimPath(cpath) continue # Skip loops if they are "deeper" than we are allowed loops = vg_pathcore.getPathLoopCount(cpath, 'nid', fromid) if loops > loopcnt: continue vg_pathcore.setNodeProp(cpath, 'eid', eid) npath = vg_pathcore.newPathNode(parent=cpath, nid=fromid, eid=None) todo.append((fromid, npath)) self.__go__ = False
def runFunction(self, funcva, stopva=None, maxhit=None, maxloop=None): """ This is a utility function specific to WorkspaceEmulation (and impemu) that will emulate, but only inside the given function. You may specify a stopva to return once that location is hit. """ self.funcva = funcva # Let the current (should be base also) path know where we are starting vg_path.setNodeProp(self.curpath, 'bva', funcva) hits = {} todo = [(funcva,self.getEmuSnap(),self.path),] vw = self.vw # Save a dereference many many times while len(todo): va,esnap,self.curpath = todo.pop() self.setEmuSnap(esnap) self.setProgramCounter(va) # Check if we are beyond our loop max... if maxloop != None: lcount = vg_path.getPathLoopCount(self.curpath, 'bva', va) if lcount > maxloop: continue while True: starteip = self.getProgramCounter() if not vw.isValidPointer(starteip): break if starteip == stopva: return # Check straight hit count... if maxhit != None: h = hits.get(starteip, 0) h += 1 if h > maxhit: break hits[starteip] = h # If we ran out of path (branches that went # somewhere that we couldn't follow? if self.curpath == None: break try: # FIXME unify with stepi code... op = self.parseOpcode(starteip) self.op = op if self.emumon: self.emumon.prehook(self, op, starteip) if self.emustop: return # Execute the opcode self.executeOpcode(op) vg_path.getNodeProp(self.curpath, 'valist').append(starteip) endeip = self.getProgramCounter() if self.emumon: self.emumon.posthook(self, op, endeip) if self.emustop: return iscall = self.checkCall(starteip, endeip, op) if self.emustop: return # If it wasn't a call, check for branches, if so, add them to # the todo list and go around again... if not iscall: blist = self.checkBranches(starteip, endeip, op) if len(blist): # pc in the snap will be wrong, but over-ridden at restore esnap = self.getEmuSnap() for bva,bpath in blist: todo.append((bva, esnap, bpath)) break # If we enounter a procedure exit, it doesn't # matter what EIP is, we're done here. if op.iflags & envi.IF_RET: vg_path.setNodeProp(self.curpath, 'cleanret', True) break except envi.UnsupportedInstruction, e: if self.strictops: break else: print 'runFunction continuing after unsupported instruction: 0x%08x %s' % (e.op.va, e.op.mnem) self.setProgramCounter(e.op.va+ e.op.size) except Exception, e: #traceback.print_exc() if self.emumon != None: self.emumon.logAnomaly(self, starteip, str(e)) break # If we exc during execution, this branch is dead.
def _runFunction(self, funcva, stopva=None, maxhit=None, maxloop=None, maxrep=None, strictops=True, func_only=True): """ :param func_only: is this emulator meant to stay in one function scope? :param strictops: should we bail on emulation if unsupported instruction encountered """ vg_path.setNodeProp(self.curpath, 'bva', funcva) hits = {} rephits = {} todo = [(funcva, self.getEmuSnap(), self.path), ] emu = self._emu vw = self._emu.vw # Save a dereference many many times depth = 0 op = None while len(todo) > 0: va, esnap, self.curpath = todo.pop() self.setEmuSnap(esnap) emu.setProgramCounter(va) # Check if we are beyond our loop max... if maxloop != None: lcount = vg_path.getPathLoopCount(self.curpath, 'bva', va) if lcount > maxloop: continue while True: startpc = emu.getProgramCounter() if not vw.isValidPointer(startpc): break if startpc == stopva: return # If we ran out of path (branches that went # somewhere that we couldn't follow? if self.curpath == None: break try: op = emu.parseOpcode(startpc) if op.prefixes & PREFIX_REP and maxrep != None: # execute same instruction with `rep` prefix up to maxrep times h = rephits.get(startpc, 0) h += 1 if h > maxrep: break rephits[startpc] = h elif maxhit != None: # Check straight hit count for all other instructions... h = hits.get(startpc, 0) h += 1 if h > maxhit: break hits[startpc] = h nextpc = startpc + len(op) self.op = op for mon in self._monitors: mon.prehook(emu, op, startpc) iscall = bool(op.iflags & v_envi.IF_CALL) if iscall: wentInto = self.handleCall(startpc, op, avoid_calls=func_only) if wentInto: depth += 1 else: emu.executeOpcode(op) vg_path.getNodeProp(self.curpath, 'valist').append(startpc) endpc = emu.getProgramCounter() for mon in self._monitors: mon.posthook(emu, op, endpc) if not iscall: # If it wasn't a call, check for branches, if so, add them to # the todo list and go around again... blist = emu.checkBranches(startpc, endpc, op) if len(blist) > 0: # pc in the snap will be wrong, but over-ridden at restore esnap = self.getEmuSnap() for bva, bpath in blist: todo.append((bva, esnap, bpath)) break if op.iflags & v_envi.IF_RET: vg_path.setNodeProp(self.curpath, 'cleanret', True) if depth == 0: break else: depth -= 1 # If we enounter a procedure exit, it doesn't # matter what PC is, we're done here. except v_envi.UnsupportedInstruction as e: if strictops: break else: self._logger.debug('runFunction continuing after unsupported instruction: 0x%08x %s', e.op.va, e.op.mnem) emu.setProgramCounter(e.op.va + e.op.size) except Exception as e: self._logger.warning("error during emulation of function: %s", e)#, exc_info=True) for mon in self._monitors: mon.logAnomaly(emu, startpc, str(e)) break # If we exc during execution, this branch is dead.
def _runFunction(self, funcva, stopva=None, maxhit=None, maxloop=None, maxrep=None, strictops=True, func_only=True): """ :param func_only: is this emulator meant to stay in one function scope? :param strictops: should we bail on emulation if unsupported instruction encountered """ vg_path.setNodeProp(self.curpath, 'bva', funcva) hits = {} rephits = {} todo = [ (funcva, self.getEmuSnap(), self.path), ] emu = self._emu vw = self._emu.vw # Save a dereference many many times depth = 0 op = None while len(todo) > 0: va, esnap, self.curpath = todo.pop() self.setEmuSnap(esnap) emu.setProgramCounter(va) # Check if we are beyond our loop max... if maxloop != None: lcount = vg_path.getPathLoopCount(self.curpath, 'bva', va) if lcount > maxloop: continue while True: startpc = emu.getProgramCounter() if not vw.isValidPointer(startpc): break if startpc == stopva: return # If we ran out of path (branches that went # somewhere that we couldn't follow? if self.curpath == None: break try: op = emu.parseOpcode(startpc) if op.prefixes & PREFIX_REP and maxrep != None: # execute same instruction with `rep` prefix up to maxrep times h = rephits.get(startpc, 0) h += 1 if h > maxrep: break rephits[startpc] = h elif maxhit != None: # Check straight hit count for all other instructions... h = hits.get(startpc, 0) h += 1 if h > maxhit: break hits[startpc] = h nextpc = startpc + len(op) self.op = op for mon in self._monitors: mon.prehook(emu, op, startpc) iscall = bool(op.iflags & v_envi.IF_CALL) if iscall: wentInto = self.handleCall(startpc, op, avoid_calls=func_only) if wentInto: depth += 1 else: emu.executeOpcode(op) vg_path.getNodeProp(self.curpath, 'valist').append(startpc) endpc = emu.getProgramCounter() for mon in self._monitors: mon.posthook(emu, op, endpc) if not iscall: # If it wasn't a call, check for branches, if so, add them to # the todo list and go around again... blist = emu.checkBranches(startpc, endpc, op) if len(blist) > 0: # pc in the snap will be wrong, but over-ridden at restore esnap = self.getEmuSnap() for bva, bpath in blist: todo.append((bva, esnap, bpath)) break if op.iflags & v_envi.IF_RET: vg_path.setNodeProp(self.curpath, 'cleanret', True) if depth == 0: break else: depth -= 1 # If we enounter a procedure exit, it doesn't # matter what PC is, we're done here. except v_envi.UnsupportedInstruction as e: if strictops: break else: self._logger.debug( 'runFunction continuing after unsupported instruction: 0x%08x %s', e.op.va, e.op.mnem) emu.setProgramCounter(e.op.va + e.op.size) except Exception as e: self._logger.warning( "error during emulation of function: %s", e) #, exc_info=True) for mon in self._monitors: mon.logAnomaly(emu, startpc, str(e)) break # If we exc during execution, this branch is dead.