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 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 _do_argtrack(self, line): #FIXME: this is currently broken and needs to be revamped to use # apiCall and VivTaints """ Track input arguments to the given function by name or address. (this is currently broken. sorry!) Usage: argtrack <func_addr_expr> <arg_idx> """ if not line: return self.do_help("argtrack") argv = e_cli.splitargs(line) if len(argv) != 2: return self.do_help("argtrack") try: fva = self.parseExpression(argv[0]) except Exception: self.vprint("Invalid Address Expression: %s" % argv[0]) return try: idx = self.parseExpression(argv[1]) except Exception: self.vprint("Invalid Index Expression: %s" % argv[1]) return if self.getFunction(fva) != fva: self.vprint("Invalid Function Address: (0x%.8x) %s" % (fva, line)) for pleaf in viv_vector.trackArgOrigin(self, fva, idx): self.vprint('=' * 80) path = vg_path.getPathToNode(pleaf) path.reverse() for pnode in path: fva = vg_path.getNodeProp(pnode, 'fva') argv = vg_path.getNodeProp(pnode, 'argv') callva = vg_path.getNodeProp(pnode, 'cva') argidx = vg_path.getNodeProp(pnode, 'argidx') if callva is not None: aval, amagic = argv[argidx] arepr = '0x%.8x' % aval if amagic is not None: arepr = repr(amagic) frepr = 'UNKNOWN' if fva is not None: frepr = '0x%.8x' % fva self.vprint('func: %s calls at: 0x%.8x with his own: %s' % (frepr, callva, arepr)) self.vprint("=" * 80)
def do_argtrack(self, line): """ Track input arguments to the given function by name or address. Usage: argtrack <func_addr_expr> <arg_idx> """ if not line: return self.do_help("argtrack") argv = e_cli.splitargs(line) if len(argv) != 2: return self.do_help("argtrack") try: fva = self.parseExpression(argv[0]) except Exception as e: self.vprint("Invalid Address Expression: %s" % argv[0]) return try: idx = self.parseExpression(argv[1]) except Exception as e: self.vprint("Invalid Index Expression: %s" % argv[1]) return if self.getFunction(fva) != fva: self.vprint("Invalid Function Address: (0x%.8x) %s" % (fva, line)) for pleaf in viv_vector.trackArgOrigin(self, fva, idx): self.vprint('='*80) path = vg_path.getPathToNode(pleaf) path.reverse() for pnode in path: fva = vg_path.getNodeProp(pnode, 'fva') argv = vg_path.getNodeProp(pnode, 'argv') callva = vg_path.getNodeProp(pnode, 'cva') argidx = vg_path.getNodeProp(pnode, 'argidx') if callva != None: aval, amagic = argv[argidx] arepr = '0x%.8x' % aval if amagic != None: arepr = repr(amagic) frepr = 'UNKNOWN' if fva != None: frepr = '0x%.8x' % fva self.vprint('func: %s calls at: 0x%.8x with his own: %s' % (frepr, callva, arepr)) self.vprint("="*80)
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 getBranchNode(self, node, bva): ''' If a node exists already for the specified branch, return it. Otherwise, create a new one and return that... ''' for knode in vg_path.getNodeKids(node): if vg_path.getNodeProp(knode, 'bva') == bva: return knode return self.newCodePathNode(node, bva)
def stack_track_visitor(node, **kwargs): vw = kwargs.get('vw') res = kwargs.get('res') emu = kwargs.get('emu') agg = kwargs.get('agg') logger = kwargs.get('logger') if (vw is None) or (emu is None) or (logger is None): raise RuntimeError('Bad arguments to stack_track_visitor') if agg is None: agg = StringAccumulator() wlog = vg_path.getNodeProp(node, 'writelog') for eip, va, bytes in wlog: # no longer check if it's writing into a stack memory location or not # this allows us to grab manually constructed strings in .data # as well if eip == 0: #logger.debug('Skipping 0 eip: 0x%08x 0x%08x: %s', eip, va, binascii.hexlify(bytes)) continue logger.debug('visiting: 0x%08x: *> 0x%08x 0x%x bytes', eip, va, len(bytes)) op = vw.parseOpcode(eip) if op.getPrefixName().startswith('rep'): #ignore rep instructions -> never seen used to acutally construct strings, # and causes lots of FPs continue elif op.mnem.startswith('call'): logger.debug('Aggregating due to call: 0x%08x', eip) agg.aggregateStack() elif all([i == '\x00' for i in bytes]): logger.debug('Adding null at 0x%08x: 0x%08x', eip, va) agg.addItem((eip, va, bytes)) if op.mnem.startswith('push'): #aggregating based purely on pushes lead to a lot of FPs pass else: agg.aggregateStack() elif all([isAscii(i) for i in bytes]): agg.addItem((eip, va, bytes)) logger.debug('Adding wlog entry: 0x%08x 0x%08x: %s', eip, va, binascii.hexlify(bytes)) elif all([isAscii(i) for i in bytes[::2]]) and all( [i == '\x00' for i in bytes[1::2]]): #just looking for wchar strings made of ascii chars agg.addItem((eip, va, bytes)) logger.debug('Adding possible wchar wlog entry: 0x%08x 0x%08x: %s', eip, va, binascii.hexlify(bytes)) else: logger.debug('Skipping wlog entry: 0x%08x 0x%08x: %s', eip, va, binascii.hexlify(bytes)) agg.aggregateStack() if res is not None: #if we're using a local agg, put the results in res res.extend(agg.stringDict.values())
def build_emu_va_map(node, **kwargs): res = kwargs.get('res') emu = kwargs.get('emu') logtype = kwargs.get('logtype') if (res is None) or (emu is None) or (logtype is None): return #for va in vg_path.getNodeProp(node, 'valist'): # res[va] = node #for pc, va, bytes in vg_path.getNodeProp(node, 'writelog'): for entry in vg_path.getNodeProp(node, logtype): pc, va, bytes = entry res[pc] = entry
def stepi(self): # NOTE: when we step, we *always* want to be stepping over calls # (and possibly import emulate them) starteip = self.getProgramCounter() # parse out an opcode op = self.parseOpcode(starteip) if self.emumon: self.emumon.prehook(self, op, starteip) # 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 not self.checkCall(starteip, endeip, op): self.checkBranches(starteip, endeip, op)
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 stack_track_visitor(node, **kwargs): vw = kwargs.get('vw') res = kwargs.get('res') emu = kwargs.get('emu') agg = kwargs.get('agg') logger = kwargs.get('logger') if (vw is None) or (emu is None) or (logger is None): raise RuntimeError('Bad arguments to stack_track_visitor') if agg is None: agg = StringAccumulator() wlog = vg_path.getNodeProp(node, 'writelog') for eip, va, bytes in wlog: # no longer check if it's writing into a stack memory location or not # this allows us to grab manually constructed strings in .data # as well if eip == 0: #logger.debug('Skipping 0 eip: 0x%08x 0x%08x: %s', eip, va, binascii.hexlify(bytes)) continue logger.debug('visiting: 0x%08x: *> 0x%08x 0x%x bytes', eip, va, len(bytes)) op = vw.parseOpcode(eip) if op.getPrefixName().startswith('rep'): #ignore rep instructions -> never seen used to acutally construct strings, # and causes lots of FPs continue elif op.mnem.startswith('call'): logger.debug('Aggregating due to call: 0x%08x', eip) agg.aggregateStack() elif all([i == '\x00' for i in bytes]): logger.debug('Adding null at 0x%08x: 0x%08x', eip, va) agg.addItem((eip, va, bytes)) if op.mnem.startswith('push'): #aggregating based purely on pushes lead to a lot of FPs pass else: agg.aggregateStack() elif all( [isAscii(i) for i in bytes]): agg.addItem((eip, va, bytes)) logger.debug('Adding wlog entry: 0x%08x 0x%08x: %s', eip, va, binascii.hexlify(bytes)) elif all( [isAscii(i) for i in bytes[::2]]) and all([i =='\x00' for i in bytes[1::2]]): #just looking for wchar strings made of ascii chars agg.addItem((eip, va, bytes)) logger.debug('Adding possible wchar wlog entry: 0x%08x 0x%08x: %s', eip, va, binascii.hexlify(bytes)) else: logger.debug('Skipping wlog entry: 0x%08x 0x%08x: %s', eip, va, binascii.hexlify(bytes)) agg.aggregateStack() if res is not None: #if we're using a local agg, put the results in res res.extend(agg.stringDict.values())
def writeMemory(self, va, bytes): """ Try to write the bytes to the memory object, otherwise, dont' complain... """ if self.logwrite: wlog = vg_path.getNodeProp(self.curpath, 'writelog') wlog.append((self.getProgramCounter(), va, bytes)) self._useVirtAddr(va) # It's totally ok to write to invalid memory during the # emulation pass (as long as safe_mem is true...) probeok = self.probeMemory(va, len(bytes), e_mem.MM_WRITE) if self._safe_mem and not probeok: return return e_mem.MemoryObject.writeMemory(self, va, bytes)
def writeMemory(self, va, bytes): """ Try to write the bytes to the memory object, otherwise, dont' complain... """ if self.logwrite: wlog = vg_path.getNodeProp(self.curpath, 'writelog') wlog.append((self.getProgramCounter(),va,bytes)) self._useVirtAddr( va ) # It's totally ok to write to invalid memory during the # emulation pass (as long as safe_mem is true...) probeok = self.probeMemory(va, len(bytes), e_mem.MM_WRITE) if self._safe_mem and not probeok: return return e_mem.MemoryObject.writeMemory(self, va, bytes)
def readMemory(self, va, size): if self.logread: rlog = vg_path.getNodeProp(self.curpath, 'readlog') rlog.append((self.getProgramCounter(), va, size)) # If they read an import entry, start a taint... loc = self.vw.getLocation(va) if loc != None: lva, lsize, ltype, ltinfo = loc if ltype == LOC_IMPORT and lsize == size: # They just read an import. ret = self.setVivTaint('import', loc) return e_bits.buildbytes(ret, lsize) self._useVirtAddr(va) # Read from the emulator's pages if we havent resolved it yet probeok = self.probeMemory(va, size, e_mem.MM_READ) if self._safe_mem and not probeok: return 'A' * size return e_mem.MemoryObject.readMemory(self, va, size)
def readMemory(self, va, size): if self.logread: rlog = vg_path.getNodeProp(self.curpath, 'readlog') rlog.append((self.getProgramCounter(),va,size)) # If they read an import entry, start a taint... loc = self.vw.getLocation(va) if loc != None: lva, lsize, ltype, ltinfo = loc if ltype == LOC_IMPORT and lsize == size: # They just read an import. ret = self.setVivTaint('import', loc) return e_bits.buildbytes(ret, lsize) self._useVirtAddr(va) # Read from the emulator's pages if we havent resolved it yet probeok = self.probeMemory(va, size, e_mem.MM_READ) if self._safe_mem and not probeok: return 'A' * size return e_mem.MemoryObject.readMemory(self, va, size)
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 runStep(self, maxstep=1000000, follow=True, showafter=True, runTil=None, pause=True, silent=False, finish=0, tracedict=None): ''' runStep is the core "debugging" functionality for this emulation-helper. it's goal is to provide a single-step interface somewhat like what you might get from a GDB experience. pertinent registers are printed with their values, the current instruction, and any helpers that the operands may point to in memory (as appropriate). special features: * tracedict allows code to be evaluated and printed at specific addresses: tracedict={va:'python code here', 'locals':{'something':4}} * prints out the operands *after* exection as well (arg:showafter=True) * cli interface allows viewing and modifying memory/python objects: rax [rax] [rax:23] [rax+8:4] [0xf00b4:8] rax=42 [0xf00b4]=0x47145 * cli allows skipping printing (arg:silent=True) silent=True * cli allows running until a VA without pauses: go 0x12345 * cli allows executing until next branch: b * cli allows dumping the stack: stack * cli allows viewing/setting the Program Counter: pc pc=0x43243 * cli allows skipping instructions: skip * cli allows numerous libc-style functions: memset memcpy strcpy strncpy strcat strlen * call_handlers dict (global in the library) allows swapping in our python code in place of calls to other binary code, like memcpy, or other code which may fail in an emulator ''' emu = self.emu mcanv = e_memcanvas.StringMemoryCanvas(emu, syms=emu.vw) self.mcanv = mcanv # store it for later inspection # set up tracedict if tracedict is None: tracedict = {} else: print("tracedict entries for %r" % (','.join( [hex(key) for key in tracedict.keys() if type(key) == int]))) nonstop = 0 tova = None quit = False moveon = False emuBranch = False silentUntil = None # set silentExcept to include all tracedict items silentExcept = [va for va, expr in tracedict.items() if expr is None] i = 0 self.startRun = time.time() while maxstep > i: try: skip = skipop = False i += 1 pc = emu.getProgramCounter() if pc in (runTil, finish): print("PC reached 0x%x." % pc) break op = emu.parseOpcode(pc) self.op = op # store it for later in case of post-mortem # cancel emuBranch as we've come to one if op.isReturn() or op.isCall(): emuBranch = False #### TRACING tdata = tracedict.get(pc) if tdata is not None: try: lcls = locals() outlcls = tracedict.get('locals') if outlcls is not None: lcls.update(outlcls) print(repr(eval(tdata, globals(), lcls))) except Exception as e: print("TraceMonitor ERROR at 0x%x: %r" % (pc, e)) #### if silentUntil == pc: silent = False silentUntil = None self.printStats(i) if silent and not pc in silentExcept: showafter = False else: # do all the interface stuff here: self.showPriRegisters(snapshot=SNAP_SWAP) self.showFlags() # ARM fails this right now. try: self.printMemStatus(op) except Exception as e: print("MEM ERROR: %s: 0x%x %s" % (e, op.va, op)) import traceback traceback.print_exc() print("Step: %s" % i) mcanv.clearCanvas() try: op.render(mcanv) except Exception as e: print("ERROR rendering opcode: %r" % e) extra = self.getNameRefs(op) opbytes = emu.readMemory(pc, len(op)) print("%.4x\t%20s\t%s\t%s" % (pc, hexlify(opbytes), mcanv.strval, extra)) print("---------") prompt = "q<enter> - exit, eval code to execute, 'skip' an instruction, 'b'ranch, 'go [+]#' to va or +# instrs or enter to continue: " # nonstop controls whether we stop. tova indicates we're hunting for a va, otherwise # treat nonstop as a negative-counter if tova is not None: if pc == tova: nonstop = 0 elif nonstop: nonstop -= 1 if not (emuBranch or nonstop) and pause: tova = None moveon = False uinp = input(prompt) while len(uinp) and not (moveon or quit or emuBranch): try: if uinp == "q": quit = True break elif uinp.startswith('silent'): parts = uinp.split(' ') silentUntil = parseExpression( emu, parts[-1]) silent = True elif uinp.startswith('go '): args = uinp.split(' ') if args[-1].startswith('+'): nonstop = parseExpression( emu, args[-1]) else: tova = parseExpression(emu, args[-1]) nonstop = 1 break elif uinp in ('b', 'branch'): emuBranch = True break elif uinp == 'stack': self.stackDump() moveon = True break elif uinp == 'refresh': # basically does a NOP, doesn't change anything, just let the data be reprinted. moveon = True break elif uinp.startswith('pc=') or uinp.startswith( 'pc ='): print("handling setProgramCounter()") args = uinp.split('=') newpc = parseExpression(emu, args[-1]) print("new PC: 0x%x" % newpc) emu.setProgramCounter(newpc) moveon = True break elif '=' in uinp: print( "handling generic register/memory writes" ) args = uinp.split('=') data = args[-1].strip( ) # .split(',') ??? why did i ever do this? if '[' in args[0]: # memory derefs tgt = args[0].replace('[', '').replace( ']', '').split(':') if len(tgt) > 1: size = parseExpression( emu, tgt[-1]) else: size = 4 addrstr = tgt[0] memaddr = parseExpression(emu, addrstr) if data.startswith( '"') and data.endswith('"'): # write string data emu.writeMemory( memaddr, data[1:-1]) else: # write number emu.writeMemValue( memaddr, parseExpression(emu, data), size) else: # must be registers emu.setRegisterByName( args[0], parseExpression(emu, data)) elif uinp.strip().startswith( '[') and ']' in uinp: try: idx = uinp.find('[') + 1 eidx = uinp.find(']', idx) expr = uinp[idx:eidx] print("handling memory read at [%s]" % expr) size = emu.getPointerSize() if ':' in expr: nexpr, size = expr.rsplit(':', 1) try: size = parseExpression( emu, size) expr = nexpr except Exception as e: # if number fails, just continue with a default size and the original expr print( "unknown size: %r. using default size." % size) va = parseExpression(emu, expr) data = emu.readMemory(va, size) print("[%s:%s] == %r" % (expr, size, data.hex())) except Exception as e: print("ERROR: %r" % e) elif uinp == 'skip': newpc = emu.getProgramCounter() + len(op) print("new PC: 0x%x" % newpc) skipop = True break elif uinp == 'memset': print(memset(emu)) skipop = True elif uinp == 'memcpy': print(memcpy(emu)) skipop = True elif uinp == 'strcpy': print(strcpy(emu)) skipop = True elif uinp == 'strncpy': print(strncpy(emu)) skipop = True elif uinp == 'strcat': print(strcat(emu)) skipop = True elif uinp == 'strlen': print(strlen(emu)) skipop = True else: try: lcls = locals() lcls.update(emu.getRegisters()) out = eval(uinp, globals(), lcls) if type(out) == int: print(hex(out)) else: print(out) except: import sys sys.excepthook(*sys.exc_info()) except: traceback.print_exc() #self.printStats(i) uinp = input(prompt) if quit: print("Quitting!") self.printStats(i) return if moveon: continue # handle Calls separately if len(op.opers) and op.iflags & (envi.IF_CALL) and not skipop: self.dbgprint("Call...") tva = op.getOperValue(0, emu) handler = self.call_handlers.get(tva) self.dbgprint(" handler for call to (0x%x): %r" % (tva, handler)) if handler is not None: handler(emu, op) skipop = True elif follow and not skip and not skipop: # use the emulator to execute the call starteip = emu.getProgramCounter() if hasattr(emu, 'emumon') and emu.emumon is not None: emu.emumon.prehook(emu, op, starteip) emu.executeOpcode(op) endeip = emu.getProgramCounter() i += 1 if hasattr(emu, 'emumon') and emu.emumon is not None: emu.emumon.posthook(emu, op, endeip) self.dbgprint( "starteip: 0x%x, endeip: 0x%x -> %s" % (starteip, endeip, emu.vw.getName(endeip))) if hasattr(emu, 'curpath'): vg_path.getNodeProp(emu.curpath, 'valist').append(starteip) skip = True # if not already emulated a call, execute the instruction here... if not skip and not skipop: emu.stepi() # print the updated latest stuff.... if showafter: try: extra = self.getNameRefs(op) if len(extra): print("after:\t%s\t%s" % (mcanv.strval, extra)) self.printMemStatus(op, use_cached=True) except Exception as e: print("MEM ERROR: %s: 0x%x %s" % (e, op.va, op)) import sys sys.excepthook(*sys.exc_info()) # unless we've asked to skip the instruction... elif skipop: newpc = emu.getProgramCounter() + len(op) emu.setProgramCounter(newpc) except KeyboardInterrupt: self.printStats(i) break except envi.SegmentationViolation: import sys sys.excepthook(*sys.exc_info()) break except: import sys sys.excepthook(*sys.exc_info()) self.printStats(i)
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 getPathProp(self, key): ''' Retrieve a named value from the current code path context. ''' return vg_path.getNodeProp(self.curpath, key)
def runStep(self, maxstep=1000000, follow=True, showafter=True, runTil=None, pause=True, silent=False, finish=0, silentExcept=()): global op, mcanv, call_handlers emu = self.emu mcanv = e_memcanvas.StringMemoryCanvas(emu, syms=emu.vw) nonstop = 0 tova = None quit = False moveon = False emuBranch = False i = 0 while maxstep > i: skip = skipop = False i += 1 pc = emu.getProgramCounter() if pc in (runTil, finish): break op = emu.parseOpcode(pc) # cancel emuBranch as we've come to one if op.isReturn() or op.isCall(): emuBranch = False if silent and not pc in silentExcept: showafter = False else: # do all the interface stuff here: showPriRegisters(emu, snapshot=SNAP_SWAP) #showFlags(emu) # ARM fails this right now. try: printMemStatus(emu, op) except Exception as e: print "MEM ERROR: %s: 0x%x %s" % (e, op.va, op) print "Step: %s" % i mcanv.clearCanvas() try: op.render(mcanv) except Exception as e: print "ERROR rendering opcode: %r" % e extra = getNameRefs(op, emu) opbytes = emu.readMemory(pc, len(op)) print("%.4x\t%20s\t%s\t%s" % (pc, sp.hexText(opbytes), mcanv.strval, extra)) print "---------" prompt = "q<enter> - exit, eval code to execute, 'skip' an instruction, 'b'ranch, 'go [+]#' to va or +# instrs or enter to continue: " # nonstop controls whether we stop. tova indicates we're hunting for a va, otherwise # treat nonstop as a negative-counter if tova is not None: if pc == tova: nonstop = 0 elif nonstop: nonstop -= 1 if not (emuBranch or nonstop) and pause: tova = None moveon = False uinp = raw_input(prompt) while len(uinp) and not (moveon or quit or emuBranch): if uinp == "q": quit = True break elif uinp.startswith('go '): args = uinp.split(' ') if args[-1].startswith('+'): nonstop = int(args[-1], 0) else: tova = int(args[-1], 0) nonstop = 1 break elif uinp in ('b', 'branch'): emuBranch = True break elif uinp == 'stack': stackDump(emu) break elif uinp == 'refresh': # basically does a NOP, doesn't change anything, just let the data be reprinted. moveon = True break elif uinp.startswith('pc=') or uinp.startswith('pc ='): print "handling setProgramCounter()" args = uinp.split('=') newpc = int(args[-1], 0) print "new PC: 0x%x" % newpc emu.setProgramCounter(newpc) moveon = True break elif '=' in uinp: print "handling generic register/memory writes" args = uinp.split('=') details = args[-1].split(',') if '[' in args[0]: if len(details) > 1: size = int(details[-1]) else: size = 4 #memaddr = int(args[0].replace('[','').replace(']',''), 0) memaddr = emu.vw.parseExpression( args[0].replace('[', '').replace(']', '')) emu.writeMemValue( memaddr, emu.vw.parseExpression(details[0], 0), size) #emu.writeMemValue(memaddr, int(details[0], 0), size) else: # must be registers emu.setRegisterByName(args[0], int(details[0], 0)) elif uinp.strip().startswith('[') and ']' in uinp: try: idx = uinp.find('[') + 1 eidx = uinp.find(']', idx) expr = uinp[idx:eidx] print "handling memory read at [%s]" % expr size = emu.getPointerSize() if ':' in expr: nexpr, size = expr.rsplit(':', 1) try: size = emu.vw.parseExpression(size) expr = nexpr except: # if number fails, just continue with a default size and the original expr pass va = emu.vw.parseExpression(expr) data = emu.readMemory(va, size) print "[%s:%s] == %r" % (expr, size, data.encode('hex')) except Exception as e: print "ERROR: %r" % e elif uinp == 'skip': newpc = emu.getProgramCounter() + len(op) print "new PC: 0x%x" % newpc skipop = True break elif uinp == 'memset': print memset(emu) skipop = True elif uinp == 'memcpy': print memcpy(emu) skipop = True elif uinp == 'strcpy': print strcpy(emu) skipop = True elif uinp == 'strncpy': print strncpy(emu) skipop = True elif uinp == 'strcat': print strcat(emu) skipop = True elif uinp == 'strlen': print strlen(emu) skipop = True else: try: print eval(uinp, globals(), locals()) except: sys.excepthook(*sys.exc_info()) uinp = raw_input(prompt) if quit: return if moveon: continue if len(op.opers) and op.iflags & (envi.IF_CALL) and not skipop: self.dbgprint("Call...") tva = op.getOperValue(0, emu) handler = self.call_handlers.get(tva) self.dbgprint(" handler for call to (0x%x): %r" % (tva, handler)) if handler is not None: handler(emu, op) skipop = True elif follow and not skip and not skipop: # use the emulator to execute the call starteip = emu.getProgramCounter() emu.executeOpcode(op) endeip = emu.getProgramCounter() i += 1 self.dbgprint("starteip: 0x%x, endeip: 0x%x -> %s" % (starteip, endeip, emu.vw.getName(endeip))) vg_path.getNodeProp(emu.curpath, 'valist').append(starteip) skip = True if not skip and not skipop: # if not already emulated a call, execute the instruction here... emu.stepi() # print the updated latest stuff.... if showafter: try: extra = getNameRefs(op, emu) if len(extra): print("after:\t%s\t%s" % (mcanv.strval, extra)) printMemStatus(emu, op, use_cached=True) except Exception as e: print "MEM ERROR: %s: 0x%x %s" % (e, op.va, op) elif skipop: newpc = emu.getProgramCounter() + len(op) emu.setProgramCounter(newpc)
def getCodePaths(vw, fromva, tova, trim=True): """ Return a list of paths, where each path is a list of code blocks from fromva to tova. Usage: getCodePaths(vw, <fromva>, <tova>) -> [ [frblock, ..., toblock], ...] NOTE: "trim" causes an optimization which may not reveal *all* the paths, but is much faster to run. It will never return no paths when there are some, but may not return all of them... (based on path overlap) """ done = {} res = [] frcb = vw.getCodeBlock(fromva) tocb = vw.getCodeBlock(tova) if frcb == None: raise viv_exc.InvalidLocation(fromva) if tocb == None: raise viv_exc.InvalidLocation(tova) frva = frcb[0] # For compare speed root = vg_path.newPathNode(cb=tocb, cbva=tocb[0]) todo = [root, ] done[tova] = tocb cbcache = {} while len(todo): path = todo.pop() cbva = vg_path.getNodeProp(path, 'cbva') codeblocks = cbcache.get(cbva) if codeblocks == None: codeblocks = getCodeFlow(vw, cbva) cbcache[cbva] = codeblocks for cblock in codeblocks: bva,bsize,bfva = cblock # Don't follow loops... if vg_path.isPathLoop(path, 'cbva', bva): continue # If we have been here before and it's *not* the answer, # skip out... if trim and done.get(bva) != None: continue done[bva] = cblock newpath = vg_path.newPathNode(parent=path, cb=cblock, cbva=bva) # If this one is a match, we don't need to # track past it. Also, put it in the results list # so we don't have to do it later.... if bva == frva: res.append(newpath) else: todo.append(newpath) # Now... if we have some results, lets build the block list. ret = [] for cpath in res: fullpath = vg_path.getPathToNode(cpath) # We actually do it by inbound references, so reverse the result! fullpath.reverse() ret.append([vg_path.getNodeProp(path, 'cb') for path in fullpath]) return ret
def _nodeedgeloop(tnode): nid = vg_pathcore.getNodeProp(tnode, 'nid') eid = vg_pathcore.getNodeProp(tnode, 'eid') loop = vg_pathcore.getNodeProp(tnode, 'loops') return nid,eid,loop
except Exception, e: self.vprint("Invalid Index Expression: %s" % argv[1]) return if self.getFunction(fva) != fva: self.vprint("Invalid Function Address: (0x%.8x) %s" % (fva, line)) for pleaf in viv_vector.trackArgOrigin(self, fva, idx): self.vprint("=" * 80) path = vg_path.getPathToNode(pleaf) path.reverse() for pnode in path: fva = vg_path.getNodeProp(pnode, "fva") argv = vg_path.getNodeProp(pnode, "argv") callva = vg_path.getNodeProp(pnode, "cva") argidx = vg_path.getNodeProp(pnode, "argidx") if callva != None: aval, amagic = argv[argidx] arepr = "0x%.8x" % aval if amagic != None: arepr = repr(amagic) frepr = "UNKNOWN" if fva != None: frepr = "0x%.8x" % fva self.vprint("func: %s calls at: 0x%.8x with his own: %s" % (frepr, callva, arepr)) self.vprint("=" * 80) def do_chat(self, line):
def _nodeedge(tnode): nid = vg_pathcore.getNodeProp(tnode, 'nid') eid = vg_pathcore.getNodeProp(tnode, 'eid') return nid, eid
def walkSymbolikPaths(self, fva, graph=None, maxpath=1000, loopcnt=0): ''' walkSymbolikPaths is a function-focused symbolik path generator, using the walkCodePaths generator foundation. Symbolik effects are dragged through each code block, and constraints are evaluated in-process to determine and trim dead code paths. Begins first node by applying self.preeffects and self.preconstraints ''' if graph is None: graph = self.getSymbolikGraph(fva) # our callback routine for code path walking 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 for pathnode in viv_graph.walkCodePaths(graph, codewalker, loopcnt=loopcnt, maxpath=maxpath): emu = vg_pathcore.getNodeProp(pathnode, 'pathemu') patheffs = vg_pathcore.getNodeProp(pathnode, 'patheffs') yield emu, patheffs
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 walkSymbolikPaths(self, fva, maxpath=1000): ''' walkSymbolikPaths is a function-focused symbolik path generator, using the walkCodePaths generator foundation. Symbolik effects are dragged through each code block, and constraints are evaluated in-process to determine and trim dead code paths. Begins first node by applying self.preeffects and self.preconstraints ''' graph = self.getSymbolikGraph(fva) # our callback routine for code path walking 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 for pathnode in viv_graph.walkCodePaths(graph, codewalker, maxpath=maxpath): emu = vg_pathcore.getNodeProp(pathnode, 'pathemu') patheffs = vg_pathcore.getNodeProp(pathnode, 'patheffs') yield emu, patheffs
def _nodeedgeloop(tnode): nid = vg_pathcore.getNodeProp(tnode, 'nid') eid = vg_pathcore.getNodeProp(tnode, 'eid') loop = vg_pathcore.getNodeProp(tnode, 'loops') return nid, eid, loop
def _nodeedge(tnode): nid = vg_pathcore.getNodeProp(tnode, 'nid') eid = vg_pathcore.getNodeProp(tnode, 'eid') return nid,eid
except Exception, e: self.vprint("Invalid Index Expression: %s" % argv[1]) return if self.getFunction(fva) != fva: self.vprint("Invalid Function Address: (0x%.8x) %s" % (fva, line)) for pleaf in viv_vector.trackArgOrigin(self, fva, idx): self.vprint('='*80) path = vg_path.getPathToNode(pleaf) path.reverse() for pnode in path: fva = vg_path.getNodeProp(pnode, 'fva') argv = vg_path.getNodeProp(pnode, 'argv') callva = vg_path.getNodeProp(pnode, 'cva') argidx = vg_path.getNodeProp(pnode, 'argidx') if callva != None: aval, amagic = argv[argidx] arepr = '0x%.8x' % aval if amagic != None: arepr = repr(amagic) frepr = 'UNKNOWN' if fva != None: frepr = '0x%.8x' % fva self.vprint('func: %s calls at: 0x%.8x with his own: %s' % (frepr, callva, arepr)) self.vprint("="*80) def do_chat(self, line):
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 getCodePaths(vw, fromva, tova, trim=True): """ Return a list of paths, where each path is a list of code blocks from fromva to tova. Usage: getCodePaths(vw, <fromva>, <tova>) -> [ [frblock, ..., toblock], ...] NOTE: "trim" causes an optimization which may not reveal *all* the paths, but is much faster to run. It will never return no paths when there are some, but may not return all of them... (based on path overlap) """ done = {} res = [] frcb = vw.getCodeBlock(fromva) tocb = vw.getCodeBlock(tova) if frcb == None: raise viv_exc.InvalidLocation(fromva) if tocb == None: raise viv_exc.InvalidLocation(tova) frva = frcb[0] # For compare speed root = vg_path.newPathNode(cb=tocb, cbva=tocb[0]) todo = [ root, ] done[tova] = tocb cbcache = {} while len(todo): path = todo.pop() cbva = vg_path.getNodeProp(path, 'cbva') codeblocks = cbcache.get(cbva) if codeblocks == None: codeblocks = getCodeFlow(vw, cbva) cbcache[cbva] = codeblocks for cblock in codeblocks: bva, bsize, bfva = cblock # Don't follow loops... if vg_path.isPathLoop(path, 'cbva', bva): continue # If we have been here before and it's *not* the answer, # skip out... if trim and done.get(bva) != None: continue done[bva] = cblock newpath = vg_path.newPathNode(parent=path, cb=cblock, cbva=bva) # If this one is a match, we don't need to # track past it. Also, put it in the results list # so we don't have to do it later.... if bva == frva: res.append(newpath) else: todo.append(newpath) # Now... if we have some results, lets build the block list. ret = [] for cpath in res: fullpath = vg_path.getPathToNode(cpath) # We actually do it by inbound references, so reverse the result! fullpath.reverse() ret.append([vg_path.getNodeProp(path, 'cb') for path in fullpath]) return ret
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.