def test_msgpack_idempotent(self): # test that what we put in, we can get out vw = vivisect.VivWorkspace() vw.setMeta('StorageName', self.tmpf.name) vw.setMeta('StorageModule', 'vivisect.storage.mpfile') add_events(vw) vw.saveWorkspace() self.tmpf.flush() ovw = vivisect.VivWorkspace() ovw.setMeta('StorageModule', 'vivisect.storage.mpfile') # So this is a bit naughty, but just the act of creating a workspace # induces some events in to the workspace. Nothing crazy, just some va sets # so delete those so we can have a clean comparison ovw._event_list = [] ovw.loadWorkspace(self.tmpf.name) old = list(vw.exportWorkspace()) new = list(ovw.exportWorkspace()) self.assertEqual(len(old), 35) self.assertEqual( len(new), 36) # the last event is a setMeta made by loadWorkspace self.assertEqual(new[-1], (VWE_SETMETA, ('StorageName', self.tmpf.name))) for idx in range(len(old)): self.assertEqual(old[idx], new[idx])
def setUpClass(cls): testpath = helpers.getTestPath('windows', 'i386', 'malware.zip') cls.embed_vw = vivisect.VivWorkspace() cls.mmap_vw = vivisect.VivWorkspace() with zipfile.ZipFile(testpath, mode='r') as zp: fd = io.BytesIO(zp.open('mal_memmaps.exe', pwd=b'infected', mode='r').read()) # DEV: intentionally skipping analysis for now since the tests I have in mind # initially don't need it (and it adds a lot of time that isn't worth it until we # add a lot more tests) cls.mmap_vw.loadFromFd(fd) # DEV: This one needs analysis, but luckily only takes ~2 seconds to analyze fd = io.BytesIO(zp.open('mal_carve.exe', pwd=b'infected', mode='r').read()) cls.embed_vw.loadFromFd(fd) cls.embed_vw.analyze()
def test_envi_i386_disasm_Specific_MultiByte_Instrs(self): ''' pick 10 arbitrary 2- and 3-byte operands ''' vw = vivisect.VivWorkspace() scanv = e_memcanvas.StringMemoryCanvas(vw) for name, bytez, va, reprOp, renderOp in i386MultiByteOpcodes: try: op = self._arch.archParseOpcode(bytez.decode('hex'), 0, va) except envi.InvalidInstruction: self.fail( "Failed to parse opcode bytes: %s (case: %s, expected: %s)" % (bytez, name, reprOp)) # print("'%s', 0x%x, '%s' == '%s'" % (bytez, va, repr(op), reprOp)) try: self.assertEqual(repr(op), reprOp) except AssertionError: self.fail("Failing match for case %s (%s != %s)" % (name, repr(op), reprOp)) scanv.clearCanvas() op.render(scanv) # print("render: %s" % repr(scanv.strval)) self.assertEqual(scanv.strval, renderOp)
def main(): vw = vivisect.VivWorkspace() vw.loadWorkspace(sys.argv[1]) print '# %s' % sys.argv[1] fnames = {} for fva, etype, ename, fname in vw.getExports(): enamekey = ename.lower() fnamekey = fname.lower() fnames[fname] = True # Skip past forwarders if not vw.isFunction(fva): continue #argv = tuple([ (type_lookup.get(t.__name__, t.__name__), name_lookup.get(t.__name__)) for t,name in vw.getFunctionArgs(fva) ]) #rtype = vw.getFunctionMeta(fva, 'ReturnType', 'int') #ccname = vw.getFunctionMeta(fva, 'CallingConvention') print " '%s.%s':( %r, None, %r, '%s.%s', %r )," % ( fnamekey, enamekey, rtype, ccname, fname, ename, argv) for fwdfname in fnames.keys(): for rva, name, fwdname in vw.getFileMeta(fwdfname, 'forwarders', ()): fwdapi = vw.getImpApi(fwdname) if not fwdapi: print(' # FIXME unresolved %s -> %s' % (name, fwdname)) continue print(" '%s.%s':%r," % (fwdfname.lower(), name.lower(), fwdapi))
def NEWPtest_setSymbolikArgs(self): ''' tests setting and then getting the args for a function. we manually smash in the function args for now *and* hardcode the test to a func in 64-bit pre-run win32k.sys.viv workspace. ''' vw = vivisect.VivWorkspace() vw.loadWorkspace('win32k.sys.viv') fva = 0xfffff97fff1706a0 args = [(v_uint64, None), ] * 6 vw.setFunctionArgs(fva, args) ctx = sym_amd64.Amd64SymbolikAnalysisContext(vw) emu = ctx.getFunctionEmulator(fva) cc = sym_amd64.MSx64CallSym() argc = len(args) argv = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06] cc.setSymbolikArgs(emu, argv) sargs = cc.getSymbolikArgs(emu, argv, update=False) for idx, arg in enumerate(sargs): val = arg.update(emu) if isinstance(arg, Mem): val = emu.readSymMemory(arg, 8) arg = arg.update(emu) arg = arg.reduce(emu) assert(len(sargs) == argc)
def check_opreprs(self, opcodes): vw = vivisect.VivWorkspace() scanv = e_memcanvas.StringMemoryCanvas(vw) for name, bytez, va, reprOp, renderOp in opcodes: try: op = self._arch.archParseOpcode(bytez.decode('hex'), 0, va) except envi.InvalidInstruction: self.fail( "Failed to parse opcode bytes: %s (case: %s, expected: %s)" % (bytez, name, reprOp)) except Exception as e: self.fail( "Failed to parse opcode bytes: %s (case: %s, expected: %s)" % (bytez, name, reprOp)) # print("'%s', 0x%x, '%s' == '%s'" % (bytez, va, repr(op), reprOp)) try: self.assertEqual(repr(op), reprOp) except AssertionError: self.fail("Failing match for case %s (%s != %s)" % (name, repr(op), reprOp)) scanv.clearCanvas() op.render(scanv) # print("render: %s" % repr(scanv.strval)) self.assertEqual(scanv.strval, renderOp)
def check_opreprs(self, opcodes): vw = vivisect.VivWorkspace() scanv = e_memcanvas.StringMemoryCanvas(vw) for name, bytez, va, reprOp, renderOp in opcodes: try: op = self._arch.archParseOpcode(binascii.unhexlify(bytez), 0, va) except envi.InvalidInstruction: self.fail( "Failed to parse opcode bytes: %s (case: %s, expected: %s)" % (bytez, name, reprOp)) except Exception: self.fail( "Failed to parse opcode bytes: %s (case: %s, expected: %s)" % (bytez, name, reprOp)) msg = '%s failed length check. Got %d, expected %d' % ( name, len(op), int(len(bytez) / 2)) self.assertEqual(len(op), int(len(bytez) / 2), msg=msg) # print("'%s', 0x%x, '%s' == '%s'" % (bytez, va, repr(op), reprOp)) try: self.assertEqual(repr(op), reprOp) except AssertionError: self.fail("Failing match for case %s (%s != %s)" % (name, repr(op), reprOp)) scanv.clearCanvas() op.render(scanv) self.assertEqual(scanv.strval, renderOp)
def test_wipeAstArch_wipeva(self): vw = viv.VivWorkspace() vw.addMemoryMap(0x410000, e_mem.MM_RWX, 'code', [0 for x in range(0x10000)]) vw.addLocation(0x41b2ac, 47, viv_const.LOC_POINTER) vw.addLocation(0x4149b3, 28, viv_const.LOC_POINTER) vw.addLocation(0x41ac93, 83, viv_const.LOC_POINTER) vw.setMeta('Architecture', 'i386') symctx = vsym_analysis.SymbolikAnalysisContext(vw) cons = vsc.Const(0x41b2ac, 4) func1 = vsc.Call(cons, 4, argsyms=[vsc.Var('edx', 4)]) func2 = vsc.Call( vsc.Const(0x4149b3, 4), 4, argsyms=[vsc.Const(0, 4), vsc.Const(1, 4), vsc.Const(0x41b2ac, 4)]) func3 = vsc.Call(vsc.Const(0x41ac93, 4), 4, argsyms=[vsc.Const(0, 4), vsc.Var('ecx', 4)]) wiped = vsym_archind.wipeAstArch(symctx, [func1 + func2, func3], wipeva=True) self.assertEqual(2, len(wiped)) self.assertEqual('(2archindva(1indreg) + 1archindva(0,1,2archindva))', str(wiped[0])) self.assertEqual('0archindva(0,0indreg)', str(wiped[1]))
def getTestWorkspace(self, fname, analyze=True): fpath = os.path.join('vivisect', 'bins', fname) vw = vivisect.VivWorkspace() vw.loadFromFile(fpath) if analyze: vw.analyze() return vw
def getWorkspace(fp, reanalyze=False, verbose=False, should_save=True): ''' 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 # this is pretty insance, but simply prop assignment doesn't work. vw.config.getSubConfig('viv').getSubConfig('parsers').getSubConfig('pe')['loadresources'] = True vw.config.getSubConfig('viv').getSubConfig('parsers').getSubConfig('pe')['nx'] = True if fp.endswith('.viv'): vw.loadWorkspace(fp) if reanalyze: vw.analyze() else: if os.path.exists(fp + ".viv"): vw.loadWorkspace(fp + ".viv") if reanalyze: vw.analyze() else: vw.loadFromFile(fp) vw.analyze() if should_save: vw.saveWorkspace() return vw
def getShellcodeWorkspace(buf, arch="i386", base=0, entry_point=0, should_save=False, save_path=None): """ Load shellcode into memory object and generate vivisect workspace. Thanks to Tom for most of the code. :param buf: shellcode buffer bytes :param arch: architecture string :param base: base address where shellcode will be loaded :param entry_point: entry point of shellcode, relative to base :param should_save: save workspace to disk :param save_path: path to save workspace to :return: vivisect workspace """ vw = vivisect.VivWorkspace() vw.setMeta('Architecture', arch) vw.setMeta('Platform', 'windows') vw.setMeta('Format', 'pe') # blob gives weaker results in some cases vw._snapInAnalysisModules() vw.addMemoryMap(base, envi.memory.MM_RWX, 'shellcode', buf) vw.addSegment(base, len(buf), 'shellcode_0x%x' % base, 'blob') vw.addEntryPoint(base + entry_point) # defaults to start of shellcode vw.analyze() if should_save: if save_path is None: raise Exception("Failed to save workspace, destination save path cannot be empty") vw.setMeta("StorageName", "%s.viv" % save_path) vw.saveWorkspace() return vw
def test_read_struct(self): vw = vivisect.VivWorkspace() src = 'struct foo { int bar; char baz; unsigned int biz[40]; }; // test struct' vw.setUserStructSource(src) usrc = vw.getUserStructSource('foo') self.assertEqual(1, len(vw.getUserStructNames())) self.assertEqual(usrc, src)
def getTestWorkspace(*paths): testdir = os.getenv('VIVTESTFILES') if not testdir: raise unittest.SkipTest('VIVTESTFILES env var not found!') fpath = os.path.join(testdir, *paths) vw = vivisect.VivWorkspace() vw.loadFromFile(fpath) vw.analyze() return vw
def test_msgpack_to_basicfile(self): # make sure we're on par with what the OG storage mechanism can do mpfile = tempfile.NamedTemporaryFile(delete=False) basicfile = tempfile.NamedTemporaryFile(delete=False) try: ogvw = vivisect.VivWorkspace() add_events(ogvw) ogvw.setMeta('StorageName', mpfile.name) ogvw.setMeta('StorageModule', 'vivisect.storage.mpfile') ogvw.saveWorkspace() # Get rid of those last two meta sets so that the two new workspaces should be # the same save for the last meta set ogvw._event_list.pop() ogvw._event_list.pop() ogvw.setMeta('StorageName', basicfile.name) ogvw.setMeta('StorageModule', 'vivisect.storage.basicfile') ogvw.saveWorkspace() ogevt = list(ogvw.exportWorkspace()) mvw = vivisect.VivWorkspace() mvw.setMeta('StorageModule', 'vivisect.storage.mpfile') mvw._event_list = [] mvw.loadWorkspace(mpfile.name) mevt = list(mvw.exportWorkspace()) self.assertEqual(len(mevt), 36) bvw = vivisect.VivWorkspace() bvw.setMeta('StorageModule', 'vivisect.storage.basicfile') bvw._event_list = [] bvw.loadWorkspace(basicfile.name) bevt = list(bvw.exportWorkspace()) self.assertEqual(len(bevt), 36) # the last three events are specific to the different storage modules for idx in range(len(mevt) - 3): self.assertEqual(mevt[idx], bevt[idx]) self.assertEqual(ogevt[idx], bevt[idx]) finally: mpfile.close() basicfile.close() os.unlink(mpfile.name) os.unlink(basicfile.name)
def loadGoBin(*path): ''' We don't need full analysis on the go binaries to complete the tests ''' vw = vivisect.VivWorkspace() path = helpers.getTestPath(*path) vw.loadFromFile(path) for ep in vw.getEntryPoints(): vw.makeFunction(ep) return vw
def getWorkspaceFromFile(filepath, analyze=True): """ deserialize a file into a new vivisect workspace. """ vw = vivisect.VivWorkspace() vw.verbose = True vw.config.viv.parsers.pe.nx = True vw.loadFromFile(filepath) if analyze: vw.analyze() return vw
def test_bad_event(self): vw = vivisect.VivWorkspace() with self.assertLogs() as logcap: vw.importWorkspace([(VWE_MAX + 1, (0xabad1dea, 4, 3, 'nope')), (VWE_ADDFILE, ('VivisectFile', 0x1000, '3bfdad02b9a6522c84e356cf8f69135b'))]) files = vw.getFiles() self.assertIn("IndexError: list index out of range", ''.join(logcap.output)) self.assertEqual(1, len(files)) self.assertEqual('VivisectFile', files[0])
def test_viv_bigend(self): fd = StringIO('ABCDEFG') vw = vivisect.VivWorkspace() vw.config.viv.parsers.blob.arch = 'arm' vw.config.viv.parsers.blob.bigend = True vw.config.viv.parsers.blob.baseaddr = 0x22220000 vw.loadFromFd(fd, fmtname='blob') self.assertEqual(vw.castPointer(0x22220000), 0x41424344) self.assertEqual(vw.parseNumber(0x22220000, 2), 0x4142)
def setUpClass(cls): super(PETests, cls).setUpClass() cls.psexec_fn = helpers.getTestPath('windows', 'i386', 'PsExec.exe') cls.vw_psexec = helpers.getTestWorkspace('windows', 'i386', 'PsExec.exe') cls.vw_sphinx = helpers.getTestWorkspace('windows', 'i386', 'sphinx_livepretend.exe') cls.vw_mimi = vivisect.VivWorkspace() mimi_fn = helpers.getTestPath('windows', 'i386', 'mimikatz.exe_') cls.vw_mimi.loadFromFile(mimi_fn) # this binary is a little big (1MB) # and we only care about the delay import table # so, don't do a full analysis fn_471 = helpers.getTestPath( 'windows', 'i386', '471ce36855fec6b44398b9b1e3cfb9e74b122fb2cc20fdf6603ebda39f86dddf') cls.vw_471 = vivisect.VivWorkspace() vivisect.parsers.pe.parseFile(cls.vw_471, fn_471)
def setUpClass(cls): super(PETests, cls).setUpClass() cls.psexec_fn = helpers.getTestPath('windows', 'i386', 'PsExec.exe') cls.vw_psexec = helpers.getTestWorkspace('windows', 'i386', 'PsExec.exe') cls.vw_sphinx = helpers.getTestWorkspace('windows', 'i386', 'sphinx_livepretend.exe') cls.vw_mimi = vivisect.VivWorkspace() mimi_fn = helpers.getTestPath('windows', 'i386', 'mimikatz.exe_') cls.vw_mimi.loadFromFile(mimi_fn)
def runServer(name, port): dirn = os.path.dirname(name) testfile = helpers.getTestPath('windows', 'amd64', 'firefox.exe') # load the file in so we get some workspace events, but not so many to make # this test take forever vw = vivisect.VivWorkspace() vw.loadFromFile(testfile) vw.setMeta('StorageName', name) vw.saveWorkspace() v_r_server.runMainServer(dirn, port)
def getWorkspaceFromBytes(buf, analyze=True): """ create a new vivisect workspace and load it from a Python string/bytes. """ vw = vivisect.VivWorkspace() vw.verbose = True vw.config.viv.parsers.pe.nx = True loadWorkspaceFromBytes(vw, buf) if analyze: vw.analyze() return vw
def setUpClass(cls): cls.longMessage=True cls.CODE_VA = 0x4400 cls.DATA_VA = 0x1000 cls.MEMSIZE = 0xffff cls._vw = vivisect.VivWorkspace() cls._vw.setMeta('Architecture', 'msp430') cls._vw.setMeta('Platform', 'unknown') cls._vw.setMeta('Format', 'blob') cls._vw.addMemoryMap(0, 0x7, 'mem', b'\x00' * cls.MEMSIZE) cls._emu = cls._vw.getEmulator()
def loadWorkspace(filename, fast=False): import vivisect logger = getLogger('loadWorkspace') # haha - screw you ida! storing values in idaapi works - you cache yourselves but not me #cacheDict = getattr(sys.modules['idaapi'], 'vw_cached_workspace', None) cacheDict = None if filename is None: return None if cacheDict is None: cacheDict = {} logger.info('No vw cache present.') setattr(sys.modules['idaapi'], 'vw_cached_workspace', cacheDict) else: vw = cacheDict.get(filename) if vw is None: logger.info('Got cache dict, but workspace not present') else: logger.info('Got vw from cached global value!') return vw vw = vivisect.VivWorkspace() vivName = queryIdbNetnode(VIV_WORKSPACE_NAME) if vivName is None or not os.path.exists(vivName): vivName = filename + '.viv' if os.path.exists(vivName): logger.info('Loading existing workspace %s', vivName) sys.stdout.flush() vw.loadWorkspace(vivName) else: logger.info('Loading file into vivisect: %s', filename) sys.stdout.flush() vw.loadFromFile(filename) if not fast: logger.info( 'Performing vivisect analysis now. This may take some time...' ) logger.info('#' * 80) vw.analyze() logger.info('#' * 80) logger.info('Analysis done. Continuing now') vw.saveWorkspace() #store the .viv filepath in the IDB for later use setIdbNetnode(VIV_WORKSPACE_NAME, vw.getMeta("StorageName")) logger.info('Stored .viv workspace to: %s', queryIdbNetnode(VIV_WORKSPACE_NAME)) else: logger.info('Found .viv name from idb netnode. Loading %s', vivName) vw.loadWorkspace(vivName) logger.info('Caching vw workspace object in global variable now') cacheDict[filename] = vw return vw
def test_msgpack_to_basicfile(self): # make sure we're on par with what the OG storage mechanism can do with tempfile.NamedTemporaryFile() as mpfile: with tempfile.NamedTemporaryFile() as basicfile: ogvw = vivisect.VivWorkspace() add_events(ogvw) ogvw.setMeta('StorageName', mpfile.name) ogvw.setMeta('StorageModule', 'vivisect.storage.mpfile') ogvw.saveWorkspace() # Get rid of those last two meta sets so that the two new workspaces should be # the same save for the last meta set ogvw._event_list.pop() ogvw._event_list.pop() ogvw.setMeta('StorageName', basicfile.name) ogvw.setMeta('StorageModule', 'vivisect.storage.basicfile') ogvw.saveWorkspace() ogevt = ogvw.exportWorkspace() mvw = vivisect.VivWorkspace() mvw.setMeta('StorageModule', 'vivisect.storage.mpfile') mvw._event_list = [] mvw.loadWorkspace(mpfile.name) mevt = mvw.exportWorkspace() self.assertEqual(len(mevt), 36) bvw = vivisect.VivWorkspace() bvw.setMeta('StorageModule', 'vivisect.storage.basicfile') bvw._event_list = [] bvw.loadWorkspace(basicfile.name) bevt = bvw.exportWorkspace() self.assertEqual(len(bevt), 36) # the last three events are specific to the different storage modules for idx in range(len(mevt) - 3): self.assertEqual(normUnicode(mevt[idx]), normUnicode(bevt[idx])) self.assertEqual(normUnicode(ogevt[idx]), normUnicode(bevt[idx]))
def getWorkspaceFromBytes(buf, analyze=True): """ create a new vivisect workspace and load it from a Python string/bytes. """ vw = vivisect.VivWorkspace() vw.verbose = True vw.config.viv.parsers.pe.nx = True loadWorkspaceFromBytes(vw, buf) assertVwMatchesVivisectLibrary(vw) if analyze: setVwVivisectLibraryVersion(vw) vw.analyze() return vw
def SKIPtest_envi_amd64_disasm_Specific_VEX_Instrs(self): vw = vivisect.VivWorkspace() scanv = e_memcanvas.StringMemoryCanvas(vw) for name, bytez, va, reprOp, renderOp in amd64VexOpcodes: op = self._arch.archParseOpcode(bytez.decode('hex'), 0, va) # print("'%s', 0x%x, '%s' == '%s'" % (bytez, va, repr(op), reprOp)) self.assertEqual(repr(op), reprOp) scanv.clearCanvas() op.render(scanv) #print "render: %s" % repr(scanv.strval) self.assertEqual(scanv.strval, renderOp)
def test_make_struct(self): fd = io.BytesIO(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\xfc\xfd\xfe\xff') vw = vivisect.VivWorkspace() vw.config.viv.parsers.blob.arch = 'arm' vw.config.viv.parsers.blob.bigend = False vw.config.viv.parsers.blob.baseaddr = 0x22220000 vw.loadFromFd(fd, fmtname='blob') src = 'struct allint {int foo; int bar; int baz; int biz;};' vw.setUserStructSource(src) vs = vw.makeStructure(0x22220000, 'allint') self.assertEqual(vs.foo, 0x03020100) self.assertEqual(vs.bar, 0x07060504) self.assertEqual(vs.baz, 0x0b0a0908) self.assertEqual(vs.biz, 0xfffefdfc)
def test_wipeAstArch(self): vw = viv.VivWorkspace() vw.addMemoryMap(0x00, e_mem.MM_RWX, 'code', [0 for x in range(256)]) vw.addLocation(0x40, 47, viv_const.LOC_POINTER) vw.setMeta('Architecture', 'i386') symctx = vsym_analysis.SymbolikAnalysisContext(vw) func = vsc.Var('eax', 4) call = vsc.Call(func, 4) addr = vsc.Var('arg0', 4) + call mem = vsc.Mem(addr, vsc.Const(4, 4)) op = mem + vsc.Var('edx', 4) final = op * vsc.Var('edx', 4) wiped = vsym_archind.wipeAstArch(symctx, [final]) self.assertEquals(1, len(wiped)) self.assertEquals('((mem[(arg0 + 1indreg()):4] + 0indreg) * 0indreg)', str(wiped[0]))
def test_envi_amd64_disasm_Specific_MultiByte_Instrs(self): ''' pick 10 arbitrary 2- and 3-byte operands ''' vw = vivisect.VivWorkspace() scanv = e_memcanvas.StringMemoryCanvas(vw) for name, bytez, va, reprOp, renderOp in amd64MultiByteOpcodes: op = self._arch.archParseOpcode(bytez.decode('hex'), 0, va) #print "'%s', 0x%x, '%s' == '%s'" % (bytez, va, repr(op), reprOp) self.assertEqual( repr(op), reprOp ) scanv.clearCanvas() op.render(scanv) #print "render: %s" % repr(scanv.strval) self.assertEqual( scanv.strval, renderOp )