Package Libs :: Module immvcglib
[hide private]
[frames] | no frames]

Source Code for Module Libs.immvcglib

   1  #!/usr/bin/env python 
   2   
   3  """ 
   4  Reads vcg buffer and creates the graph using Immunity Debugger lib 
   5   
   6  (c) Immunity, Inc. 2004-2007 
   7   
   8   
   9  U{Immunity Inc.<http://www.immunityinc.com>} 
  10   
  11   
  12  """ 
  13   
  14  __VERSION__ = '1.2' 
  15   
  16   
  17  """ 
  18  NOTES: 
  19  need to divide graph in layers 
  20  save max layer in graph 
  21  every set of childs [unique and different part vertex] E a different layer 
  22  save vertex of layer in each layer 
  23  mark blank path points in each layer [i preffer path points to dummy vertices]  
  24   
  25  for layer in layers: 
  26     move east and west vertices, depending on their type * 
  27   
  28  pathfinder(graph) 
  29    search empy spots where edge lines might travel 
  30     
  31     
  32  a cool thing might be mark the whole graph as east-slanted or west-slanted, according the graph 
  33  the n east or n west it will move 
  34   
  35  if the graph is slanting too much to east from center point, we can start thinking on going west 
  36  that can be too fuzzy, but will try to make an aproach for human eye 
  37   
  38   
  39  new lib against old lib: 
  40  orphan vertices from old lib has been solved, now every vertex has at least 1 relationship saved  
  41  parent<->child type of vertex are correctly relationed now 
  42   
  43  """ 
  44   
  45  import graphclass 
  46   
  47  import immlib 
  48  import debugger 
  49  #chaos is our friend  
  50  # XXX: Sure .. but how does chaos theory relate to random human interaction? 
  51  # XXX: Chance meetings that ultimately end up derailing your life .. 
  52  # XXX: The butterfly effect of hello's .. I don't know .. do you? 
  53  from random import randint 
  54   
  55  # default GRAPH palette 
  56  PALETTE = [] 
  57   
  58  PALETTE.append("manhattan_edges: yes\r\n") 
  59  PALETTE.append("layoutalgorithm: mindepth\r\n") 
  60  PALETTE.append("finetuning: no\r\n") 
  61  PALETTE.append("layout_downfactor: 100\r\n") 
  62  PALETTE.append("layout_upfactor: 0\r\n") 
  63  PALETTE.append("layout_nearfactor: 0\r\n") 
  64  PALETTE.append("xlspace: 12\r\n") 
  65  PALETTE.append("yspace: 30\r\n") 
  66  PALETTE.append("colorentry 32: 0 0 0\r\n") 
  67  PALETTE.append("colorentry 33: 0 0 255\r\n") 
  68  PALETTE.append("colorentry 34: 0 0 255\r\n") 
  69  PALETTE.append("colorentry 35: 128 128 128\r\n") 
  70  PALETTE.append("colorentry 36: 128 128 128\r\n") 
  71  PALETTE.append("colorentry 37: 0 0 128\r\n") 
  72  PALETTE.append("colorentry 38: 0 0 128\r\n") 
  73  PALETTE.append("colorentry 39: 0 0 255\r\n") 
  74  PALETTE.append("colorentry 40: 0 0 255\r\n") 
  75  PALETTE.append("colorentry 41: 0 0 128\r\n") 
  76  PALETTE.append("colorentry 42: 0 128 0\r\n") 
  77  PALETTE.append("colorentry 43: 0 255 0\r\n") 
  78  PALETTE.append("colorentry 44: 0 128 0\r\n") 
  79  PALETTE.append("colorentry 45: 255 128 0\r\n") 
  80  PALETTE.append("colorentry 46: 0 128 0\r\n") 
  81  PALETTE.append("colorentry 47: 128 128 255\r\n") 
  82  PALETTE.append("colorentry 48: 255 0 0\r\n") 
  83  PALETTE.append("colorentry 49: 128 128 0\r\n") 
  84  PALETTE.append("colorentry 50: 1 1 1\r\n") 
  85  PALETTE.append("colorentry 51: 192 192 192\r\n") 
  86  PALETTE.append("colorentry 52: 0 0 255\r\n") 
  87  PALETTE.append("colorentry 53: 0 0 255\r\n") 
  88  PALETTE.append("colorentry 54: 0 0 255\r\n") 
  89  PALETTE.append("colorentry 55: 128 128 128\r\n") 
  90  PALETTE.append("colorentry 56: 128 128 255\r\n") 
  91  PALETTE.append("colorentry 57: 0 128 0\r\n") 
  92  PALETTE.append("colorentry 58: 0 0 128\r\n") 
  93  PALETTE.append("colorentry 59: 0 0 255\r\n") 
  94  PALETTE.append("colorentry 60: 128 0 128\r\n") 
  95  PALETTE.append("colorentry 61: 0 128 0\r\n") 
  96  PALETTE.append("colorentry 62: 0 128 0\r\n") 
  97  PALETTE.append("colorentry 63: 0 128 64\r\n") 
  98  PALETTE.append("colorentry 64: 0 0 128\r\n") 
  99  PALETTE.append("colorentry 65: 0 0 128\r\n") 
 100  PALETTE.append("colorentry 66: 255 0 255\r\n") 
 101  PALETTE.append("colorentry 67: 128 128 0\r\n") 
 102  PALETTE.append("colorentry 68: 0 0 128\r\n") 
 103  PALETTE.append("colorentry 69: 0 0 255\r\n") 
 104  PALETTE.append("colorentry 70: 0 0 128\r\n") 
 105  PALETTE.append("colorentry 71: 0 0 255\r\n") 
 106  PALETTE.append("colorentry 72: 0 0 0\r\n") 
 107  PALETTE.append("colorentry 73: 255 255 255\r\n") 
 108  PALETTE.append("colorentry 74: 192 192 192\r\n") 
 109  PALETTE.append("colorentry 75: 0 255 255\r\n") 
 110  PALETTE.append("colorentry 76: 0 0 0\r\n") 
 111  PALETTE.append("colorentry 77: 128 0 0\r\n") 
 112  PALETTE.append("colorentry 78: 128 128 128\r\n") 
 113  PALETTE.append("colorentry 79: 128 128 0\r\n") 
 114  PALETTE.append("colorentry 80: 255 0 255\r\n") 
 115  PALETTE.append("colorentry 81: 0 0 0\r\n") 
 116  PALETTE.append("colorentry 82: 0 0 255\r\n") 
 117  PALETTE.append("colorentry 83: 0 0 0\r\n") 
 118   
