PK 2‰¦HƒTMlA lA viv_utils/emulator_drivers.pyimport logging import vivisect import envi as v_envi import envi.memory as v_mem import visgraph.pathcore as vg_path from . import LoggingObject class StopEmulation(Exception): pass class BreakpointHit(Exception): pass class UnsupportedFunction(Exception): pass class InstructionRangeExceededError(Exception): def __init__(self, pc): super(InstructionRangeExceededError, self).__init__() self.pc = pc def __str__(self): return "InstructionRangeExceededError(ended at instruction 0x%08X)" % self.pc class Hook(LoggingObject): def __init__(self): super(Hook, self).__init__() def hook(self, callname, emu, callconv, api, argv): # must return something other than None if handled # raise UnsupportedFunction to pass raise UnsupportedFunction() class Monitor(vivisect.impemu.monitor.EmulationMonitor, LoggingObject): def __init__(self, vw): vivisect.impemu.monitor.EmulationMonitor.__init__(self) LoggingObject.__init__(self) self._vw = vw self._logger = logging.getLogger("Monitor") def getStackValue(self, emu, offset): return emu.readMemoryFormat(emu.getStackCounter() + offset, "
0 callconv.execCallReturn(emu, 0, len(funcargs)) emu.setProgramCounter(pc + len(op)) return False elif not avoid_calls: if emu.probeMemory(endpc, 0x1, v_mem.MM_EXEC): # this is executable memory, so we're good # op already emulated, just return return True else: # this is some unknown region of memory, try to return callconv.execCallReturn(emu, 0, len(funcargs)) emu.setProgramCounter(pc + len(op)) return False class DebuggerEmulatorDriver(EmulatorDriver): """ this is a EmulatorDriver that supports debugger-like operations, such as stepi, stepo, call, etc. """ def __init__(self, emu): super(DebuggerEmulatorDriver, self).__init__(emu) self._bps = set([]) def step(self, avoid_calls): emu = self._emu startpc = emu.getProgramCounter() op = emu.parseOpcode(startpc) for mon in self._monitors: mon.prehook(emu, op, startpc) if self.isCall(op): self.handleCall(startpc, op, avoid_calls=avoid_calls) else: emu.executeOpcode(op) endpc = emu.getProgramCounter() for mon in self._monitors: mon.posthook(emu, op, endpc) def stepo(self): return self.step(True) def stepi(self): return self.step(False) def runToCall(self, max_instruction_count=1000): """ stepi until ret instruction """ emu = self._emu for _ in xrange(max_instruction_count): pc = emu.getProgramCounter() if pc in self._bps: raise BreakpointHit() op = emu.parseOpcode(pc) if self.isCall(op): return else: self.stepi() raise InstructionRangeExceededError(pc) def runToReturn(self, max_instruction_count=1000): """ stepo until ret instruction """ emu = self._emu for _ in xrange(max_instruction_count): pc = emu.getProgramCounter() if pc in self._bps: raise BreakpointHit() op = emu.parseOpcode(pc) if self.isRet(op): return else: self.stepo() raise InstructionRangeExceededError(pc) def runToVa(self, va, max_instruction_count=1000): """ stepi until ret instruction """ emu = self._emu for _ in xrange(max_instruction_count): pc = emu.getProgramCounter() if pc in self._bps: raise BreakpointHit() if pc == va: return else: self.stepi() raise InstructionRangeExceededError(pc) def addBreakpoint(self, va): self._bps.add(va) def removeBreakpoint(self, va): self._bps.remove(va) def getBreakpoints(self): return list(self._bps) class FunctionRunnerEmulatorDriver(EmulatorDriver): """ this is a EmulatorDriver that supports emulating all the instructions in a function. it explores all code paths by taking both branches. the .runFunction() implementation is essentially the same as emu.runFunction() """ def __init__(self, emu): super(FunctionRunnerEmulatorDriver, self).__init__(emu) self.path = self.newCodePathNode() self.curpath = self.path def newCodePathNode(self, parent=None, bva=None): ''' NOTE: Right now, this is only called from the actual branch state which needs it. it must stay that way for now (register context is being copied for symbolic emulator...) ''' props = { 'bva':bva, # the entry virtual address for this branch 'valist':[], # the virtual addresses in this node in order 'calllog':[], # FIXME is this even used? 'readlog':[], # a log of all memory reads from this block 'writelog':[],# a log of all memory writes from this block } return vg_path.newPathNode(parent=parent, **props) def _runFunction(self, funcva, stopva=None, maxhit=None, maxloop=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 = {} todo = [(funcva, self.getEmuSnap(), self.path), ] emu = self._emu vw = self._emu.vw # Save a dereference many many times depth = 0 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 # Check straight hit count... if maxhit != None: h = hits.get(startpc, 0) h += 1 if h > maxhit: break hits[startpc] = h # 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) nextpc = startpc + len(op) self.op = op for mon in self._monitors: mon.prehook(emu, op, startpc) iscall = bool(op.iflags & v_envi.IF_CALL) if iscall: wentInto = self.handleCall(startpc, op, avoid_calls=func_only) if wentInto: depth += 1 else: emu.executeOpcode(op) vg_path.getNodeProp(self.curpath, 'valist').append(startpc) endpc = emu.getProgramCounter() for mon in self._monitors: mon.posthook(emu, op, endpc) if not iscall: # If it wasn't a call, check for branches, if so, add them to # the todo list and go around again... blist = emu.checkBranches(startpc, endpc, op) if len(blist) > 0: # pc in the snap will be wrong, but over-ridden at restore esnap = self.getEmuSnap() for bva, bpath in blist: todo.append((bva, esnap, bpath)) break if op.iflags & v_envi.IF_RET: vg_path.setNodeProp(self.curpath, 'cleanret', True) if depth == 0: break else: depth -= 1 # If we enounter a procedure exit, it doesn't # matter what PC is, we're done here. except v_envi.UnsupportedInstruction as e: if strictops: break else: self._logger.debug('runFunction continuing after unsupported instruction: 0x%08x %s', e.op.va, e.op.mnem) emu.setProgramCounter(e.op.va + e.op.size) except Exception as e: self._logger.warning("error during emulation of function: %s", e)#, exc_info=True) for mon in self._monitors: mon.logAnomaly(emu, startpc, str(e)) break # If we exc during execution, this branch is dead. def runFunction(self, funcva, stopva=None, maxhit=None, maxloop=None, strictops=True, func_only=True): try: self._runFunction(funcva, stopva, maxhit, maxloop, strictops, func_only) except StopEmulation: return PK Ùˆ¦HqêgTU U viv_utils/__init__.pyimport os import logging import inspect import funcy import vivisect import intervaltree def getVwSampleMd5(vw): return vw.filemeta.values()[0]["md5sum"] def getWorkspace(fp, reanalyze=False, verbose=False): ''' For a file path return a workspace, it will create one if the extension is not .viv, otherwise it will load the existing one. Reanalyze will cause it to create and save a new one. ''' vw = vivisect.VivWorkspace() vw.verbose = verbose vw.config.viv.parsers.pe.nx = True if fp.endswith('.viv'): vw.loadWorkspace(fp) if reanalyze: vw.analyze() vw.saveWorkspace() else: if os.path.exists(fp + ".viv"): vw.loadWorkspace(fp + ".viv") if reanalyze: vw.analyze() vw.saveWorkspace() else: vw.loadFromFile(fp) vw.analyze() vw.saveWorkspace() return vw class LoggingObject(object): def __init__(self): self._logger = logging.getLogger("{:s}.{:s}".format( self.__module__, self.__class__.__name__)) def _getCallerFunction(self): FUNCTION_NAME_INDEX = 3 return inspect.stack()[3][FUNCTION_NAME_INDEX] def _formatFormatString(self, args): return [self._getCallerFunction() + ": " + args[0]] + [a for a in args[1:]] def d(self, *args, **kwargs): if self._logger.isEnabledFor(logging.DEBUG): self._logger.debug(*self._formatFormatString(args), **kwargs) def i(self, *args, **kwargs): if self._logger.isEnabledFor(logging.INFO): self._logger.info(*self._formatFormatString(args), **kwargs) def w(self, *args, **kwargs): if self._logger.isEnabledFor(logging.WARN): self._logger.warn(*self._formatFormatString(args), **kwargs) def e(self, *args, **kwargs): if self._logger.isEnabledFor(logging.ERROR): self._logger.error(*self._formatFormatString(args), **kwargs) def set_function_name(vw, va, new_name): # vivgui seems to override function_name with symbol names, but this is correct ret_type, ret_name, call_conv, func_name, args = vw.getFunctionApi(va) vw.setFunctionApi(va, (ret_type, ret_name, call_conv, new_name, args)) def get_function_name(vw, va): ret_type, ret_name, call_conv, func_name, args = vw.getFunctionApi(va) return func_name class Function(LoggingObject): def __init__(self, vw, va): super(Function, self).__init__() self.vw = vw self.va = va @funcy.cached_property def basic_blocks(self): bb = map(lambda b: BasicBlock(self.vw, *b), self.vw.getFunctionBlocks(self.va)) return sorted(bb, key=lambda b: b.va) @funcy.cached_property def id(self): return self.vw.filemeta.values()[0]["md5sum"] + ":" + hex(self.va) def __repr__(self): return "Function(va: {:s})".format(hex(self.va)) @property def name(self): return get_function_name(self.vw, self.va) @name.setter def name(self, new_name): return set_function_name(self.vw, self.va, new_name) class BasicBlock(LoggingObject): def __init__(self, vw, va, size, fva): super(BasicBlock, self).__init__() self.vw = vw self.va = va self.size = size self.fva = fva @funcy.cached_property def instructions(self): """ from envi/__init__.py:class Opcode 391 opcode - An architecture specific numerical value for the opcode 392 mnem - A humon readable mnemonic for the opcode 393 prefixes - a bitmask of architecture specific instruction prefixes 394 size - The size of the opcode in bytes 395 operands - A list of Operand objects for this opcode 396 iflags - A list of Envi (architecture independant) instruction flags (see IF_FOO) 397 va - The virtual address the instruction lives at (used for PC relative im mediates etc...) """ ret = [] va = self.va while va < self.va + self.size: o = self.vw.parseOpcode(va) ret.append(o) va += len(o) return ret def __repr__(self): return "BasicBlock(va: {:s}, size: {:s}, fva: {:s})".format( hex(self.va), hex(self.size), hex(self.fva)) def one(s): for i in s: return i class InstructionFunctionIndex(LoggingObject): """ Index from VA to containing function VA """ def __init__(self, vw): super(InstructionFunctionIndex, self).__init__() self.vw = vw self._index = intervaltree.IntervalTree() self._do_index() def _do_index(self): for funcva in self.vw.getFunctions(): f = Function(self.vw, funcva) for bb in f.basic_blocks: self._index[bb.va:bb.va + bb.size] = funcva def __getitem__(self, key): v = one(self._index[key]) if v is None: raise KeyError() return v.data def getFunctionName(vw, fva): ret_type, ret_name, call_conv, func_name, args = vw.getFunctionApi(fva) return func_name def getFunctionCallingConvention(vw, fva): ret_type, ret_name, call_conv, func_name, args = vw.getFunctionApi(fva) return call_conv def getFunctionArgs(vw, fva): return vw.getFunctionArgs(fva) PK ‰¦H“SÃ0 0 ) viv_utils-0.3.0.dist-info/DESCRIPTION.rstUtilities for binary analysis using vivisect. PK ‰¦H'LÞ˜› › * viv_utils-0.3.0.dist-info/entry_points.txt[console_scripts] get_function_args = viv_utils.scripts.get_function_args:main trace_function_emulation = viv_utils.scripts.trace_function_emulation:main PK ‰¦HéãA¬ ¬ ' viv_utils-0.3.0.dist-info/metadata.json{"classifiers": ["Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "Natural Language :: English", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7"], "extensions": {"python.commands": {"wrap_console": {"get_function_args": "viv_utils.scripts.get_function_args:main", "trace_function_emulation": "viv_utils.scripts.trace_function_emulation:main"}}, "python.details": {"contacts": [{"email": "william.ballenthin@mandiant.com", "name": "Willi Ballenthin", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.mandiant.com/wballenthin/viv-utils"}}, "python.exports": {"console_scripts": {"get_function_args": "viv_utils.scripts.get_function_args:main", "trace_function_emulation": "viv_utils.scripts.trace_function_emulation:main"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["viv_utils"], "metadata_version": "2.0", "name": "viv-utils", "run_requires": [{"requires": ["argparse", "funcy", "intervaltree", "ipython", "pefile", "vivisect"]}], "summary": "Utilities for binary analysis using vivisect.", "version": "0.3.0"}PK ‰¦H„8&ã ' viv_utils-0.3.0.dist-info/top_level.txtviv_utils PK ‰¦Hìndªn n viv_utils-0.3.0.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any PK ‰¦HÆú¤þ þ " viv_utils-0.3.0.dist-info/METADATAMetadata-Version: 2.0 Name: viv-utils Version: 0.3.0 Summary: Utilities for binary analysis using vivisect. Home-page: https://github.mandiant.com/wballenthin/viv-utils Author: Willi Ballenthin Author-email: william.ballenthin@mandiant.com License: UNKNOWN Keywords: viv_utils Platform: UNKNOWN Classifier: Development Status :: 2 - Pre-Alpha Classifier: Intended Audience :: Developers Classifier: Natural Language :: English Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Requires-Dist: argparse Requires-Dist: funcy Requires-Dist: intervaltree Requires-Dist: ipython Requires-Dist: pefile Requires-Dist: vivisect Utilities for binary analysis using vivisect. PK ‰¦Hg°ÃÈ viv_utils-0.3.0.dist-info/RECORDviv_utils/__init__.py,sha256=PyldxafZ-fEGNrYUL3QvQhgIqX1jenBpExj3er4JgMc,5461 viv_utils/emulator_drivers.py,sha256=DaYGb4_8Iw-D3cYuNr5UNch12ce7ADoe9txRNRJxa8c,16748 viv_utils-0.3.0.dist-info/DESCRIPTION.rst,sha256=txb8JnZpYBCLDXrdXxUOVYY7LEcQHpRuTgeBp3I7gNA,48 viv_utils-0.3.0.dist-info/METADATA,sha256=Zp1opsHtF_jDchsf2eO3dg7gfeL5RehOhS6-m1-gKik,766 viv_utils-0.3.0.dist-info/RECORD,, viv_utils-0.3.0.dist-info/WHEEL,sha256=GrqQvamwgBV4nLoJe0vhYRSWzWsx7xjlt74FT0SWYfE,110 viv_utils-0.3.0.dist-info/entry_points.txt,sha256=TqGKib4jqzvP-_iUbaSWJ01WAeraNA1G5x3y7lyenVI,155 viv_utils-0.3.0.dist-info/metadata.json,sha256=Pynj7N0rqKgZC5YHdrEf9tCePNVgIqz-RI7Mv_JR4NU,1196 viv_utils-0.3.0.dist-info/top_level.txt,sha256=rScNCO9TELgbTnjp502fd5rN31XoL7jyGNAV2-1ppQ8,10 PK 2‰¦HƒTMlA lA viv_utils/emulator_drivers.pyPK Ùˆ¦HqêgTU U §A viv_utils/__init__.pyPK ‰¦H“SÃ0 0 ) /W viv_utils-0.3.0.dist-info/DESCRIPTION.rstPK ‰¦H'LÞ˜› › * ¦W viv_utils-0.3.0.dist-info/entry_points.txtPK ‰¦HéãA¬ ¬ ' ‰X viv_utils-0.3.0.dist-info/metadata.jsonPK ‰¦H„8&ã ' z] viv_utils-0.3.0.dist-info/top_level.txtPK ‰¦Hìndªn n É] viv_utils-0.3.0.dist-info/WHEELPK ‰¦HÆú¤þ þ " t^ viv_utils-0.3.0.dist-info/METADATAPK ‰¦Hg°ÃÈ ²a viv_utils-0.3.0.dist-info/RECORDPK Ò òd