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