119 -class graphTree:
120 # address to call tree from, ID immlib.Debugger() object
121 - def __init__(self, address, imm):
122 """ Init the graphing object """ 123 self.imm = imm 124 self.callTree = imm.getCallTree(address) 125 self.address = address
126
127 - def orderNodesFromTree(self):
128 """ return a call ordered list of nodes """ 129 130 # call[0] -> line number in column 131 # call[1] -> dummy (must be 1) 132 # call[2] -> type (set of TY_xxx) 133 # call[3] -> entry (address of function) 134 # call[4] -> from (address of calling function) 135 # call[5] -> calls to (address of called subfunction) 136 137 # so really for now we just do up and down for the first entry 138 TARGET = [] 139 PARENTS = [] 140 CHILDREN = [] 141 142 for call in self.callTree: 143 if call[3]: 144 if "0x%X"%call[3] not in TARGET: 145 TARGET.append("0x%X"% call[3]) 146 if call[4]: 147 if "0x%X"%call[4] not in PARENTS: 148 PARENTS.append("0x%X"% call[4]) 149 if call[5]: 150 if "0x%X"%call[5] not in CHILDREN: 151 CHILDREN.append("0x%X"% call[5]) 152 153 return TARGET, PARENTS, CHILDREN
154
155 - def makeNode(self, title, content = "", vertical_order = 0):
156 """ build a simple node VCG buf entry """ 157 node = [] 158 node.append('node: {\r\n') 159 node.append('title: "%s"\r\n'% title) 160 node.append('vertical_order: %d\r\n'% vertical_order) 161 if content != "": 162 node.append('label: "\x0c69%s\x0c31\r\n%s"\r\n'% (title, content)) 163 else: 164 node.append('label: "\x0c69%s\x0c31\r\n'% title) 165 node.append('}\r\n') 166 return node
167
168 - def makeEdge(self, source, target, label = "", color = "green"):
169 """ work out the relations between the boxies """ 170 # we call these 'edges', edges basically connect the boxies 171 edge = [] 172 173 edge.append('edge: {\r\n') 174 edge.append('sourcename: "%s"\r\n'% source) 175 edge.append('targetname: "%s"\r\n'% target) 176 if label != "": 177 edge.append('label: "%s"\r\n'% label) 178 edge.append('color: %s\r\n'% color) 179 edge.append('}\r\n') 180 181 return edge
182
183 - def makeVCG(self, title, nodes = [], edges = []):
184 """ build a simple node tree VCG buffer """ 185 vcg = [] 186 187 vcg.append('graph: {\r\n') 188 # XXX: dummy title (0xaddress) so parser doesn't choke .. fix that 189 vcg.append('title: "%s"\r\n'% title) 190 191 # add default palette 192 for line in PALETTE: 193 vcg.append(line) 194 195 # add nodes, nodes is a list of node entries 196 for node in nodes: 197 for line in node: 198 vcg.append(line) 199 200 # work out the relations from the call tree 201 for edge in edges: 202 for line in edge: 203 vcg.append(line) 204 205 # close the graph 206 vcg.append('}\r\n') 207 208 return vcg
209
210 - def graphCallTree(self):
211 """ pop up a call tree graph for this address """ 212 TARGET, PARENTS, CHILDREN = self.orderNodesFromTree() 213 214 nodes = [] 215 unique = [] 216 # make sure we don't double up on nodes .. 217 for title in TARGET+PARENTS+CHILDREN: 218 if title not in unique: 219 unique.append(title) 220 221 # make nodes for all the entries 222 for title in unique: 223 224 if title in PARENTS: 225 order = 0 226 if title in TARGET: 227 order = 1 228 if title in CHILDREN: 229 order = 2 230 231 # try to resolve to symbol using decodeAddress() 232 node_content = self.imm.decodeAddress(int(title, 16)) 233 nodes.append(self.makeNode(title, content = node_content, vertical_order = order)) 234 235 edges = [] 236 # we want to connect all the parents to the target and the target to all the children 237 target = TARGET[0] 238 239 for parent in PARENTS: 240 ### makeEdge(source-node, target-node) 241 edges.append(self.makeEdge(parent, target)) 242 for child in CHILDREN: 243 edges.append(self.makeEdge(target, child)) 244 245 # make the main VCG 246 vcg = self.makeVCG("Call Graph <-for-> %s [0x%X]"% (self.imm.decodeAddress(self.address), self.address), nodes, edges) 247 248 # XXX: debug write out 249 fd = open("CALLTREE.vcg", "w") 250 for line in vcg: 251 fd.write(line) 252 fd.close() 253 254 # pop up the MDI window 255 generateGraphFromBuf(vcg)
256
257 -class ParseVCGList:
258 """ recursive VCG parser """ 259
260 - def __init__(self, vcgList):
261 """ pre-process our shiznit """ 262 self.sep = '!SEP!' 263 self.DEBUG = False 264 265 # XXX: need to implement the full VCG grammar at some point 266 # XXX: also see http://www.penguin-soft.com/penguin/man/1/vcg.html 267 268 # WHEN MOVING TO MORE COMPLEX VCG, ADD FUNCTIONALITY _HERE_ 269 self.MODETOKENS = [ 'graph:', 'node:', 'edge:' ] 270 271 self.VARTOKENS = [ 'title:', 'label:', 'vertical_order:', 'horizontal_order:', 'manhattan_edges:', 'layoutalgorithm:' ] 272 self.VARTOKENS += [ 'finetuning:', 'layout_downfactor:', 'layout_upfactor:' ] 273 self.VARTOKENS += [ 'layout_nearfactor:', 'xlspace:', 'yspace:' ] 274 self.VARTOKENS += [ 'sourcename:', 'targetname:', 'color:' ] 275 276 # strip comment lines ... 277 cleanVCG = [] 278 # in string mode we don't want to replace .. 279 sMode = False 280 281 for line in vcgList: 282 283 # skip comments ... 284 if line[:2] == "//": 285 continue 286 287 clean = [] 288 lineList = list(line) 289 290 for c in lineList: 291 if c == '"': 292 # flip pre-process mode 293 sMode = not sMode 294 295 if sMode == True: # string mode open 296 clean.append(c) 297 else: 298 if c in ['\r']: # stripped chars .. 299 continue 300 if c in ['\n', ' ']: 301 clean.append(self.sep) 302 else: 303 clean.append(c) 304 305 line = ''.join(clean) 306 307 if len(line): 308 cleanVCG.append(line) 309 310 self.vcgText = ''.join(cleanVCG) 311 312 self.nodeList = [] 313 self.edgeList = [] 314 self.graphList = [] 315 316 self.lastMode = ""
317
318 - def error(self, error):
319 """ raise an error exception """ 320 raise error
321
322 - def reParse(self, vcgItems, mode = ""):
323 """ used for recursive parse """ 324 325 # DEBUG LOGS 326 if self.DEBUG: 327 logger = immlib.Debugger() 328 logger.Log(repr(vcgItems)) 329 330 # if not empty == True .. recursive calls .. bla bla 331 if vcgItems: 332 333 if vcgItems[0] in self.MODETOKENS: 334 mode = vcgItems[0] 335 self.lastMode = mode 336 self.reParse(vcgItems[1:], mode = mode) 337 338 elif vcgItems[0] in self.VARTOKENS or 'colorentry' in vcgItems[0]: 339 340 ### Special case color entry ... 341 if 'colorentry' in vcgItems[0]: 342 vcgItems[0] = " ".join([vcgItems[0], vcgItems[1]]) 343 del vcgItems[1] 344 345 args = [] 346 key = vcgItems[0] 347 348 i = 1 349 while vcgItems[i] not in self.VARTOKENS and vcgItems[i] not in self.MODETOKENS and 'colorentry' not in vcgItems[i]: 350 if '}' in vcgItems[i]: 351 break 352 args.append(vcgItems[i]) 353 i += 1 354 355 if mode == 'node:' and len(self.nodeList): 356 self.nodeList[len(self.nodeList)-1][key] = " ".join(args) 357 358 if mode == 'edge:' and len(self.edgeList): 359 self.edgeList[len(self.edgeList)-1][key] = " ".join(args) 360 361 if mode == 'graph:' and len(self.graphList): 362 self.graphList[len(self.graphList)-1][key] = " ".join(args) 363 364 self.reParse(vcgItems[i:], mode = self.lastMode) 365 366 elif '{' in vcgItems[0]: 367 368 # decide if mode needs a new dict .. or if it's just a pair: val 369 if mode == 'graph:': 370 self.graphList.append({}) 371 elif mode == 'node:': 372 self.nodeList.append({}) 373 elif mode == 'edge:': 374 self.edgeList.append({}) 375 376 self.reParse(vcgItems[1:], mode = mode) 377 378 # close control block, go up one mode 379 elif '}' in vcgItems[0]: 380 self.reParse(vcgItems[1:], mode = '') 381 382 # all done .. 383 return self.graphList, self.nodeList, self.edgeList
384
385 - def parseGraph(self):
386 """ Parse a VCG graph .. not 100% proper .. but proper enough """ 387 vcgItems = self.vcgText.split(self.sep) 388 return self.reParse(vcgItems)
389
390 -def testVCGParse(path):
391 """ test our new VCG parsing logic """ 392 vcgList = [] 393 394 fd = open(path, 'r') 395 for line in fd: 396 vcgList.append(line) 397 fd.close() 398 399 parser = ParseVCGList(vcgList) 400 401 # these are lists of dicts :> so 1 dict per node/edge/graph 402 graph, nodes, edges = parser.parseGraph() 403 404 logger = immlib.Debugger() 405 406 logger.Log("GRAPH:") 407 for gDict in graph: 408 for key in gDict: 409 logger.Log("KeyVal: %s"% key) 410 logger.Log(repr(gDict[key])) 411 logger.Log("EDGES:") 412 for eDict in edges: 413 for key in eDict: 414 logger.Log("KeyVal: %s"% key) 415 logger.Log(repr(eDict[key])) 416 logger.Log("NODES:") 417 for nDict in nodes: 418 for key in nDict: 419 logger.Log("KeyVal: %s"% key) 420 logger<