mirror of
https://github.com/vivisect/vivisect
synced 2026-06-08 18:04:23 +00:00
214 lines
5.7 KiB
Python
214 lines
5.7 KiB
Python
'''
|
|
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]
|