Example #1
0
        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
Example #2
0
        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
Example #3
0
    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)
Example #4
0
    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)
Example #6
0
 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)
Example #7
0
 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)
Example #8
0
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())
Example #9
0
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
Example #10
0
    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)
Example #11
0
    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)
Example #12
0
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
Example #13
0
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)
Example #14
0
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())
Example #15
0
    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)
Example #16
0
    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)
Example #17
0
    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)
Example #18
0
    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)
Example #19
0
    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.
Example #20
0
    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)
Example #21
0
    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.
Example #22
0
 def getPathProp(self, key):
     '''
     Retrieve a named value from the current code path context.
     '''
     return vg_path.getNodeProp(self.curpath, key)
Example #23
0
    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)
Example #24
0
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
Example #25
0
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
Example #26
0
        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):
Example #27
0
def _nodeedge(tnode):
    nid = vg_pathcore.getNodeProp(tnode, 'nid')
    eid = vg_pathcore.getNodeProp(tnode, 'eid')
    return nid, eid
Example #28
0
    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
Example #29
0
    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.
Example #30
0
    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
Example #31
0
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
Example #32
0
def _nodeedge(tnode):
    nid = vg_pathcore.getNodeProp(tnode, 'nid')
    eid = vg_pathcore.getNodeProp(tnode, 'eid')
    return nid,eid
Example #33
0
 def getPathProp(self, key):
     '''
     Retrieve a named value from the current code path context.
     '''
     return vg_path.getNodeProp(self.curpath, key)
Example #34
0
        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):
Example #35
0
    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
Example #37
0
    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.