''' Various codeflow oriented graph constructs. ''' import envi import visgraph.graphcore as v_graphcore import vivisect.const as v_const class CallGraph(v_graphcore.HierGraph): ''' A graph which represents procedural branches. ''' def __init__(self): super().__init__() def getFunctionNode(self, va): node = self.getNode(va) if node is None: node = self.addNode(nid=va) return node def getCallEdge(self, f1va, f2va): f1 = self.getFunctionNode(f1va) # deconflict call graph edges... for edge in self.getRefsFrom(f1): if edge[2] == f2va: return edge f2 = self.getFunctionNode(f2va) return self.addEdge(f1, f2) class CodeBlockGraph(v_graphcore.HierGraph): def __init__(self, vw): super().__init__() self.vw = vw self.nodevas = {} def addEntryPoint(self, va): node = self.getNode(va) if node is not None: return node # entry point, by de-facto has a node enode = self.getCodeBlockNode(va) done = set() todo = [va,] while todo: va = todo.pop() if va in done: continue done.add(va) branches = self._getCodeBranches(va) tdone = set() for tova, bflags in branches: if tova in tdone: continue tdone.add(tova) node = self.getNodeByVa(va) if self._addCodeBranch(node, va, tova, bflags): todo.append(tova) return enode def _getCodeBranches(self, va): loc = self.vw.getLocation(va) if loc is None or loc[v_const.L_LTYPE] != v_const.LOC_OP: return [] lva, lsize, ltype, ltinfo = loc xrefs = self.vw.getXrefsFrom(va, rtype=v_const.REF_CODE) crefs = [(xto, xflags) for (xfrom, xto, xtype, xflags) in xrefs] # If any of our other branches are conditional, so is our fall if not ltinfo & envi.IF_NOFALL: bflags = envi.BR_FALL if any([(x[3] & envi.BR_COND) for x in xrefs]): bflags |= envi.BR_COND crefs.append((lva+lsize, bflags)) return crefs def _addCodeBranch(self, node, va, brva, bflags): if self.isCodeBlockNode(brva): self.addCodeBlockEdge(node, va, brva) return True if bflags & envi.BR_FALL and not bflags & envi.BR_COND: self.addVaToNode(node, brva) return True if bflags & envi.BR_DEREF: # FIXME handle these return False n2node = self.addCodeBlockEdge(node, va, brva) if bflags & envi.BR_PROC: self.setNodeProp(n2node, 'isfunc', True) return True def isCodeBlockNode(self, va): return self.getNode(va) is not None def getCodeBlockBounds(self, node): cbva = node[0] lastva = node[1]['valist'][-1] cbsize = (lastva - cbva) + 1 return cbva, cbsize def getCodeBlockNode(self, va): ''' Create or retrieve a codeblock node for the given va. NOTE: If the given va is already present within another node, this API will *split* the other node. ''' # is it already a cb node? node = self.getNode(va) if node is not None: return node # is it part of another block already? node = self.getNodeByVa(va) newnode = self.addNode(nid=va, cbva=va, valist=()) self.addVaToNode(newnode, va) if node is None: return newnode # we need to split an existing node... neato... valist = node[1]['valist'] vaidx = valist.index(va) vabeg = valist[:vaidx] vaend = valist[vaidx:] lastva = vabeg[-1] self.setNodeVaList(node, vabeg) self.setNodeVaList(newnode, vaend) # steal all his outbound codeflow edges for edge in self.getRefsFrom(node): codeflow = edge[3].get('codeflow') if codeflow is None: continue self.addCodeBlockEdge(newnode, codeflow[0], codeflow[1]) self.delEdge(edge) # add an outbound to us... self.addCodeBlockEdge(node, lastva, va) return newnode def addCodeBlockEdge(self, node1, va1, va2): vatup = (va1, va2) edges = self.getEdgesByProp('codeflow', vatup) if len(edges): return edges[0] node2 = self.getCodeBlockNode(va2) edge = self.addEdge(node1, node2) self.setEdgeProp(edge, 'va1', va1) self.setEdgeProp(edge, 'va2', va2) self.setEdgeProp(edge, 'codeflow', vatup) #w1 = node1[1].get('weight',0) #w2 = node2[1].get('weight',0) # track weights in real time ( per func? ) #self.setNodeProp(node2,'weight',max(w2,w1+1)) return node2 def addVaToNode(self, node, va): self.nodevas[va] = node valist = node[1]['valist'] self.setNodeProp(node, 'valist', valist + (va,)) def setNodeVaList(self, node, valist): [self.nodevas.pop(va,None) for va in node[1]['valist']] [self.nodevas.__setitem__(va,node) for va in valist] self.setNodeProp(node, 'valist', valist) def getNodeByVa(self, va): return self.nodevas.get(va) class FuncBlockGraph(CodeBlockGraph): def __init__(self, vw, fva): CodeBlockGraph.__init__(self, vw) root = self.addEntryPoint(fva) self.setHierRootNode(root) def _getCodeBranches(self, va): return [x for x in CodeBlockGraph._getCodeBranches(self,va) if not x[1] & envi.BR_PROC]