Files
vivisect-vivisect/vivisect/codegraph.py
T
James Gross 80de840881 Even More Syntax Cleanup (#293)
A lot of cleanup things in prep for a python 3 transition. Getting rid of the old exception syntax, converting prints over to logging, cutting random scraps of code to be proper unit tests,  cut away some older bits of code, etc.

This still works in python2. It's just a lot of tidying up. There are no major functionality changes.
2020-09-08 13:00:06 -04:00

212 lines
5.7 KiB
Python

'''
Various codeflow oriented graph constructs.
'''
import envi
import visgraph.graphcore as v_graphcore
from vivisect.const import *
class CallGraph(v_graphcore.HierGraph):
'''
A graph which represents procedural branches.
'''
def __init__(self):
v_graphcore.Graph.__init__(self)
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):
v_graphcore.Graph.__init__(self)
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[L_LTYPE] != LOC_OP:
return []
lva,lsize,ltype,ltinfo = loc
xrefs = self.vw.getXrefsFrom(va, rtype=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]
newlastva = vaend[-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 ]