def getEmuAtVa(vw, va, maxhit=None): """ Build and run an emulator to the given virtual address from the function entry point. (most useful for state analysis. kinda heavy though...) """ fva = vw.getFunction(va) if fva == None: return None cbva,cbsize,cbfva = vw.getCodeBlock(va) fgraph = v_graphutil.buildFunctionGraph(vw, fva) # Just take the first one off the iterator... for path in v_graphutil.getCodePathsTo(fgraph, cbva): emu = vw.getEmulator() opcodes = v_graphutil.getOpsFromPath(vw, fgraph, path) for op in opcodes: if op.va == va: break emu.executeOpcode(op) return emu
def _hotkey_paintUp(self, va=None): """ Paint the VA's from the selected basic block up to all possible non-looping starting points. """ graph = viv_graphutil.buildFunctionGraph(self.vw, self.fva, revloop=True) startva = self.mem_canvas._canv_curva if startva == None: return viv_graphutil.preRouteGraphUp(graph, startva, mark="hit") count = 0 colormap = {} for node in graph.getNodesByProp("hit"): count += 1 off = 0 cbsize = node[1].get("cbsize") if cbsize == None: raise Exception("node has no cbsize: %s" % repr(node)) # step through opcode for a node while off < cbsize: op = self.vw.parseOpcode(node[0] + off) colormap[op.va] = "orange" off += len(op) self.vw.vprint("Colored Blocks: %d" % count) vqtevent("viv:colormap", colormap) return colormap
def _hotkey_paintMerge(self, va=None): """ same as paintdown but only until the graph remerges """ graph = viv_graphutil.buildFunctionGraph(self.vw, self.fva, revloop=True) startva = self.mem_canvas._canv_curva if startva == None: return viv_graphutil.findRemergeDown(graph, startva) count = 0 colormap = {} for node in graph.getNodesByProp("hit"): count += 1 off = 0 cbsize = node[1].get("cbsize") if cbsize == None: raise Exception("node has no cbsize: %s" % repr(node)) # step through opcode for a node while off < cbsize: op = self.vw.parseOpcode(node[0] + off) colormap[op.va] = "brown" off += len(op) self.vw.vprint("Colored Blocks: %d" % count) vqtevent("viv:colormap", colormap) return colormap
def _hotkey_paintDown(self, va=None): """ Paint the VA's from the selected basic block down to all possible non-looping blocks. This is valuable for determining what code can execute from any starting basic block, without a loop. """ # TODO: make overlapping colors available for multiple paintings graph = viv_graphutil.buildFunctionGraph(self.vw, self.fva, revloop=True) startva = self.mem_canvas._canv_curva if startva == None: return viv_graphutil.preRouteGraphDown(graph, startva, mark="hit") count = 0 colormap = {} for node in graph.getNodesByProp("hit"): count += 1 off = 0 cbsize = node[1].get("cbsize") if cbsize == None: raise Exception("node has no cbsize: %s" % repr(node)) # step through opcode for a node while off < cbsize: op = self.vw.parseOpcode(node[0] + off) colormap[op.va] = "brown" off += len(op) self.vw.vprint("Colored Blocks: %d" % count) vqtevent("viv:colormap", colormap) return colormap
def graphSearch(self): """ Do a graph search in the code for leaf nodes TODO: check if detected instruction is a jump! """ # search for EIP and loop on all of them for eip in self.vw.getEntryPoints(): if self.debug: print("FOUND ENTRY POINT 0x%08x\n" % eip) # build a code graph starting at EIP graph = viv_cgh.buildFunctionGraph(self.vw, eip) visited = [] for node in graph.getNodes(): if(node in visited): if self.debug: print("LOOP DETECTED in CODE -- ignoring path!") break else: visited.append(node) if graph.isLeafNode(node): # TODO print the surrounding code block if self.isJumpFar(node[0]): print("TIP: Set BP at: 0x%08x (%s)" % (node[0], self.vw.reprVa(node[0]))) refby = self.vw.getXrefsTo(node[0]) for ref in refby: print(" REFERENCED at: 0x%08x (%s)" % (ref[0], self.vw.reprVa(ref[0]))) self.getFunctionCode(ref[0]) self.bininfo.breakpoints.append(node[0]) return
def getFunctionEdgeList(vw, fva): ''' Return a list of b1mnems|b2mnems strings where b*mnems is a list of the instruction mnemonics for each block. This creates a list of "edges" rather than a list of blocks which makes false positive matches on similar blocks less likely. ''' g = viv_graphutil.buildFunctionGraph(vw, fva) # A variant on the function hash graph stuff which allows # similarity comparison... for nid,ninfo in g.getNodes(): # Just the mnemonics... (may allow block comparison *inside* funcions with diffs) mnems = getBlockMnems(vw, ninfo['cbva'], ninfo['cbsize']) ninfo['diff_node'] = mnems # Similar to get function hash but the data in the block relationship *must* # be more stable across changes to identify the totally new blocks edgediff = [] for eid, fromid, toid, einfo in g.getEdges(): fromdiff = g.getNodeInfo(fromid, 'diff_node') todiff = g.getNodeInfo(toid, 'diff_node') edgediff.append('%s|%s' % (fromdiff, todiff)) return edgediff
def renderFunctionGraph(self, fva): self.fva = fva #self.graph = self.vw.getFunctionGraph(fva) self.graph = viv_graphutil.buildFunctionGraph(self.vw, fva, revloop=True) # Go through each of the nodes and render them so we know sizes for node in self.graph.getNodes(): #cbva,cbsize = self.graph.getCodeBlockBounds(node) cbva = node[1].get('cbva') cbsize = node[1].get('cbsize') self.mem_canvas.renderMemory(cbva, cbsize) # Let the renders complete... eatevents() frame = self.mem_canvas.page().mainFrame() frame.evaluateJavaScript(funcgraph_js) for nid,nprops in self.graph.getNodes(): cbva = nprops.get('cbva') cbname = 'codeblock_%.8x' % cbva girth, ok = frame.evaluateJavaScript('document.getElementById("%s").offsetWidth;' % cbname).toInt() height, ok = frame.evaluateJavaScript('document.getElementById("%s").offsetHeight;' % cbname).toInt() self.graph.setNodeProp((nid,nprops), "size", (girth, height)) self.dylayout = vg_dynadag.DynadagLayout(self.graph) self.dylayout._barry_count = 20 self.dylayout.layoutGraph() width, height = self.dylayout.getLayoutSize() svgid = 'funcgraph_%.8x' % fva frame.evaluateJavaScript('svgwoot("vbody", "%s", %d, %d);' % (svgid, width+18, height)) for nid,nprops in self.graph.getNodes(): cbva = nprops.get('cbva') if cbva == None: continue xpos, ypos = nprops.get('position') girth, height = nprops.get('size') foid = 'fo_cb_%.8x' % cbva cbid = 'codeblock_%.8x' % cbva frame.evaluateJavaScript('addSvgForeignObject("%s", "%s", %d, %d);' % (svgid, foid, girth+16, height)) frame.evaluateJavaScript('addSvgForeignHtmlElement("%s", "%s");' % (foid, cbid)) frame.evaluateJavaScript('moveSvgElement("%s", %d, %d);' % (foid, xpos, ypos)) # Draw in some edge lines! for eid, n1, n2, einfo in self.graph.getEdges(): points = einfo.get('edge_points') pointstr = ' '.join(['%d,%d' % (x,y) for (x,y) in points ]) frame.evaluateJavaScript('drawSvgLine("%s", "edge_%.8s", "%s");' % (svgid, eid, pointstr)) self.updateWindowTitle()
def checkGetCodePathsTo(self, vw, fva, cbva): graph = viv_graph.buildFunctionGraph(vw, fva ) paths = [ path for path in viv_graph.getCodePathsTo(graph, cbva) ] self.codepathsto = paths self.assertGreater(len(self.codepaths), len(self.codepathsto)) paths = [ path for path in graph.getHierPathsTo((cbva,)) ] self.hierpathsto = paths self.assertGreater(len(self.codepaths), len(self.hierpathsto))
def getFunctionHash(self, vw, fva): rowpos = {} g = viv_graphutil.buildFunctionGraph(vw, fva) weights = g.getNodeWeights() #print '0x%.8x: %d' % (fva, len(g.getNodes())) # Here, we assume that the calculation of "node order" is consistant # because it is dependant on the graphutil code following branches in # a consistant way. # NOTE: This is a good way to identify totally identical functions, but # a terrible way to tell *what* changed due to all subsequant nodes being # bumped and moved around... for nid,ninfo in g.getNodes(): # Set each node's "node identifier" info # ( which is currently, nodeid:depth:rowpos weight = weights.get(nid, -1) row = rowpos.get(weight) if row == None: row = [] rowpos[weight] = row # Just ID information and graph layout... (may allow matching # even in the presence of compiler optimization #ninfo['diff_node'] = '%d:%d:%d' % (nid, weight, len(row)) # Mnemonics and graph layouts. Probably the most twitchy match... #mnems = getBlockMnems(vw, ninfo['cbva'], ninfo['cbsize']) #ninfo['diff_node'] = '%s:%d:%d' % (mnems, weight, len(row)) # FIXME this could also do hash's of above/below to do block compare... # Just the mnemonics... (may allow block comparison *inside* funcions with diffs) mnems = getBlockMnems(vw, ninfo['cbva'], ninfo['cbsize']) ninfo['diff_node'] = mnems #print nid,ninfo['diff_node'] row.append(nid) # The *actual* data we use to unique the function is the *relationship* # between nodes rather than the nodes themselves. Each node's 'diff_node' # identifier is assumed to be non-unique, but grouping. The relationship # between nodes is used to feed a sha1 hash... edgediff = [] for eid, fromid, toid, einfo in g.getEdges(): fromdiff = g.getNodeInfo(fromid, 'diff_node') todiff = g.getNodeInfo(toid, 'diff_node') edgediff.append('%s|%s' % (fromdiff, todiff)) edgediff.sort() return hashlib.sha1(','.join(edgediff)).hexdigest()
def getSymbolikGraph(self, fva, fgraph=None): ''' Instantiate a standard vivisect function graph (visgraph hierarchical graph) and translate all the opcodes in each block to un-applied symbolik effects. The list of effects for each node is stored in 'symbolik_effects' list in the node properties. ''' xlate = self.getTranslator() graph = SymbolikFunctionGraph() if fgraph == None: fgraph = viv_graph.buildFunctionGraph(self.vw, fva) for nodeva,ninfo in fgraph.getNodes(): cbva = ninfo.get('cbva') cbsize = ninfo.get('cbsize') cbmax = cbva + cbsize oplist = [] while cbva < cbmax: op = self.vw.parseOpcode(cbva) oplist.append(op) cbva += len(op) for op in oplist: xlate.translateOpcode(op) efflist = xlate.getEffects() # we needn't copy conlist = xlate.getConstraints() xlate.clearEffects() # Put constraints into a dictionary lookup by target address con_lookup = {} for coneff in conlist: addrva = coneff.addrsym.solve() clist = con_lookup.get(addrva) if clist == None: clist = [] con_lookup[addrva] = clist clist.append(coneff) # Save these off in node info for later ninfo['opcodes'] = oplist ninfo['symbolik_effects'] = efflist # Add the constraints to the edges for eid,fromid,toid,einfo in fgraph.getRefsFromByNid(nodeva): clist = con_lookup.pop(toid, None) if clist == None: continue einfo['symbolik_constraints'] = clist #if len(con_lookup): #raise Exception('FIXME: We ditched a constraint! %s' % repr(con_lookup)) return fgraph
def getSymbolikGraph(self, fva, fgraph=None): ''' Instantiate a standard vivisect function graph (visgraph hierarchical graph) and translate all the opcodes in each block to un-applied symbolik effects. The list of effects for each node is stored in 'symbolik_effects' list in the node properties. ''' xlate = self.getTranslator() if fgraph is None: fgraph = viv_graph.buildFunctionGraph(self.vw, fva) for nodeva, ninfo in fgraph.getNodes(): cbva = ninfo.get('cbva') cbsize = ninfo.get('cbsize') cbmax = cbva + cbsize oplist = [] while cbva < cbmax: op = self.vw.parseOpcode(cbva) oplist.append(op) cbva += len(op) for op in oplist: xlate.translateOpcode(op) efflist = xlate.getEffects() # we needn't copy conlist = xlate.getConstraints() xlate.clearEffects() # Put constraints into a dictionary lookup by target address con_lookup = {} for coneff in conlist: addrva = coneff.addrsym.solve() clist = con_lookup.get(addrva) if clist is None: clist = [] con_lookup[addrva] = clist clist.append(coneff) # Save these off in node info for later ninfo['opcodes'] = oplist ninfo['symbolik_effects'] = efflist # Add the constraints to the edges for eid, fromid, toid, einfo in fgraph.getRefsFromByNid(nodeva): clist = con_lookup.pop(toid, None) if clist is None: continue einfo['symbolik_constraints'] = clist #if len(con_lookup): #raise Exception('FIXME: We ditched a constraint! %s' % repr(con_lookup)) return fgraph
def test_graphutil_longpath(self): ''' order matters ''' vw = self.firefox_vw g = v_t_graphutil.buildFunctionGraph(vw, 0x1400037c0) longpath = [] longpath = [0x1400037c0, 0x14000382d, 0x1400038a4, 0x140003964, 0x140003994, 0x1400039cc, 0x1400039f6, 0x140003a29, 0x140003a59, 0x140003a83, 0x140003ab3, 0x140003b3b, 0x140003b3e, 0x140003ccd, 0x140003c3c, 0x140003c3f, 0x140003c4c, 0x140003c52, 0x140003c5f, 0x140003c65, 0x140003c72, 0x140003c78, 0x140003c85, 0x140003c8b, 0x140003c98, 0x140003c9e, 0x140003cab, 0x140003cb1, 0x140003cc2, 0x1400038fd, 0x14000390a, 0x140003910, 0x14000392b, 0x140003938, 0x14000393e] path = next(v_t_graphutil.getLongPath(g)) path = [k[0] for k in path] self.assertEqual(path, longpath)
def renderFunctionGraph(self, fva, graph=None): self.fva = fva #self.graph = self.vw.getFunctionGraph(fva) if graph == None: try: graph = viv_graphutil.buildFunctionGraph(self.vw, fva, revloop=True) except Exception, e: import sys sys.excepthook(*sys.exc_info()) return
def do_searchopcodes(self, line): ''' search opcodes/function for a pattern searchopcodes [-f <funcva>] [options] <pattern> -f [fva] - focus on one function -c - search comments -o - search operands -t - search text -M <color> - mark opcodes (default = orange) -R - pattern is REGEX (otherwise just text) ''' parser = e_cli.VOptionParser() parser.add_option('-f', action='store', dest='funcva', type='long') parser.add_option('-c', action='store_true', dest='searchComments') parser.add_option('-o', action='store_true', dest='searchOperands') parser.add_option('-t', action='store_true', dest='searchText') parser.add_option('-M', action='store', dest='markColor', default='orange') parser.add_option('-R', action='store_true', dest='is_regex') argv = shlex.split(line) try: options, args = parser.parse_args(argv) except Exception as e: self.vprint(repr(e)) return self.do_help('searchopcodes') pattern = ' '.join(args) if len(pattern) == 0: self.vprint('you must specify a pattern') return self.do_help('searchopcodes') # generate our interesting va list valist = [] if options.funcva: # setup valist from function data try: fva = int(args[0], 0) graph = viv_graph.buildFunctionGraph(self, fva) except Exception, e: self.vprint(repr(e)) return for nva, node in graph.getNodes(): va = nva endva = va + node.get('cbsize') while va < endva: lva, lsz, ltype, ltinfo = self.getLocation(va) valist.append(va) va += lsz
def checkTestWorkspace(self, vw, ans): self.assertEqual(vw.getMeta('Platform'), ans.getMeta('Platform')) self.assertEqual(vw.getMeta('Architecture'), ans.getMeta('Architecture')) self.assertEqual(sorted(vw.getFunctions()), sorted(ans.getFunctions())) self.assertEqual(sorted(vw.getLibraryDependancies()), sorted(ans.getLibraryDependancies())) self.assertEqual(sorted(vw.getRelocations()), sorted(ans.getRelocations())) self.assertEqual(sorted(vw.getImports()), sorted(ans.getImports())) self.assertEqual(sorted(vw.getExports()), sorted(ans.getExports())) self.assertEqual(sorted(vw.getSegments()), sorted(ans.getSegments())) # self.assertEqual( sorted( vw.getCodeBlocks() ), sorted( ans.getCodeBlocks() ) ) # self.assertEqual( sorted( vw.getLocations() ), sorted( ans.getLocations() ) ) # self.assertEqual( sorted( vw.getNames() ), sorted( ans.getNames() ) ) self.assertEqual(sorted(vw.getFiles()), sorted(ans.getFiles())) for fva in ans.getFunctions(): self.assertEqual(vw.getFunction(fva), ans.getFunction(fva)) vwfgraph = viv_graph.buildFunctionGraph(vw, fva) ansfgraph = viv_graph.buildFunctionGraph(ans, fva) vwnodes = list(vwfgraph.nodes.keys()) ansnodes = list(ansfgraph.nodes.keys()) self.assertEqual(sorted(vwnodes), sorted(ansnodes))
def checkTestWorkspace(self, vw, ans): self.assertEqual( vw.getMeta('Platform'), ans.getMeta('Platform') ) self.assertEqual( vw.getMeta('Architecture'), ans.getMeta('Architecture') ) self.assertEqual( sorted( vw.getFunctions() ), sorted( ans.getFunctions() ) ) self.assertEqual( sorted( vw.getLibraryDependancies() ), sorted( ans.getLibraryDependancies() ) ) self.assertEqual( sorted( vw.getRelocations() ), sorted( ans.getRelocations() ) ) self.assertEqual( sorted( vw.getImports() ), sorted( ans.getImports() ) ) self.assertEqual( sorted( vw.getExports() ), sorted( ans.getExports() ) ) self.assertEqual( sorted( vw.getSegments() ), sorted( ans.getSegments() ) ) #self.assertEqual( sorted( vw.getCodeBlocks() ), sorted( ans.getCodeBlocks() ) ) #self.assertEqual( sorted( vw.getLocations() ), sorted( ans.getLocations() ) ) #self.assertEqual( sorted( vw.getNames() ), sorted( ans.getNames() ) ) self.assertEqual( sorted( vw.getFiles() ), sorted( ans.getFiles() ) ) for fva in ans.getFunctions(): self.assertEqual(vw.getFunction(fva), ans.getFunction(fva)) vwfgraph = viv_graph.buildFunctionGraph(vw, fva) ansfgraph = viv_graph.buildFunctionGraph(ans, fva) vwnodes = vwfgraph.nodes.keys() ansnodes = ansfgraph.nodes.keys() self.assertEqual( sorted( vwnodes ), sorted( ansnodes ) )
def test_graphutil_longpath(self): ''' order matters ''' vw = self.gcc_vw # TODO: symbolik switchcase # g = v_t_graphutil.buildFunctionGraph(vw, 0x445db6) # longpath = [0x445db6, 0x445dc3, 0x445dd7, 0x445deb, 0x445dfc, 0x445e01, 0x445e0b, 0x445ddc, 0x445e11, 0x445e20, 0x445e2c, 0x445e30, 0x445e4b, 0x445e5b, 0x445e72, 0x445e85, 0x445e85, 0x445ea1, 0x445ea3, 0x445eae, 0x445ebb, 0x445df5, 0x445ec2] # path = next(v_t_graphutil.getLongPath(g)) # self.assertEqual(longpath, map(lambda k: k[0], path)) g = v_t_graphutil.buildFunctionGraph(vw, 0x405c10) longpath=[0x405c10, 0x405c48, 0x405ca6, 0x405cb0, 0x405cc3, 0x405c4e, 0x405c57, 0x405c5c, 0x405c6b, 0x405cd4, 0x405ce4, 0x405c80, 0x405c8c, 0x405cf6, 0x405c92] path = next(v_t_graphutil.getLongPath(g)) path = map(lambda k: k[0], path) self.assertEqual(path, longpath)
def do_pathcount(self, line): ''' Mostly for testing the graph stuff... this will likely be removed. (does not count paths with loops currently...) Usage: pathcount <func_expr> ''' fva = self.parseExpression(line) if not self.isFunction(fva): self.vprint('Not a function!') return g = v_t_graph.buildFunctionGraph(self, fva) pathcnt = 0 for path in v_t_graph.getCodePaths(g): self.vprint('Path through 0x%.8x: %s' % (fva, [hex(p[0]) for p in path])) pathcnt += 1 self.vprint('Total Paths: %d' % pathcnt)
def renderFunctionGraph(self, fva=None, graph=None): if fva is not None: self.fva = fva if graph is None: try: graph = viv_graphutil.buildFunctionGraph(self.vw, fva, revloop=True) except Exception as e: self.vw.vprint(f'Error building function graph for {fva} ({str(e)}') self.vw.vprint(traceback.format_exc()) return self.graph = graph # Go through each of the nodes and render them so we know sizes self.nodes = self.graph.getNodes() if len(self.nodes): node = self.nodes.pop(0) cbva = node[1].get('cbva') cbsize = node[1].get('cbsize') self.mem_canvas.renderMemory(cbva, cbsize, self._renderCodeBlock)
def test_graphutil_getopsfrompath(self): vw = self.firefox_vw g = v_t_graphutil.buildFunctionGraph(vw, 0x140048b84) path = next(v_t_graphutil.getLongPath(g)) ops = [ 'push rbx', 'push rsi', 'push rdi', 'sub rsp,64', 'mov rbx,rcx', 'call qword [rip + 36451]', 'mov rsi,qword [rbx + 248]', 'xor edi,edi', 'xor r8d,r8d', 'lea rdx,qword [rsp + 96]', 'mov rcx,rsi', 'call qword [rip + 36505]', 'test rax,rax', 'jz 0x140048bed', 'and qword [rsp + 56],0', 'lea rcx,qword [rsp + 104]', 'mov rdx,qword [rsp + 96]', 'mov r9,rax', 'mov qword [rsp + 48],rcx', 'mov r8,rsi', 'lea rcx,qword [rsp + 112]', 'mov qword [rsp + 40],rcx', 'xor ecx,ecx', 'mov qword [rsp + 32],rbx', 'call qword [rip + 36514]', 'inc edi', 'cmp edi,2', 'jl 0x140048b9e', 'add rsp,64', 'pop rdi', 'pop rsi', 'pop rbx', 'ret ' ] self.assertEqual( ops, [str(op) for op in v_t_graphutil.getOpsFromPath(vw, g, path)])
def renderFunctionGraph(self, fva=None, graph=None): ''' Begins the process of drawing the function graph to the canvas. So, this is a bit of complicated mess due to how PyQt5's runJavaScript method works. runJavaScript is asynchronous, but not like actual python async, but Qt's async variant with their event loop. The only way to get some level of a guarantee that the javascript ran (which we need for getting the size of codeblocks and line layouts) is via the secondary parameter, which is a callback that gets run when the javascript completes. That being said, technically according to their docs, the callback is guaranteed to be run, but it might be during page destruction. In practice it's somewhat responsive. runJavascript also doesn't check if the DOM has been created. So...yea. In practice that doesn't matter too much, but something to keep in mind. So the general method is to build up a bunch of javascript that we need to run in order to render the codeblocks to get their sizes, layout the graph lines, realign everything nicely, etc. And it's all callbacks, all the way down. ''' if fva is not None: self.fva = fva if graph is None: try: graph = viv_graphutil.buildFunctionGraph(self.vw, fva, revloop=True) except Exception as e: self.vw.vprint(f'Error building function graph for {fva} ({str(e)}') self.vw.vprint(traceback.format_exc()) return self.graph = graph # Go through each of the nodes and render them so we know sizes self.nodes = self.graph.getNodes() if len(self.nodes): node = self.nodes.pop(0) cbva = node[1].get('cbva') cbsize = node[1].get('cbsize') self.mem_canvas.renderMemory(cbva, cbsize, self._renderCodeBlock)
def do_pathcount(self, line): ''' Mostly for testing the graph stuff... this will likely be removed. (does not count paths with loops currently...) Usage: pathcount <func_expr> ''' fva = self.parseExpression(line) if not self.isFunction(fva): self.vprint('Not a function!') return g = v_t_graph.buildFunctionGraph(self, fva) # Lets find the "bottom" nodes... endblocks = [] for nid,ninfo in g.getNodes(): if len(g.getRefsFrom(nid)) == 0: endblocks.append((nid,ninfo)) for nid,ninfo in endblocks: paths = list(g.pathSearch(0, toid=nid)) self.vprint('paths to 0x%.8x: %d' % (ninfo.get('cbva'), len(paths)))
def test_graphutil_getopsfrompath(self): vw = self.gcc_vw g = v_t_graphutil.buildFunctionGraph(vw, 0x414a2a) path = next(v_t_graphutil.getLongPath(g)) ops = [ 'push r14', 'push r13', 'mov r13,rdx', 'push r12', 'push rbp', 'mov r12,rsi', 'push rbx', 'mov rbx,rdi', 'sar r12,3', 'mov rbp,rsi', 'mov edx,r12d', 'mov r14d,ecx', 'sub rsp,16', 'mov rdi,qword [rdi + 48]', 'mov qword [rsp + 8],rsi', 'lea rsi,qword [rsp + 8]', 'call 0x00414962', 'cmp qword [rax],0', 'jz 0x00414abc', 'mov rdx,qword [rax + 8]', 'mov rax,qword [rdx]', 'cmp r13,rax', 'jbe 0x00414a85', 'mov edx,0x004d76b0', 'mov esi,151', 'mov edi,0x004d7534', 'call 0x0041806c', 'sub rax,r13', 'test r14l,r14l', 'mov qword [rdx],rax', 'jz 0x00414abc', 'mov rbx,qword [rbx + 48]', 'lea rsi,qword [rsp + 8]', 'xor ecx,ecx', 'mov edx,r12d', 'mov qword [rsp + 8],rbp', 'mov rdi,rbx', 'call 0x004154ec', 'cmp qword [rax],0', 'jz 0x00414abc', 'mov qword [rax],1', 'inc qword [rbx + 24]', 'add rsp,16', 'pop rbx', 'pop rbp', 'pop r12', 'pop r13', 'pop r14', 'ret ' ] self.assertEqual(ops, map(str, v_t_graphutil.getOpsFromPath(vw, g, path)))
def do_searchopcodes(self, line): ''' search opcodes/function for a pattern searchopcodes [-f <funcva>] [options] <pattern> -f [fva] - focus on one function -c - search comments -o - search operands -t - search text -M <color> - mark opcodes (default = orange) -R - pattern is REGEX (otherwise just text) ''' parser = e_cli.VOptionParser() parser.add_option('-f', action='store', dest='funcva', type='int') parser.add_option('-c', action='store_true', dest='searchComments') parser.add_option('-o', action='store_true', dest='searchOperands') parser.add_option('-t', action='store_true', dest='searchText') parser.add_option('-M', action='store', dest='markColor', default='orange') parser.add_option('-R', action='store_true', dest='is_regex') argv = shlex.split(line) try: options, args = parser.parse_args(argv) except Exception as e: self.vprint(repr(e)) return self.do_help('searchopcodes') pattern = ' '.join(args) if len(pattern) == 0: self.vprint('you must specify a pattern') return self.do_help('searchopcodes') # generate our interesting va list valist = [] if options.funcva: # setup valist from function data try: fva = options.funcva graph = viv_graph.buildFunctionGraph(self, fva) except Exception as e: self.vprint(repr(e)) return for nva, node in graph.getNodes(): va = nva endva = va + node.get('cbsize') while va < endva: lva, lsz, ltype, ltinfo = self.getLocation(va) valist.append(va) va += lsz else: # the whole workspace is our oyster valist = [ va for va, lvsz, ltype, ltinfo in self.getLocations(LOC_OP) ] res = [] canv = e_canvas.StringMemoryCanvas(self) defaultSearchAll = True for va in valist: try: addthis = False op = self.parseOpcode(va) # search comment if options.searchComments: defaultSearchAll = False cmt = self.getComment(va) if cmt is not None: if options.is_regex: if len(re.findall(pattern, cmt)): addthis = True else: if pattern in cmt: addthis = True # search operands if options.searchOperands: defaultSearchAll = False for opidx, oper in enumerate(op.opers): # we're writing to a temp canvas, so clear it before each test canv.clearCanvas() oper = op.opers[opidx] oper.render(canv, op, opidx) operepr = canv.strval if options.is_regex: if len(re.findall(pattern, operepr)): addthis = True else: if pattern in operepr: addthis = True # if we're doing non-regex, let's test against real numbers # (instead of converting to hex and back) numpattrn = pattern try: numpattrn = int(numpattrn, 0) except: pass if numpattrn in vars(oper).values(): addthis = True # search full text if options.searchText or defaultSearchAll: # search through the rendering of the opcode, as well as the comment canv.clearCanvas() op.render(canv) oprepr = canv.strval cmt = self.getComment(va) if cmt is not None: oprepr += " ; " + cmt if options.is_regex: if len(re.findall(pattern, oprepr)): addthis = True else: if pattern in oprepr: addthis = True # only want one listing of each va, no matter how many times it matches if addthis: res.append(va) except: self.vprint(''.join( traceback.format_exception(*sys.exc_info()))) if len(res) == 0: self.vprint('pattern not found: %s (%s)' % (pattern.encode('utf-8').hex(), repr(pattern))) return # set the color for each finding color = options.markColor colormap = {va: color for va in res} if self._viv_gui is not None: from vqt.main import vqtevent vqtevent('viv:colormap', colormap) self.vprint('matches for: %s (%s)' % (pattern.encode('utf-8').hex(), repr(pattern))) for va in res: mbase, msize, mperm, mfile = self.memobj.getMemoryMap(va) pname = e_memory.reprPerms(mperm) sname = self.reprPointer(va) op = self.parseOpcode(va) self.canvas.renderMemory(va, len(op)) cmt = self.getComment(va) if cmt is not None: self.canvas.addText('\t\t; %s (Perms: %s, Smartname: %s)' % (cmt, pname, sname)) self.canvas.addText('\n') self.vprint('done (%d results).' % len(res))
def test_graphutil_getcodepaths(self): ''' In this function, order doesn't matter ''' vw = self.firefox_vw g = v_t_graphutil.buildFunctionGraph(vw, 0x140010e60) paths = [ set([ 5368778336, 5368778350, 5368778362, 5368778366, 5368778394, 5368778400 ]), set([ 5368778336, 5368778350, 5368778362, 5368778366, 5368778498, 5368778515, 5368778394, 5368778400 ]), set([ 5368778336, 5368778350, 5368778362, 5368778366, 5368778498, 5368778520, 5368778544, 5368778549 ]), set([ 5368778336, 5368778350, 5368778362, 5368778366, 5368778498, 5368778520, 5368778544, 5368778601, 5368778603 ]), set([ 5368778336, 5368778350, 5368778362, 5368778366, 5368778498, 5368778520, 5368778560, 5368778603 ]), set([ 5368778336, 5368778350, 5368778482, 5368778366, 5368778394, 5368778400 ]), set([ 5368778336, 5368778350, 5368778482, 5368778366, 5368778498, 5368778515, 5368778394, 5368778400 ]), set([ 5368778336, 5368778350, 5368778482, 5368778366, 5368778498, 5368778520, 5368778544, 5368778549 ]), set([ 5368778336, 5368778350, 5368778482, 5368778366, 5368778498, 5368778520, 5368778544, 5368778601, 5368778603 ]), set([ 5368778336, 5368778350, 5368778482, 5368778366, 5368778498, 5368778520, 5368778560, 5368778603 ]), set([5368778336, 5368778400]), ] pathcount = 0 genr = v_t_graphutil.getCodePaths(g, loopcnt=0, maxpath=None) for path in genr: p = set([k[0] for k in path]) self.assertIn(p, paths) pathcount += 1 self.assertEqual(11, pathcount) g = v_t_graphutil.buildFunctionGraph(vw, vw.getFunction(0x1400110a0)) thruCnt = glen(v_t_graphutil.getCodePathsThru(g, 0x1400110a0)) self.assertEqual(23, thruCnt) thruCnt = glen( v_t_graphutil.getCodePathsThru(g, 0x1400110a0, maxpath=2)) self.assertEqual(2, thruCnt) g = v_t_graphutil.buildFunctionGraph(vw, vw.getFunction(0x14001ead0)) toCnt = glen(v_t_graphutil.getCodePathsTo(g, 0x14001ec2a)) self.assertEqual(2, toCnt) toCnt = glen(v_t_graphutil.getCodePathsTo(g, 0x14001ec2a, maxpath=99)) self.assertEqual(2, toCnt) g = v_t_graphutil.buildFunctionGraph(vw, vw.getFunction(0x1400019ab)) fromCnt = glen(v_t_graphutil.getCodePathsFrom(g, 0x1400019ab)) self.assertEqual(2, fromCnt) fromCnt = glen( v_t_graphutil.getCodePathsFrom(g, 0x1400019ab, maxpath=1)) self.assertEqual(1, fromCnt)
def checkCoveragePaths(self, vw, fva): graph = viv_graph.buildFunctionGraph(vw, fva) paths = [ path for path in viv_graph.getCoveragePaths(graph, 150) ] self.codepaths = paths self.assertEqual(len(self.codepaths), 22)
def checkGetLongPath(self, vw, fva): graph = viv_graph.buildFunctionGraph(vw, fva) paths = [ path for path in viv_graph.getLongPath(graph) ] self.codepaths = paths self.assertGreater(len(self.codepaths), 150)
def checkPathGenGetCodePaths(self, vw, fva): graph = viv_graph.buildFunctionGraph(vw, fva) paths = [ path for path in viv_graph.getCodePathsThru(graph) ] self.codepaths = paths self.assertGreater(len(self.codepaths), 150)
def test_graphutil_getcodepaths(self): ''' In this function, order doesn't matter ''' vw = self.firefox_vw g = v_t_graphutil.buildFunctionGraph(vw, 0x140010e60) paths = [ set([ 0x140010e60, 0x140010e6e, 0x140010e7a, 0x140010e7e, 0x140010e9a, 0x140010ea0 ]), set([ 0x140010e60, 0x140010e6e, 0x140010e7a, 0x140010e7e, 0x140010e9a, 0x140010ea0, 0x140010f02, 0x140010f13 ]), set([ 0x140010e60, 0x140010e6e, 0x140010e7a, 0x140010e7e, 0x140010f02, 0x140010f18 ]), set([ 0x140010e60, 0x140010e6e, 0x140010e7e, 0x140010e9a, 0x140010ea0, 0x140010ef2 ]), set([ 0x140010e60, 0x140010e6e, 0x140010e7e, 0x140010e9a, 0x140010ea0, 0x140010ef2, 0x140010f02, 0x140010f13 ]), set([ 0x140010e60, 0x140010e6e, 0x140010e7e, 0x140010ef2, 0x140010f02, 0x140010f18 ]), set([0x140010e60, 0x140010ea0]), ] pathcount = 0 genr = v_t_graphutil.getCodePaths(g, loopcnt=0, maxpath=None) for path in genr: p = set([k[0] for k in path]) self.assertIn(p, paths) pathcount += 1 self.assertEqual(7, pathcount) g = v_t_graphutil.buildFunctionGraph(vw, vw.getFunction(0x1400110a0)) thruCnt = glen(v_t_graphutil.getCodePathsThru(g, 0x1400110a0)) self.assertEqual(21, thruCnt) thruCnt = glen( v_t_graphutil.getCodePathsThru(g, 0x1400110a0, maxpath=2)) self.assertEqual(2, thruCnt) g = v_t_graphutil.buildFunctionGraph(vw, vw.getFunction(0x14001ead0)) toCnt = glen(v_t_graphutil.getCodePathsTo(g, 0x14001ec2a)) self.assertEqual(2, toCnt) toCnt = glen(v_t_graphutil.getCodePathsTo(g, 0x14001ec2a, maxpath=99)) self.assertEqual(2, toCnt) g = v_t_graphutil.buildFunctionGraph(vw, vw.getFunction(0x1400019ab)) fromCnt = glen(v_t_graphutil.getCodePathsFrom(g, 0x1400019ab)) self.assertEqual(2, fromCnt) fromCnt = glen( v_t_graphutil.getCodePathsFrom(g, 0x1400019ab, maxpath=1)) self.assertEqual(1, fromCnt)
def renderFunctionGraph(self, fva, graph=None): self.fva = fva #self.graph = self.vw.getFunctionGraph(fva) if graph == None: graph = viv_graphutil.buildFunctionGraph(self.vw, fva, revloop=True) self.graph = graph # Go through each of the nodes and render them so we know sizes for node in self.graph.getNodes(): #cbva,cbsize = self.graph.getCodeBlockBounds(node) cbva = node[1].get('cbva') cbsize = node[1].get('cbsize') self.mem_canvas.renderMemory(cbva, cbsize) # Let the renders complete... eatevents() frame = self.mem_canvas.page().mainFrame() frame.evaluateJavaScript(funcgraph_js) for nid, nprops in self.graph.getNodes(): cbva = nprops.get('cbva') cbname = 'codeblock_%.8x' % cbva girth, ok = frame.evaluateJavaScript( 'document.getElementById("%s").offsetWidth;' % cbname).toInt() height, ok = frame.evaluateJavaScript( 'document.getElementById("%s").offsetHeight;' % cbname).toInt() self.graph.setNodeProp((nid, nprops), "size", (girth, height)) self.dylayout = vg_dynadag.DynadagLayout(self.graph) self.dylayout._barry_count = 20 self.dylayout.layoutGraph() width, height = self.dylayout.getLayoutSize() svgid = 'funcgraph_%.8x' % fva frame.evaluateJavaScript('svgwoot("vbody", "%s", %d, %d);' % (svgid, width + 18, height)) for nid, nprops in self.graph.getNodes(): cbva = nprops.get('cbva') if cbva == None: continue xpos, ypos = nprops.get('position') girth, height = nprops.get('size') foid = 'fo_cb_%.8x' % cbva cbid = 'codeblock_%.8x' % cbva frame.evaluateJavaScript( 'addSvgForeignObject("%s", "%s", %d, %d);' % (svgid, foid, girth + 16, height)) frame.evaluateJavaScript('addSvgForeignHtmlElement("%s", "%s");' % (foid, cbid)) frame.evaluateJavaScript('moveSvgElement("%s", %d, %d);' % (foid, xpos, ypos)) # Draw in some edge lines! for eid, n1, n2, einfo in self.graph.getEdges(): points = einfo.get('edge_points') pointstr = ' '.join(['%d,%d' % (x, y) for (x, y) in points]) frame.evaluateJavaScript('drawSvgLine("%s", "edge_%.8s", "%s");' % (svgid, eid, pointstr)) self.updateWindowTitle()
def checkCoveragePaths(self, vw, fva): graph = viv_graph.buildFunctionGraph(vw, fva) paths = [path for path in viv_graph.getCoveragePaths(graph, 150)] self.codepaths = paths self.assertEqual(len(self.codepaths), 22)
def checkGetLongPath(self, vw, fva): graph = viv_graph.buildFunctionGraph(vw, fva) paths = [path for path in viv_graph.getLongPath(graph)] self.codepaths = paths self.assertGreater(len(self.codepaths), 150)
else: vw.loadFromFile(malbin, None) vw.analyze() # binary analysis" vw.saveWorkspace() # save work # Test -- ! need to find correctly the "main" function for eip in vw.getEntryPoints(): print("FOUND ENTRY POINT 0x%08x\n" % eip) eip = vw.getEntryPoints()[0] # to replace by a call to a function for each iteration of loop # add threading # call the code block graph #graph = viv_cg.CodeBlockGraph(vw) print "GRAPH SEARCH" #graph = vw.getFunctionGraph(eip) graph = viv_cgh.buildFunctionGraph(vw, eip) #graph = vw.getCallGraph() visited = [] print("NUMBER OF NODES: %d" % len(graph.getNodes())) for node in graph.getNodes(): if(node in visited): print("LOOP DETECTED!") break else: visited.append(node) if graph.isLeafNode(node): print "LEAF NODE FOUND:" print("Set BP at: 0x%08x" % node[0]) print "THIS IS THE END :-D or :'("
def checkPathGenGetCodePaths(self, vw, fva): graph = viv_graph.buildFunctionGraph(vw, fva) paths = [path for path in viv_graph.getCodePathsThru(graph)] self.codepaths = paths self.assertGreater(len(self.codepaths), 150)
def test_graphutil_getcodepaths(self): ''' In this function, order doesn't matter ''' vw = self.gcc_vw g = v_t_graphutil.buildFunctionGraph(vw, 0x456190) paths = [ set([ 0x456190, 0x4561ba, 0x456202, 0x456216, 0x45621c, 0x456240, 0x4561fa, 0x456242 ]), set([ 0x456190, 0x4561ba, 0x456202, 0x456216, 0x45621c, 0x40aa97, 0x4561ea, 0x4561ef, 0x4561fa, 0x456242 ]), set([ 0x456190, 0x4561ba, 0x456202, 0x456216, 0x45621c, 0x40aa97, 0x4561ea, 0x4561fa, 0x456242 ]), set([ 0x456190, 0x4561ba, 0x456202, 0x456216, 0x4561c2, 0x4561d0, 0x4561ea, 0x4561ef, 0x4561fa, 0x456242 ]), set([ 0x456190, 0x4561ba, 0x456202, 0x456216, 0x4561c2, 0x4561d0, 0x4561ea, 0x4561fa, 0x456242 ]), set([ 0x456190, 0x4561ba, 0x456202, 0x456216, 0x4561c2, 0x40aa85, 0x40aa8d, 0x4561d0, 0x4561ea, 0x4561ef, 0x4561fa, 0x456242 ]), set([ 0x456190, 0x4561ba, 0x456202, 0x456216, 0x4561c2, 0x40aa85, 0x40aa8d, 0x4561d0, 0x4561ea, 0x4561fa, 0x456242 ]), set([ 0x456190, 0x4561ba, 0x456202, 0x456216, 0x4561c2, 0x40aa85, 0x40aab9, 0x456242 ]), set([ 0x456190, 0x4561ba, 0x456202, 0x45621c, 0x456240, 0x4561fa, 0x456242 ]), set([ 0x456190, 0x4561ba, 0x456202, 0x45621c, 0x40aa97, 0x4561ea, 0x4561ef, 0x4561fa, 0x456242 ]), set([ 0x456190, 0x4561ba, 0x456202, 0x45621c, 0x40aa97, 0x4561ea, 0x4561fa, 0x456242 ]), set([0x456190, 0x456242]), ] pathcount = 0 genr = v_t_graphutil.getCodePaths(g, loopcnt=0, maxpath=None) for path in genr: p = set(map(lambda k: k[0], path)) self.assertIn(p, paths) pathcount += 1 self.assertEqual(12, pathcount) g = v_t_graphutil.buildFunctionGraph(vw, vw.getFunction(0x0041a766)) thruCnt = glen(v_t_graphutil.getCodePathsThru(g, 0x0041a766)) self.assertEqual(2, thruCnt) thruCnt = glen(v_t_graphutil.getCodePathsThru(g, 0x0041a766, maxpath=1)) self.assertEqual(1, thruCnt) # this will not be true for all examples, but for this function it is g = v_t_graphutil.buildFunctionGraph(vw, vw.getFunction(0x0041a77d)) toCnt = glen(v_t_graphutil.getCodePathsTo(g, 0x0041a77d)) self.assertEqual(2, toCnt) toCnt = glen(v_t_graphutil.getCodePathsTo(g, 0x0041a77d, maxpath=99)) self.assertEqual(2, toCnt) g = v_t_graphutil.buildFunctionGraph(vw, vw.getFunction(0x004042eb)) fromCnt = glen(v_t_graphutil.getCodePathsFrom(g, 0x004042eb)) self.assertEqual(8, fromCnt) fromCnt = glen(v_t_graphutil.getCodePathsFrom(g, 0x004042eb, maxpath=3)) self.assertEqual(3, fromCnt)