├── README.md ├── XTreeIDAPro ├── XTreeIDAPro.py ├── d3 │ ├── d3.js │ ├── d3.layout.js │ └── d3.v3.min.js ├── index.html └── style.css ├── autoname_functions.py ├── compile_hexrays.py ├── fix_function_detection_on_arm.py ├── ida_hexrays.h ├── kallsyms_to_idc.py └── xrefs_trees.py /README.md: -------------------------------------------------------------------------------- 1 | idatools 2 | ======== 3 | 4 | Tools for IDA 5 | 6 | 7 | 8 | autorename_functions.py 9 | ----------------------- 10 | 11 | **Backup your IDB first** 12 | 13 | Will name your functions and data based on strings and other symbols using a Markov model. All names are prefixed with ***'z_'*** . Only symbols that 14 | aren't already named will be changed. For example for functions, only names that starts with ***'sub_'*** will change. 15 | 16 | First step, it renames all the strings to something sane. I'm not a fan of IDA's aCamelCaseNaming of strings. Unlike IDA, 17 | this won't truncate a string until 256 bytes and spaces are replaced with '_' instead of an empty string. 18 | 19 | Second, it finds unique strings references by a function and renames the function based on 20 | the string. This is useful if you have functions that log, but don't have symbols. It will 21 | take the strings references in the log calls and rename your functions. I need some better heuristics 22 | in the future for this. String based functions are prefixed with 'z_' 23 | 24 | ###Markov Model 25 | It then builds a Markov model of the cross reference graph between data and code. The number of cross references over the total number of outgoing edges is used to 26 | approximate the probability of the model. This weights which names to generate. An arbitrary cutoff of 0.5 was picked, but you can adjust 27 | this by changing ***PROBABILITY_CUTTOFF*** at the top of the file. Any named Xref that is >= the cutoff is used as the name of the symbol. 28 | 29 | Once the model is built, it will continue through passes of data and code, renaming as it goes, until there is nothing left to rename. 30 | 31 | 32 | XTreeIDAPro 33 | ----------- 34 | 35 | Experimental, but builds a tree view of function calls using javascript D3. 36 | 37 | xref_trees.py 38 | ------------- 39 | 40 | Builds up a callgraph of cross references to and from a function and spits it out as a text... 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /XTreeIDAPro/XTreeIDAPro.py: -------------------------------------------------------------------------------- 1 | # xref_trees.py 2 | # 3 | # This generates callgraph trees to and from the current function. 4 | # It will ignore allocs and any 5 | # functions that start with zzz_ . You can add other functions you want to 6 | # ignore in BLACKLIST 7 | # 8 | # Weston Hopkins 9 | # August 2014 10 | # 11 | 12 | from idaapi import * 13 | from idautils import * 14 | import json 15 | import sys 16 | import SimpleHTTPServer 17 | import BaseHTTPServer 18 | import SocketServer 19 | import os.path 20 | import webbrowser 21 | 22 | functionAddresses = {} 23 | 24 | 25 | BLACKLIST = ['alloc', 'zzz_'] 26 | 27 | 28 | def demangledName(addr): 29 | name = Name(addr) 30 | testName = Demangle( name, INF_LONG_DN) 31 | if testName: 32 | name = testName 33 | return name 34 | 35 | ###################################################################### 36 | ###################################################################### 37 | class XTree(dict): 38 | 39 | def __init__(self, name, addr): 40 | self["name"] = name 41 | self["children"] = [] 42 | self["addr"] = addr 43 | self["size"] = 0 44 | 45 | 46 | def add(self, kidTree): 47 | self["children"].append(kidTree) 48 | self["size"] += 1 49 | 50 | 51 | ###################################################################### 52 | # XTreeServer 53 | ###################################################################### 54 | class XTreeServer(SimpleHTTPServer.SimpleHTTPRequestHandler): 55 | 56 | def do_GET(self): 57 | if self.path == "/xtree.json": 58 | self.send_response(200) 59 | self.send_header('Content-type', 'application/javascript') 60 | self.end_headers() 61 | self.wfile.write(self.server.xtree_json) 62 | self.server.finished = True 63 | else: 64 | return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) 65 | 66 | 67 | 68 | 69 | ###################################################################### 70 | # dumpShit() 71 | ###################################################################### 72 | def dumpShit(obj, name=""): 73 | return 74 | Message( "%s: %s\n" % (name, obj )) 75 | for attr in dir(obj): 76 | val = None 77 | try: 78 | val = eval( "obj." + attr ) 79 | except: 80 | pass 81 | Message( "\t%s: \t%s\n" % (attr, val)) 82 | 83 | 84 | ###################################################################### 85 | # dumpXrefsFrom() 86 | ###################################################################### 87 | def dumpXrefsFrom( pc, callStack, functionCallCounts, parentTree ): 88 | func = get_func(pc) 89 | 90 | if func is None: 91 | print( "0x%08x is not at a function\n" % pc ) 92 | return 93 | 94 | func = get_func(func.startEA) 95 | dumpShit(func) 96 | functionName = demangledName(func.startEA) 97 | 98 | for blackList in BLACKLIST: 99 | if( blackList in functionName ): 100 | return 101 | if( functionName in callStack ): 102 | return 103 | else: 104 | callStack.append(functionName) 105 | 106 | if( not functionName in functionCallCounts ): 107 | functionCallCounts[functionName] = 0 108 | 109 | functionCallCounts[functionName] = functionCallCounts[functionName] + 1 110 | functionCallCount = functionCallCounts[functionName] 111 | tree = XTree( functionName, func.startEA ) 112 | parentTree.add(tree) 113 | 114 | prefix = " |" * len(callStack) 115 | Message( prefix[:-1] + "+ " + functionName + "()" ) 116 | 117 | if( functionCallCount>1 ): 118 | Message(" ... [%d]\n" % functionCallCount ) 119 | else: 120 | Message("\n") 121 | functionCallCounts[functionName] = True 122 | items = FuncItems( func.startEA ) 123 | xrefs = [] 124 | for i in items: 125 | for xref in XrefsFrom(i, 0): 126 | if xref.type==fl_CN or xref.type==fl_CF or xref.type==fl_JF or xref.type==fl_JN: 127 | xrefName = str(xref.to) 128 | if( not xrefName in xrefs ): 129 | dumpXrefsFrom( xref.to, list(callStack), functionCallCounts, tree ) 130 | xrefs.append(xrefName) 131 | 132 | 133 | 134 | ###################################################################### 135 | # dumpXrefsFrom() 136 | ###################################################################### 137 | def generateCallsJSONTree( pc, callStack, functionCallCounts ): 138 | func = get_func(pc) 139 | 140 | if func is None: 141 | # print( "0x%08x is not at a function\n" % pc ) 142 | return 143 | 144 | func = get_func(func.startEA) 145 | dumpShit(func) 146 | functionName = demangledName(func.startEA) 147 | if( functionName[0] == '_' ): 148 | return 149 | for blackList in BLACKLIST: 150 | if( blackList in functionName ): 151 | return 152 | if( functionName in callStack ): 153 | return 154 | else: 155 | callStack.append(functionName) 156 | 157 | if( not functionName in functionCallCounts ): 158 | functionCallCounts[functionName] = 0 159 | 160 | functionCallCounts[functionName] = functionCallCounts[functionName] + 1 161 | functionCallCount = functionCallCounts[functionName] 162 | 163 | prefix = " |" * len(callStack) 164 | Message( prefix[:-1] + "+ " + functionName + "()" ) 165 | 166 | if( functionCallCount>1 ): 167 | Message(" ... [%d]\n" % functionCallCount ) 168 | else: 169 | Message("\n") 170 | functionCallCounts[functionName] = True 171 | items = FuncItems( func.startEA ) 172 | xrefs = [] 173 | for i in items: 174 | for xref in XrefsFrom(i, 0): 175 | if xref.type==fl_CN or xref.type==fl_CF or xref.type==fl_JF or xref.type==fl_JN: 176 | xrefName = str(xref.to) 177 | if( not xrefName in xrefs ): 178 | dumpXrefsFrom( xref.to, list(callStack), functionCallCounts ) 179 | xrefs.append(xrefName) 180 | 181 | 182 | 183 | ###################################################################### 184 | # dumpXrefsTo() 185 | ###################################################################### 186 | def dumpXrefsTo( pc, callStack, functionCallCounts ): 187 | func = get_func(pc) 188 | 189 | if func is None: 190 | # print( "0x%08x is not at a function\n" % pc ) 191 | return 192 | 193 | func = get_func(func.startEA) 194 | dumpShit(func) 195 | functionName = demangledName(func.startEA) 196 | if( functionName[0] == '_' ): 197 | return 198 | for blackList in BLACKLIST: 199 | if( blackList in functionName ): 200 | return 201 | if( functionName in callStack ): 202 | return 203 | else: 204 | callStack.append(functionName) 205 | 206 | if( not functionName in functionCallCounts ): 207 | functionCallCounts[functionName] = 0 208 | 209 | functionCallCounts[functionName] = functionCallCounts[functionName] + 1 210 | functionCallCount = functionCallCounts[functionName] 211 | 212 | prefix = " |" * len(callStack) 213 | Message( prefix[:-1] + "+ " + functionName + "()" ) 214 | 215 | if( functionCallCount>1 ): 216 | Message(" ... [%d]\n" % functionCallCount ) 217 | else: 218 | Message("\n") 219 | functionCallCounts[functionName] = True 220 | xrefs = [] 221 | 222 | for xref in XrefsTo(func.startEA, 0 ): 223 | if xref.type==fl_CN or xref.type==fl_CF or xref.type==fl_JF or xref.type==fl_JN: 224 | xrefName = str(xref.frm) 225 | if( not xrefName in xrefs ): 226 | dumpXrefsTo( xref.frm, list(callStack), functionCallCounts ) 227 | xrefs.append(xrefName) 228 | 229 | 230 | def startServer(tree): 231 | 232 | homedir = os.path.dirname( sys.argv[0] ) 233 | os.chdir(homedir) 234 | print("Starting server in %s" % homedir ) 235 | print(" go to: http://localhost:6969") 236 | webbrowser.open("http://localhost:6969" ) 237 | httpd = BaseHTTPServer.HTTPServer( ("127.0.0.1", 6969), XTreeServer ) 238 | httpd.finished = False 239 | httpd.xtree_json = json.dumps(tree) 240 | while not httpd.finished: 241 | try: 242 | httpd.handle_request() 243 | except KeyboardInterrupt: 244 | httpd.finished = True 245 | httpd.server_close() 246 | print("Done") 247 | 248 | def main(): 249 | 250 | print sys.argv 251 | 252 | functionEA = ChooseFunction("Select function to generate graph") 253 | if BADADDR == functionEA: 254 | print("No function selected. exiting... ") 255 | return 256 | 257 | Message( "=" * 80 + "\n" ) 258 | Message("Cross References From\n") 259 | Message( "=" * 80 + "\n" ) 260 | name = demangledName(functionEA) 261 | tree = XTree(name, functionEA) 262 | dumpXrefsFrom(functionEA, [], {}, tree ) 263 | 264 | startServer(tree) 265 | 266 | 267 | # filename = "xtree.json" 268 | # Message("Writing tree to %s.\n" % filename) 269 | # f = file(filename, "w+") 270 | # f.write(json.dumps(tree)) 271 | # f.close() 272 | 273 | 274 | main() 275 | 276 | # Message( "=" * 80 + "\n" ) 277 | # Message("Cross References To\n") 278 | # Message( "=" * 80 + "\n" ) 279 | # dumpXrefsTo(functionEA, [], {} ) 280 | -------------------------------------------------------------------------------- /XTreeIDAPro/d3/d3.layout.js: -------------------------------------------------------------------------------- 1 | (function(){d3.layout = {}; 2 | // Implements hierarchical edge bundling using Holten's algorithm. For each 3 | // input link, a path is computed that travels through the tree, up the parent 4 | // hierarchy to the least common ancestor, and then back down to the destination 5 | // node. Each path is simply an array of nodes. 6 | d3.layout.bundle = function() { 7 | return function(links) { 8 | var paths = [], 9 | i = -1, 10 | n = links.length; 11 | while (++i < n) paths.push(d3_layout_bundlePath(links[i])); 12 | return paths; 13 | }; 14 | }; 15 | 16 | function d3_layout_bundlePath(link) { 17 | var start = link.source, 18 | end = link.target, 19 | lca = d3_layout_bundleLeastCommonAncestor(start, end), 20 | points = [start]; 21 | while (start !== lca) { 22 | start = start.parent; 23 | points.push(start); 24 | } 25 | var k = points.length; 26 | while (end !== lca) { 27 | points.splice(k, 0, end); 28 | end = end.parent; 29 | } 30 | return points; 31 | } 32 | 33 | function d3_layout_bundleAncestors(node) { 34 | var ancestors = [], 35 | parent = node.parent; 36 | while (parent != null) { 37 | ancestors.push(node); 38 | node = parent; 39 | parent = parent.parent; 40 | } 41 | ancestors.push(node); 42 | return ancestors; 43 | } 44 | 45 | function d3_layout_bundleLeastCommonAncestor(a, b) { 46 | if (a === b) return a; 47 | var aNodes = d3_layout_bundleAncestors(a), 48 | bNodes = d3_layout_bundleAncestors(b), 49 | aNode = aNodes.pop(), 50 | bNode = bNodes.pop(), 51 | sharedNode = null; 52 | while (aNode === bNode) { 53 | sharedNode = aNode; 54 | aNode = aNodes.pop(); 55 | bNode = bNodes.pop(); 56 | } 57 | return sharedNode; 58 | } 59 | d3.layout.chord = function() { 60 | var chord = {}, 61 | chords, 62 | groups, 63 | matrix, 64 | n, 65 | padding = 0, 66 | sortGroups, 67 | sortSubgroups, 68 | sortChords; 69 | 70 | function relayout() { 71 | var subgroups = {}, 72 | groupSums = [], 73 | groupIndex = d3.range(n), 74 | subgroupIndex = [], 75 | k, 76 | x, 77 | x0, 78 | i, 79 | j; 80 | 81 | chords = []; 82 | groups = []; 83 | 84 | // Compute the sum. 85 | k = 0, i = -1; while (++i < n) { 86 | x = 0, j = -1; while (++j < n) { 87 | x += matrix[i][j]; 88 | } 89 | groupSums.push(x); 90 | subgroupIndex.push(d3.range(n)); 91 | k += x; 92 | } 93 | 94 | // Sort groups… 95 | if (sortGroups) { 96 | groupIndex.sort(function(a, b) { 97 | return sortGroups(groupSums[a], groupSums[b]); 98 | }); 99 | } 100 | 101 | // Sort subgroups… 102 | if (sortSubgroups) { 103 | subgroupIndex.forEach(function(d, i) { 104 | d.sort(function(a, b) { 105 | return sortSubgroups(matrix[i][a], matrix[i][b]); 106 | }); 107 | }); 108 | } 109 | 110 | // Convert the sum to scaling factor for [0, 2pi]. 111 | // TODO Allow start and end angle to be specified. 112 | // TODO Allow padding to be specified as percentage? 113 | k = (2 * Math.PI - padding * n) / k; 114 | 115 | // Compute the start and end angle for each group and subgroup. 116 | x = 0, i = -1; while (++i < n) { 117 | x0 = x, j = -1; while (++j < n) { 118 | var di = groupIndex[i], 119 | dj = subgroupIndex[i][j], 120 | v = matrix[di][dj]; 121 | subgroups[di + "-" + dj] = { 122 | index: di, 123 | subindex: dj, 124 | startAngle: x, 125 | endAngle: x += v * k, 126 | value: v 127 | }; 128 | } 129 | groups.push({ 130 | index: di, 131 | startAngle: x0, 132 | endAngle: x, 133 | value: (x - x0) / k 134 | }); 135 | x += padding; 136 | } 137 | 138 | // Generate chords for each (non-empty) subgroup-subgroup link. 139 | i = -1; while (++i < n) { 140 | j = i - 1; while (++j < n) { 141 | var source = subgroups[i + "-" + j], 142 | target = subgroups[j + "-" + i]; 143 | if (source.value || target.value) { 144 | chords.push(source.value < target.value 145 | ? {source: target, target: source} 146 | : {source: source, target: target}); 147 | } 148 | } 149 | } 150 | 151 | if (sortChords) resort(); 152 | } 153 | 154 | function resort() { 155 | chords.sort(function(a, b) { 156 | return sortChords(a.target.value, b.target.value); 157 | }); 158 | } 159 | 160 | chord.matrix = function(x) { 161 | if (!arguments.length) return matrix; 162 | n = (matrix = x) && matrix.length; 163 | chords = groups = null; 164 | return chord; 165 | }; 166 | 167 | chord.padding = function(x) { 168 | if (!arguments.length) return padding; 169 | padding = x; 170 | chords = groups = null; 171 | return chord; 172 | }; 173 | 174 | chord.sortGroups = function(x) { 175 | if (!arguments.length) return sortGroups; 176 | sortGroups = x; 177 | chords = groups = null; 178 | return chord; 179 | }; 180 | 181 | chord.sortSubgroups = function(x) { 182 | if (!arguments.length) return sortSubgroups; 183 | sortSubgroups = x; 184 | chords = null; 185 | return chord; 186 | }; 187 | 188 | chord.sortChords = function(x) { 189 | if (!arguments.length) return sortChords; 190 | sortChords = x; 191 | if (chords) resort(); 192 | return chord; 193 | }; 194 | 195 | chord.chords = function() { 196 | if (!chords) relayout(); 197 | return chords; 198 | }; 199 | 200 | chord.groups = function() { 201 | if (!groups) relayout(); 202 | return groups; 203 | }; 204 | 205 | return chord; 206 | }; 207 | // A rudimentary force layout using Gauss-Seidel. 208 | d3.layout.force = function() { 209 | var force = {}, 210 | event = d3.dispatch("tick"), 211 | size = [1, 1], 212 | drag, 213 | alpha, 214 | friction = .9, 215 | linkDistance = d3_layout_forceLinkDistance, 216 | linkStrength = d3_layout_forceLinkStrength, 217 | charge = -30, 218 | gravity = .1, 219 | theta = .8, 220 | interval, 221 | nodes = [], 222 | links = [], 223 | distances, 224 | strengths, 225 | charges; 226 | 227 | function repulse(node) { 228 | return function(quad, x1, y1, x2, y2) { 229 | if (quad.point !== node) { 230 | var dx = quad.cx - node.x, 231 | dy = quad.cy - node.y, 232 | dn = 1 / Math.sqrt(dx * dx + dy * dy); 233 | 234 | /* Barnes-Hut criterion. */ 235 | if ((x2 - x1) * dn < theta) { 236 | var k = quad.charge * dn * dn; 237 | node.px -= dx * k; 238 | node.py -= dy * k; 239 | return true; 240 | } 241 | 242 | if (quad.point && isFinite(dn)) { 243 | var k = quad.pointCharge * dn * dn; 244 | node.px -= dx * k; 245 | node.py -= dy * k; 246 | } 247 | } 248 | return !quad.charge; 249 | }; 250 | } 251 | 252 | function tick() { 253 | var n = nodes.length, 254 | m = links.length, 255 | q, 256 | i, // current index 257 | o, // current object 258 | s, // current source 259 | t, // current target 260 | l, // current distance 261 | k, // current force 262 | x, // x-distance 263 | y; // y-distance 264 | 265 | // gauss-seidel relaxation for links 266 | for (i = 0; i < m; ++i) { 267 | o = links[i]; 268 | s = o.source; 269 | t = o.target; 270 | x = t.x - s.x; 271 | y = t.y - s.y; 272 | if (l = (x * x + y * y)) { 273 | l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; 274 | x *= l; 275 | y *= l; 276 | t.x -= x * (k = s.weight / (t.weight + s.weight)); 277 | t.y -= y * k; 278 | s.x += x * (k = 1 - k); 279 | s.y += y * k; 280 | } 281 | } 282 | 283 | // apply gravity forces 284 | if (k = alpha * gravity) { 285 | x = size[0] / 2; 286 | y = size[1] / 2; 287 | i = -1; if (k) while (++i < n) { 288 | o = nodes[i]; 289 | o.x += (x - o.x) * k; 290 | o.y += (y - o.y) * k; 291 | } 292 | } 293 | 294 | // compute quadtree center of mass and apply charge forces 295 | if (charge) { 296 | d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges); 297 | i = -1; while (++i < n) { 298 | if (!(o = nodes[i]).fixed) { 299 | q.visit(repulse(o)); 300 | } 301 | } 302 | } 303 | 304 | // position verlet integration 305 | i = -1; while (++i < n) { 306 | o = nodes[i]; 307 | if (o.fixed) { 308 | o.x = o.px; 309 | o.y = o.py; 310 | } else { 311 | o.x -= (o.px - (o.px = o.x)) * friction; 312 | o.y -= (o.py - (o.py = o.y)) * friction; 313 | } 314 | } 315 | 316 | event.tick.dispatch({type: "tick", alpha: alpha}); 317 | 318 | // simulated annealing, basically 319 | return (alpha *= .99) < .005; 320 | } 321 | 322 | force.on = function(type, listener) { 323 | event[type].add(listener); 324 | return force; 325 | }; 326 | 327 | force.nodes = function(x) { 328 | if (!arguments.length) return nodes; 329 | nodes = x; 330 | return force; 331 | }; 332 | 333 | force.links = function(x) { 334 | if (!arguments.length) return links; 335 | links = x; 336 | return force; 337 | }; 338 | 339 | force.size = function(x) { 340 | if (!arguments.length) return size; 341 | size = x; 342 | return force; 343 | }; 344 | 345 | force.linkDistance = function(x) { 346 | if (!arguments.length) return linkDistance; 347 | linkDistance = d3.functor(x); 348 | return force; 349 | }; 350 | 351 | // For backwards-compatibility. 352 | force.distance = force.linkDistance; 353 | 354 | force.linkStrength = function(x) { 355 | if (!arguments.length) return linkStrength; 356 | linkStrength = d3.functor(x); 357 | return force; 358 | }; 359 | 360 | force.friction = function(x) { 361 | if (!arguments.length) return friction; 362 | friction = x; 363 | return force; 364 | }; 365 | 366 | force.charge = function(x) { 367 | if (!arguments.length) return charge; 368 | charge = typeof x === "function" ? x : +x; 369 | return force; 370 | }; 371 | 372 | force.gravity = function(x) { 373 | if (!arguments.length) return gravity; 374 | gravity = x; 375 | return force; 376 | }; 377 | 378 | force.theta = function(x) { 379 | if (!arguments.length) return theta; 380 | theta = x; 381 | return force; 382 | }; 383 | 384 | force.start = function() { 385 | var i, 386 | j, 387 | n = nodes.length, 388 | m = links.length, 389 | w = size[0], 390 | h = size[1], 391 | neighbors, 392 | o; 393 | 394 | for (i = 0; i < n; ++i) { 395 | (o = nodes[i]).index = i; 396 | o.weight = 0; 397 | } 398 | 399 | distances = []; 400 | strengths = []; 401 | for (i = 0; i < m; ++i) { 402 | o = links[i]; 403 | if (typeof o.source == "number") o.source = nodes[o.source]; 404 | if (typeof o.target == "number") o.target = nodes[o.target]; 405 | distances[i] = linkDistance.call(this, o, i); 406 | strengths[i] = linkStrength.call(this, o, i); 407 | ++o.source.weight; 408 | ++o.target.weight; 409 | } 410 | 411 | for (i = 0; i < n; ++i) { 412 | o = nodes[i]; 413 | if (isNaN(o.x)) o.x = position("x", w); 414 | if (isNaN(o.y)) o.y = position("y", h); 415 | if (isNaN(o.px)) o.px = o.x; 416 | if (isNaN(o.py)) o.py = o.y; 417 | } 418 | 419 | charges = []; 420 | if (typeof charge === "function") { 421 | for (i = 0; i < n; ++i) { 422 | charges[i] = +charge.call(this, nodes[i], i); 423 | } 424 | } else { 425 | for (i = 0; i < n; ++i) { 426 | charges[i] = charge; 427 | } 428 | } 429 | 430 | // initialize node position based on first neighbor 431 | function position(dimension, size) { 432 | var neighbors = neighbor(i), 433 | j = -1, 434 | m = neighbors.length, 435 | x; 436 | while (++j < m) if (!isNaN(x = neighbors[j][dimension])) return x; 437 | return Math.random() * size; 438 | } 439 | 440 | // initialize neighbors lazily 441 | function neighbor() { 442 | if (!neighbors) { 443 | neighbors = []; 444 | for (j = 0; j < n; ++j) { 445 | neighbors[j] = []; 446 | } 447 | for (j = 0; j < m; ++j) { 448 | var o = links[j]; 449 | neighbors[o.source.index].push(o.target); 450 | neighbors[o.target.index].push(o.source); 451 | } 452 | } 453 | return neighbors[i]; 454 | } 455 | 456 | return force.resume(); 457 | }; 458 | 459 | force.resume = function() { 460 | alpha = .1; 461 | d3.timer(tick); 462 | return force; 463 | }; 464 | 465 | force.stop = function() { 466 | alpha = 0; 467 | return force; 468 | }; 469 | 470 | // use `node.call(force.drag)` to make nodes draggable 471 | force.drag = function() { 472 | if (!drag) drag = d3.behavior.drag() 473 | .on("dragstart", dragstart) 474 | .on("drag", d3_layout_forceDrag) 475 | .on("dragend", d3_layout_forceDragEnd); 476 | 477 | this.on("mouseover.force", d3_layout_forceDragOver) 478 | .on("mouseout.force", d3_layout_forceDragOut) 479 | .call(drag); 480 | }; 481 | 482 | function dragstart(d) { 483 | d3_layout_forceDragOver(d3_layout_forceDragNode = d); 484 | d3_layout_forceDragForce = force; 485 | } 486 | 487 | return force; 488 | }; 489 | 490 | var d3_layout_forceDragForce, 491 | d3_layout_forceDragNode; 492 | 493 | function d3_layout_forceDragOver(d) { 494 | d.fixed |= 2; 495 | } 496 | 497 | function d3_layout_forceDragOut(d) { 498 | if (d !== d3_layout_forceDragNode) d.fixed &= 1; 499 | } 500 | 501 | function d3_layout_forceDragEnd() { 502 | d3_layout_forceDrag(); 503 | d3_layout_forceDragNode.fixed &= 1; 504 | d3_layout_forceDragForce = d3_layout_forceDragNode = null; 505 | } 506 | 507 | function d3_layout_forceDrag() { 508 | d3_layout_forceDragNode.px += d3.event.dx; 509 | d3_layout_forceDragNode.py += d3.event.dy; 510 | d3_layout_forceDragForce.resume(); // restart annealing 511 | } 512 | 513 | function d3_layout_forceAccumulate(quad, alpha, charges) { 514 | var cx = 0, 515 | cy = 0; 516 | quad.charge = 0; 517 | if (!quad.leaf) { 518 | var nodes = quad.nodes, 519 | n = nodes.length, 520 | i = -1, 521 | c; 522 | while (++i < n) { 523 | c = nodes[i]; 524 | if (c == null) continue; 525 | d3_layout_forceAccumulate(c, alpha, charges); 526 | quad.charge += c.charge; 527 | cx += c.charge * c.cx; 528 | cy += c.charge * c.cy; 529 | } 530 | } 531 | if (quad.point) { 532 | // jitter internal nodes that are coincident 533 | if (!quad.leaf) { 534 | quad.point.x += Math.random() - .5; 535 | quad.point.y += Math.random() - .5; 536 | } 537 | var k = alpha * charges[quad.point.index]; 538 | quad.charge += quad.pointCharge = k; 539 | cx += k * quad.point.x; 540 | cy += k * quad.point.y; 541 | } 542 | quad.cx = cx / quad.charge; 543 | quad.cy = cy / quad.charge; 544 | } 545 | 546 | function d3_layout_forceLinkDistance(link) { 547 | return 20; 548 | } 549 | 550 | function d3_layout_forceLinkStrength(link) { 551 | return 1; 552 | } 553 | d3.layout.partition = function() { 554 | var hierarchy = d3.layout.hierarchy(), 555 | size = [1, 1]; // width, height 556 | 557 | function position(node, x, dx, dy) { 558 | var children = node.children; 559 | node.x = x; 560 | node.y = node.depth * dy; 561 | node.dx = dx; 562 | node.dy = dy; 563 | if (children && (n = children.length)) { 564 | var i = -1, 565 | n, 566 | c, 567 | d; 568 | dx = node.value ? dx / node.value : 0; 569 | while (++i < n) { 570 | position(c = children[i], x, d = c.value * dx, dy); 571 | x += d; 572 | } 573 | } 574 | } 575 | 576 | function depth(node) { 577 | var children = node.children, 578 | d = 0; 579 | if (children && (n = children.length)) { 580 | var i = -1, 581 | n; 582 | while (++i < n) d = Math.max(d, depth(children[i])); 583 | } 584 | return 1 + d; 585 | } 586 | 587 | function partition(d, i) { 588 | var nodes = hierarchy.call(this, d, i); 589 | position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); 590 | return nodes; 591 | } 592 | 593 | partition.size = function(x) { 594 | if (!arguments.length) return size; 595 | size = x; 596 | return partition; 597 | }; 598 | 599 | return d3_layout_hierarchyRebind(partition, hierarchy); 600 | }; 601 | d3.layout.pie = function() { 602 | var value = Number, 603 | sort = null, 604 | startAngle = 0, 605 | endAngle = 2 * Math.PI; 606 | 607 | function pie(data, i) { 608 | 609 | // Compute the start angle. 610 | var a = +(typeof startAngle === "function" 611 | ? startAngle.apply(this, arguments) 612 | : startAngle); 613 | 614 | // Compute the angular range (end - start). 615 | var k = (typeof endAngle === "function" 616 | ? endAngle.apply(this, arguments) 617 | : endAngle) - startAngle; 618 | 619 | // Optionally sort the data. 620 | var index = d3.range(data.length); 621 | if (sort != null) index.sort(function(i, j) { 622 | return sort(data[i], data[j]); 623 | }); 624 | 625 | // Compute the numeric values for each data element. 626 | var values = data.map(value); 627 | 628 | // Convert k into a scale factor from value to angle, using the sum. 629 | k /= values.reduce(function(p, d) { return p + d; }, 0); 630 | 631 | // Compute the arcs! 632 | var arcs = index.map(function(i) { 633 | return { 634 | data: data[i], 635 | value: d = values[i], 636 | startAngle: a, 637 | endAngle: a += d * k 638 | }; 639 | }); 640 | 641 | // Return the arcs in the original data's order. 642 | return data.map(function(d, i) { 643 | return arcs[index[i]]; 644 | }); 645 | } 646 | 647 | /** 648 | * Specifies the value function *x*, which returns a nonnegative numeric value 649 | * for each datum. The default value function is `Number`. The value function 650 | * is passed two arguments: the current datum and the current index. 651 | */ 652 | pie.value = function(x) { 653 | if (!arguments.length) return value; 654 | value = x; 655 | return pie; 656 | }; 657 | 658 | /** 659 | * Specifies a sort comparison operator *x*. The comparator is passed two data 660 | * elements from the data array, a and b; it returns a negative value if a is 661 | * less than b, a positive value if a is greater than b, and zero if a equals 662 | * b. 663 | */ 664 | pie.sort = function(x) { 665 | if (!arguments.length) return sort; 666 | sort = x; 667 | return pie; 668 | }; 669 | 670 | /** 671 | * Specifies the overall start angle of the pie chart. Defaults to 0. The 672 | * start angle can be specified either as a constant or as a function; in the 673 | * case of a function, it is evaluated once per array (as opposed to per 674 | * element). 675 | */ 676 | pie.startAngle = function(x) { 677 | if (!arguments.length) return startAngle; 678 | startAngle = x; 679 | return pie; 680 | }; 681 | 682 | /** 683 | * Specifies the overall end angle of the pie chart. Defaults to 2π. The 684 | * end angle can be specified either as a constant or as a function; in the 685 | * case of a function, it is evaluated once per array (as opposed to per 686 | * element). 687 | */ 688 | pie.endAngle = function(x) { 689 | if (!arguments.length) return endAngle; 690 | endAngle = x; 691 | return pie; 692 | }; 693 | 694 | return pie; 695 | }; 696 | // data is two-dimensional array of x,y; we populate y0 697 | d3.layout.stack = function() { 698 | var values = Object, 699 | order = d3_layout_stackOrders["default"], 700 | offset = d3_layout_stackOffsets["zero"], 701 | out = d3_layout_stackOut, 702 | x = d3_layout_stackX, 703 | y = d3_layout_stackY; 704 | 705 | function stack(data, index) { 706 | 707 | // Convert series to canonical two-dimensional representation. 708 | var series = data.map(function(d, i) { 709 | return values.call(stack, d, i); 710 | }); 711 | 712 | // Convert each series to canonical [[x,y]] representation. 713 | var points = series.map(function(d, i) { 714 | return d.map(function(v, i) { 715 | return [x.call(stack, v, i), y.call(stack, v, i)]; 716 | }); 717 | }); 718 | 719 | // Compute the order of series, and permute them. 720 | var orders = order.call(stack, points, index); 721 | series = d3.permute(series, orders); 722 | points = d3.permute(points, orders); 723 | 724 | // Compute the baseline… 725 | var offsets = offset.call(stack, points, index); 726 | 727 | // And propagate it to other series. 728 | var n = series.length, 729 | m = series[0].length, 730 | i, 731 | j, 732 | o; 733 | for (j = 0; j < m; ++j) { 734 | out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); 735 | for (i = 1; i < n; ++i) { 736 | out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); 737 | } 738 | } 739 | 740 | return data; 741 | } 742 | 743 | stack.values = function(x) { 744 | if (!arguments.length) return values; 745 | values = x; 746 | return stack; 747 | }; 748 | 749 | stack.order = function(x) { 750 | if (!arguments.length) return order; 751 | order = typeof x === "function" ? x : d3_layout_stackOrders[x]; 752 | return stack; 753 | }; 754 | 755 | stack.offset = function(x) { 756 | if (!arguments.length) return offset; 757 | offset = typeof x === "function" ? x : d3_layout_stackOffsets[x]; 758 | return stack; 759 | }; 760 | 761 | stack.x = function(z) { 762 | if (!arguments.length) return x; 763 | x = z; 764 | return stack; 765 | }; 766 | 767 | stack.y = function(z) { 768 | if (!arguments.length) return y; 769 | y = z; 770 | return stack; 771 | }; 772 | 773 | stack.out = function(z) { 774 | if (!arguments.length) return out; 775 | out = z; 776 | return stack; 777 | }; 778 | 779 | return stack; 780 | } 781 | 782 | function d3_layout_stackX(d) { 783 | return d.x; 784 | } 785 | 786 | function d3_layout_stackY(d) { 787 | return d.y; 788 | } 789 | 790 | function d3_layout_stackOut(d, y0, y) { 791 | d.y0 = y0; 792 | d.y = y; 793 | } 794 | 795 | var d3_layout_stackOrders = { 796 | 797 | "inside-out": function(data) { 798 | var n = data.length, 799 | i, 800 | j, 801 | max = data.map(d3_layout_stackMaxIndex), 802 | sums = data.map(d3_layout_stackReduceSum), 803 | index = d3.range(n).sort(function(a, b) { return max[a] - max[b]; }), 804 | top = 0, 805 | bottom = 0, 806 | tops = [], 807 | bottoms = []; 808 | for (i = 0; i < n; ++i) { 809 | j = index[i]; 810 | if (top < bottom) { 811 | top += sums[j]; 812 | tops.push(j); 813 | } else { 814 | bottom += sums[j]; 815 | bottoms.push(j); 816 | } 817 | } 818 | return bottoms.reverse().concat(tops); 819 | }, 820 | 821 | "reverse": function(data) { 822 | return d3.range(data.length).reverse(); 823 | }, 824 | 825 | "default": function(data) { 826 | return d3.range(data.length); 827 | } 828 | 829 | }; 830 | 831 | var d3_layout_stackOffsets = { 832 | 833 | "silhouette": function(data) { 834 | var n = data.length, 835 | m = data[0].length, 836 | sums = [], 837 | max = 0, 838 | i, 839 | j, 840 | o, 841 | y0 = []; 842 | for (j = 0; j < m; ++j) { 843 | for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; 844 | if (o > max) max = o; 845 | sums.push(o); 846 | } 847 | for (j = 0; j < m; ++j) { 848 | y0[j] = (max - sums[j]) / 2; 849 | } 850 | return y0; 851 | }, 852 | 853 | "wiggle": function(data) { 854 | var n = data.length, 855 | x = data[0], 856 | m = x.length, 857 | max = 0, 858 | i, 859 | j, 860 | k, 861 | s1, 862 | s2, 863 | s3, 864 | dx, 865 | o, 866 | o0, 867 | y0 = []; 868 | y0[0] = o = o0 = 0; 869 | for (j = 1; j < m; ++j) { 870 | for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; 871 | for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { 872 | for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { 873 | s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; 874 | } 875 | s2 += s3 * data[i][j][1]; 876 | } 877 | y0[j] = o -= s1 ? s2 / s1 * dx : 0; 878 | if (o < o0) o0 = o; 879 | } 880 | for (j = 0; j < m; ++j) y0[j] -= o0; 881 | return y0; 882 | }, 883 | 884 | "expand": function(data) { 885 | var n = data.length, 886 | m = data[0].length, 887 | k = 1 / n, 888 | i, 889 | j, 890 | o, 891 | y0 = []; 892 | for (j = 0; j < m; ++j) { 893 | for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; 894 | if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; 895 | else for (i = 0; i < n; i++) data[i][j][1] = k; 896 | } 897 | for (j = 0; j < m; ++j) y0[j] = 0; 898 | return y0; 899 | }, 900 | 901 | "zero": function(data) { 902 | var j = -1, 903 | m = data[0].length, 904 | y0 = []; 905 | while (++j < m) y0[j] = 0; 906 | return y0; 907 | } 908 | 909 | }; 910 | 911 | function d3_layout_stackMaxIndex(array) { 912 | var i = 1, 913 | j = 0, 914 | v = array[0][1], 915 | k, 916 | n = array.length; 917 | for (; i < n; ++i) { 918 | if ((k = array[i][1]) > v) { 919 | j = i; 920 | v = k; 921 | } 922 | } 923 | return j; 924 | } 925 | 926 | function d3_layout_stackReduceSum(d) { 927 | return d.reduce(d3_layout_stackSum, 0); 928 | } 929 | 930 | function d3_layout_stackSum(p, d) { 931 | return p + d[1]; 932 | } 933 | d3.layout.histogram = function() { 934 | var frequency = true, 935 | valuer = Number, 936 | ranger = d3_layout_histogramRange, 937 | binner = d3_layout_histogramBinSturges; 938 | 939 | function histogram(data, i) { 940 | var bins = [], 941 | values = data.map(valuer, this), 942 | range = ranger.call(this, values, i), 943 | thresholds = binner.call(this, range, values, i), 944 | bin, 945 | i = -1, 946 | n = values.length, 947 | m = thresholds.length - 1, 948 | k = frequency ? 1 : 1 / n, 949 | x; 950 | 951 | // Initialize the bins. 952 | while (++i < m) { 953 | bin = bins[i] = []; 954 | bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); 955 | bin.y = 0; 956 | } 957 | 958 | // Fill the bins, ignoring values outside the range. 959 | i = -1; while(++i < n) { 960 | x = values[i]; 961 | if ((x >= range[0]) && (x <= range[1])) { 962 | bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; 963 | bin.y += k; 964 | bin.push(data[i]); 965 | } 966 | } 967 | 968 | return bins; 969 | } 970 | 971 | // Specifies how to extract a value from the associated data. The default 972 | // value function is `Number`, which is equivalent to the identity function. 973 | histogram.value = function(x) { 974 | if (!arguments.length) return valuer; 975 | valuer = x; 976 | return histogram; 977 | }; 978 | 979 | // Specifies the range of the histogram. Values outside the specified range 980 | // will be ignored. The argument `x` may be specified either as a two-element 981 | // array representing the minimum and maximum value of the range, or as a 982 | // function that returns the range given the array of values and the current 983 | // index `i`. The default range is the extent (minimum and maximum) of the 984 | // values. 985 | histogram.range = function(x) { 986 | if (!arguments.length) return ranger; 987 | ranger = d3.functor(x); 988 | return histogram; 989 | }; 990 | 991 | // Specifies how to bin values in the histogram. The argument `x` may be 992 | // specified as a number, in which case the range of values will be split 993 | // uniformly into the given number of bins. Or, `x` may be an array of 994 | // threshold values, defining the bins; the specified array must contain the 995 | // rightmost (upper) value, thus specifying n + 1 values for n bins. Or, `x` 996 | // may be a function which is evaluated, being passed the range, the array of 997 | // values, and the current index `i`, returning an array of thresholds. The 998 | // default bin function will divide the values into uniform bins using 999 | // Sturges' formula. 1000 | histogram.bins = function(x) { 1001 | if (!arguments.length) return binner; 1002 | binner = typeof x === "number" 1003 | ? function(range) { return d3_layout_histogramBinFixed(range, x); } 1004 | : d3.functor(x); 1005 | return histogram; 1006 | }; 1007 | 1008 | // Specifies whether the histogram's `y` value is a count (frequency) or a 1009 | // probability (density). The default value is true. 1010 | histogram.frequency = function(x) { 1011 | if (!arguments.length) return frequency; 1012 | frequency = !!x; 1013 | return histogram; 1014 | }; 1015 | 1016 | return histogram; 1017 | }; 1018 | 1019 | function d3_layout_histogramBinSturges(range, values) { 1020 | return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); 1021 | } 1022 | 1023 | function d3_layout_histogramBinFixed(range, n) { 1024 | var x = -1, 1025 | b = +range[0], 1026 | m = (range[1] - b) / n, 1027 | f = []; 1028 | while (++x <= n) f[x] = m * x + b; 1029 | return f; 1030 | } 1031 | 1032 | function d3_layout_histogramRange(values) { 1033 | return [d3.min(values), d3.max(values)]; 1034 | } 1035 | d3.layout.hierarchy = function() { 1036 | var sort = d3_layout_hierarchySort, 1037 | children = d3_layout_hierarchyChildren, 1038 | value = d3_layout_hierarchyValue; 1039 | 1040 | // Recursively compute the node depth and value. 1041 | // Also converts the data representation into a standard hierarchy structure. 1042 | function recurse(data, depth, nodes) { 1043 | var childs = children.call(hierarchy, data, depth), 1044 | node = d3_layout_hierarchyInline ? data : {data: data}; 1045 | node.depth = depth; 1046 | nodes.push(node); 1047 | if (childs && (n = childs.length)) { 1048 | var i = -1, 1049 | n, 1050 | c = node.children = [], 1051 | v = 0, 1052 | j = depth + 1; 1053 | while (++i < n) { 1054 | d = recurse(childs[i], j, nodes); 1055 | d.parent = node; 1056 | c.push(d); 1057 | v += d.value; 1058 | } 1059 | if (sort) c.sort(sort); 1060 | if (value) node.value = v; 1061 | } else if (value) { 1062 | node.value = +value.call(hierarchy, data, depth) || 0; 1063 | } 1064 | return node; 1065 | } 1066 | 1067 | // Recursively re-evaluates the node value. 1068 | function revalue(node, depth) { 1069 | var children = node.children, 1070 | v = 0; 1071 | if (children && (n = children.length)) { 1072 | var i = -1, 1073 | n, 1074 | j = depth + 1; 1075 | while (++i < n) v += revalue(children[i], j); 1076 | } else if (value) { 1077 | v = +value.call(hierarchy, d3_layout_hierarchyInline ? node : node.data, depth) || 0; 1078 | } 1079 | if (value) node.value = v; 1080 | return v; 1081 | } 1082 | 1083 | function hierarchy(d) { 1084 | var nodes = []; 1085 | recurse(d, 0, nodes); 1086 | return nodes; 1087 | } 1088 | 1089 | hierarchy.sort = function(x) { 1090 | if (!arguments.length) return sort; 1091 | sort = x; 1092 | return hierarchy; 1093 | }; 1094 | 1095 | hierarchy.children = function(x) { 1096 | if (!arguments.length) return children; 1097 | children = x; 1098 | return hierarchy; 1099 | }; 1100 | 1101 | hierarchy.value = function(x) { 1102 | if (!arguments.length) return value; 1103 | value = x; 1104 | return hierarchy; 1105 | }; 1106 | 1107 | // Re-evaluates the `value` property for the specified hierarchy. 1108 | hierarchy.revalue = function(root) { 1109 | revalue(root, 0); 1110 | return root; 1111 | }; 1112 | 1113 | return hierarchy; 1114 | }; 1115 | 1116 | // A method assignment helper for hierarchy subclasses. 1117 | function d3_layout_hierarchyRebind(object, hierarchy) { 1118 | object.sort = d3.rebind(object, hierarchy.sort); 1119 | object.children = d3.rebind(object, hierarchy.children); 1120 | object.links = d3_layout_hierarchyLinks; 1121 | object.value = d3.rebind(object, hierarchy.value); 1122 | 1123 | // If the new API is used, enabling inlining. 1124 | object.nodes = function(d) { 1125 | d3_layout_hierarchyInline = true; 1126 | return (object.nodes = object)(d); 1127 | }; 1128 | 1129 | return object; 1130 | } 1131 | 1132 | function d3_layout_hierarchyChildren(d) { 1133 | return d.children; 1134 | } 1135 | 1136 | function d3_layout_hierarchyValue(d) { 1137 | return d.value; 1138 | } 1139 | 1140 | function d3_layout_hierarchySort(a, b) { 1141 | return b.value - a.value; 1142 | } 1143 | 1144 | // Returns an array source+target objects for the specified nodes. 1145 | function d3_layout_hierarchyLinks(nodes) { 1146 | return d3.merge(nodes.map(function(parent) { 1147 | return (parent.children || []).map(function(child) { 1148 | return {source: parent, target: child}; 1149 | }); 1150 | })); 1151 | } 1152 | 1153 | // For backwards-compatibility, don't enable inlining by default. 1154 | var d3_layout_hierarchyInline = false; 1155 | d3.layout.pack = function() { 1156 | var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), 1157 | size = [1, 1]; 1158 | 1159 | function pack(d, i) { 1160 | var nodes = hierarchy.call(this, d, i), 1161 | root = nodes[0]; 1162 | 1163 | // Recursively compute the layout. 1164 | root.x = 0; 1165 | root.y = 0; 1166 | d3_layout_packTree(root); 1167 | 1168 | // Scale the layout to fit the requested size. 1169 | var w = size[0], 1170 | h = size[1], 1171 | k = 1 / Math.max(2 * root.r / w, 2 * root.r / h); 1172 | d3_layout_packTransform(root, w / 2, h / 2, k); 1173 | 1174 | return nodes; 1175 | } 1176 | 1177 | pack.size = function(x) { 1178 | if (!arguments.length) return size; 1179 | size = x; 1180 | return pack; 1181 | }; 1182 | 1183 | return d3_layout_hierarchyRebind(pack, hierarchy); 1184 | }; 1185 | 1186 | function d3_layout_packSort(a, b) { 1187 | return a.value - b.value; 1188 | } 1189 | 1190 | function d3_layout_packInsert(a, b) { 1191 | var c = a._pack_next; 1192 | a._pack_next = b; 1193 | b._pack_prev = a; 1194 | b._pack_next = c; 1195 | c._pack_prev = b; 1196 | } 1197 | 1198 | function d3_layout_packSplice(a, b) { 1199 | a._pack_next = b; 1200 | b._pack_prev = a; 1201 | } 1202 | 1203 | function d3_layout_packIntersects(a, b) { 1204 | var dx = b.x - a.x, 1205 | dy = b.y - a.y, 1206 | dr = a.r + b.r; 1207 | return (dr * dr - dx * dx - dy * dy) > .001; // within epsilon 1208 | } 1209 | 1210 | function d3_layout_packCircle(nodes) { 1211 | var xMin = Infinity, 1212 | xMax = -Infinity, 1213 | yMin = Infinity, 1214 | yMax = -Infinity, 1215 | n = nodes.length, 1216 | a, b, c, j, k; 1217 | 1218 | function bound(node) { 1219 | xMin = Math.min(node.x - node.r, xMin); 1220 | xMax = Math.max(node.x + node.r, xMax); 1221 | yMin = Math.min(node.y - node.r, yMin); 1222 | yMax = Math.max(node.y + node.r, yMax); 1223 | } 1224 | 1225 | // Create node links. 1226 | nodes.forEach(d3_layout_packLink); 1227 | 1228 | // Create first node. 1229 | a = nodes[0]; 1230 | a.x = -a.r; 1231 | a.y = 0; 1232 | bound(a); 1233 | 1234 | // Create second node. 1235 | if (n > 1) { 1236 | b = nodes[1]; 1237 | b.x = b.r; 1238 | b.y = 0; 1239 | bound(b); 1240 | 1241 | // Create third node and build chain. 1242 | if (n > 2) { 1243 | c = nodes[2]; 1244 | d3_layout_packPlace(a, b, c); 1245 | bound(c); 1246 | d3_layout_packInsert(a, c); 1247 | a._pack_prev = c; 1248 | d3_layout_packInsert(c, b); 1249 | b = a._pack_next; 1250 | 1251 | // Now iterate through the rest. 1252 | for (var i = 3; i < n; i++) { 1253 | d3_layout_packPlace(a, b, c = nodes[i]); 1254 | 1255 | // Search for the closest intersection. 1256 | var isect = 0, s1 = 1, s2 = 1; 1257 | for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { 1258 | if (d3_layout_packIntersects(j, c)) { 1259 | isect = 1; 1260 | break; 1261 | } 1262 | } 1263 | if (isect == 1) { 1264 | for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { 1265 | if (d3_layout_packIntersects(k, c)) { 1266 | if (s2 < s1) { 1267 | isect = -1; 1268 | j = k; 1269 | } 1270 | break; 1271 | } 1272 | } 1273 | } 1274 | 1275 | // Update node chain. 1276 | if (isect == 0) { 1277 | d3_layout_packInsert(a, c); 1278 | b = c; 1279 | bound(c); 1280 | } else if (isect > 0) { 1281 | d3_layout_packSplice(a, j); 1282 | b = j; 1283 | i--; 1284 | } else { // isect < 0 1285 | d3_layout_packSplice(j, b); 1286 | a = j; 1287 | i--; 1288 | } 1289 | } 1290 | } 1291 | } 1292 | 1293 | // Re-center the circles and return the encompassing radius. 1294 | var cx = (xMin + xMax) / 2, 1295 | cy = (yMin + yMax) / 2, 1296 | cr = 0; 1297 | for (var i = 0; i < n; i++) { 1298 | var node = nodes[i]; 1299 | node.x -= cx; 1300 | node.y -= cy; 1301 | cr = Math.max(cr, node.r + Math.sqrt(node.x * node.x + node.y * node.y)); 1302 | } 1303 | 1304 | // Remove node links. 1305 | nodes.forEach(d3_layout_packUnlink); 1306 | 1307 | return cr; 1308 | } 1309 | 1310 | function d3_layout_packLink(node) { 1311 | node._pack_next = node._pack_prev = node; 1312 | } 1313 | 1314 | function d3_layout_packUnlink(node) { 1315 | delete node._pack_next; 1316 | delete node._pack_prev; 1317 | } 1318 | 1319 | function d3_layout_packTree(node) { 1320 | var children = node.children; 1321 | if (children && children.length) { 1322 | children.forEach(d3_layout_packTree); 1323 | node.r = d3_layout_packCircle(children); 1324 | } else { 1325 | node.r = Math.sqrt(node.value); 1326 | } 1327 | } 1328 | 1329 | function d3_layout_packTransform(node, x, y, k) { 1330 | var children = node.children; 1331 | node.x = (x += k * node.x); 1332 | node.y = (y += k * node.y); 1333 | node.r *= k; 1334 | if (children) { 1335 | var i = -1, n = children.length; 1336 | while (++i < n) d3_layout_packTransform(children[i], x, y, k); 1337 | } 1338 | } 1339 | 1340 | function d3_layout_packPlace(a, b, c) { 1341 | var db = a.r + c.r, 1342 | dx = b.x - a.x, 1343 | dy = b.y - a.y; 1344 | if (db && (dx || dy)) { 1345 | var da = b.r + c.r, 1346 | dc = Math.sqrt(dx * dx + dy * dy), 1347 | cos = Math.max(-1, Math.min(1, (db * db + dc * dc - da * da) / (2 * db * dc))), 1348 | theta = Math.acos(cos), 1349 | x = cos * (db /= dc), 1350 | y = Math.sin(theta) * db; 1351 | c.x = a.x + x * dx + y * dy; 1352 | c.y = a.y + x * dy - y * dx; 1353 | } else { 1354 | c.x = a.x + db; 1355 | c.y = a.y; 1356 | } 1357 | } 1358 | // Implements a hierarchical layout using the cluster (or dendogram) algorithm. 1359 | d3.layout.cluster = function() { 1360 | var hierarchy = d3.layout.hierarchy().sort(null).value(null), 1361 | separation = d3_layout_treeSeparation, 1362 | size = [1, 1]; // width, height 1363 | 1364 | function cluster(d, i) { 1365 | var nodes = hierarchy.call(this, d, i), 1366 | root = nodes[0], 1367 | previousNode, 1368 | x = 0, 1369 | kx, 1370 | ky; 1371 | 1372 | // First walk, computing the initial x & y values. 1373 | d3_layout_treeVisitAfter(root, function(node) { 1374 | var children = node.children; 1375 | if (children && children.length) { 1376 | node.x = d3_layout_clusterX(children); 1377 | node.y = d3_layout_clusterY(children); 1378 | } else { 1379 | node.x = previousNode ? x += separation(node, previousNode) : 0; 1380 | node.y = 0; 1381 | previousNode = node; 1382 | } 1383 | }); 1384 | 1385 | // Compute the left-most, right-most, and depth-most nodes for extents. 1386 | var left = d3_layout_clusterLeft(root), 1387 | right = d3_layout_clusterRight(root), 1388 | x0 = left.x - separation(left, right) / 2, 1389 | x1 = right.x + separation(right, left) / 2; 1390 | 1391 | // Second walk, normalizing x & y to the desired size. 1392 | d3_layout_treeVisitAfter(root, function(node) { 1393 | node.x = (node.x - x0) / (x1 - x0) * size[0]; 1394 | node.y = (1 - node.y / root.y) * size[1]; 1395 | }); 1396 | 1397 | return nodes; 1398 | } 1399 | 1400 | cluster.separation = function(x) { 1401 | if (!arguments.length) return separation; 1402 | separation = x; 1403 | return cluster; 1404 | }; 1405 | 1406 | cluster.size = function(x) { 1407 | if (!arguments.length) return size; 1408 | size = x; 1409 | return cluster; 1410 | }; 1411 | 1412 | return d3_layout_hierarchyRebind(cluster, hierarchy); 1413 | }; 1414 | 1415 | function d3_layout_clusterY(children) { 1416 | return 1 + d3.max(children, function(child) { 1417 | return child.y; 1418 | }); 1419 | } 1420 | 1421 | function d3_layout_clusterX(children) { 1422 | return children.reduce(function(x, child) { 1423 | return x + child.x; 1424 | }, 0) / children.length; 1425 | } 1426 | 1427 | function d3_layout_clusterLeft(node) { 1428 | var children = node.children; 1429 | return children && children.length ? d3_layout_clusterLeft(children[0]) : node; 1430 | } 1431 | 1432 | function d3_layout_clusterRight(node) { 1433 | var children = node.children, n; 1434 | return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; 1435 | } 1436 | // Node-link tree diagram using the Reingold-Tilford "tidy" algorithm 1437 | d3.layout.tree = function() { 1438 | var hierarchy = d3.layout.hierarchy().sort(null).value(null), 1439 | separation = d3_layout_treeSeparation, 1440 | size = [1, 1]; // width, height 1441 | 1442 | function tree(d, i) { 1443 | var nodes = hierarchy.call(this, d, i), 1444 | root = nodes[0]; 1445 | 1446 | function firstWalk(node, previousSibling) { 1447 | var children = node.children, 1448 | layout = node._tree; 1449 | if (children && (n = children.length)) { 1450 | var n, 1451 | firstChild = children[0], 1452 | previousChild, 1453 | ancestor = firstChild, 1454 | child, 1455 | i = -1; 1456 | while (++i < n) { 1457 | child = children[i]; 1458 | firstWalk(child, previousChild); 1459 | ancestor = apportion(child, previousChild, ancestor); 1460 | previousChild = child; 1461 | } 1462 | d3_layout_treeShift(node); 1463 | var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim); 1464 | if (previousSibling) { 1465 | layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling); 1466 | layout.mod = layout.prelim - midpoint; 1467 | } else { 1468 | layout.prelim = midpoint; 1469 | } 1470 | } else { 1471 | if (previousSibling) { 1472 | layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling); 1473 | } 1474 | } 1475 | } 1476 | 1477 | function secondWalk(node, x) { 1478 | node.x = node._tree.prelim + x; 1479 | var children = node.children; 1480 | if (children && (n = children.length)) { 1481 | var i = -1, 1482 | n; 1483 | x += node._tree.mod; 1484 | while (++i < n) { 1485 | secondWalk(children[i], x); 1486 | } 1487 | } 1488 | } 1489 | 1490 | function apportion(node, previousSibling, ancestor) { 1491 | if (previousSibling) { 1492 | var vip = node, 1493 | vop = node, 1494 | vim = previousSibling, 1495 | vom = node.parent.children[0], 1496 | sip = vip._tree.mod, 1497 | sop = vop._tree.mod, 1498 | sim = vim._tree.mod, 1499 | som = vom._tree.mod, 1500 | shift; 1501 | while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { 1502 | vom = d3_layout_treeLeft(vom); 1503 | vop = d3_layout_treeRight(vop); 1504 | vop._tree.ancestor = node; 1505 | shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip); 1506 | if (shift > 0) { 1507 | d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift); 1508 | sip += shift; 1509 | sop += shift; 1510 | } 1511 | sim += vim._tree.mod; 1512 | sip += vip._tree.mod; 1513 | som += vom._tree.mod; 1514 | sop += vop._tree.mod; 1515 | } 1516 | if (vim && !d3_layout_treeRight(vop)) { 1517 | vop._tree.thread = vim; 1518 | vop._tree.mod += sim - sop; 1519 | } 1520 | if (vip && !d3_layout_treeLeft(vom)) { 1521 | vom._tree.thread = vip; 1522 | vom._tree.mod += sip - som; 1523 | ancestor = node; 1524 | } 1525 | } 1526 | return ancestor; 1527 | } 1528 | 1529 | // Initialize temporary layout variables. 1530 | d3_layout_treeVisitAfter(root, function(node, previousSibling) { 1531 | node._tree = { 1532 | ancestor: node, 1533 | prelim: 0, 1534 | mod: 0, 1535 | change: 0, 1536 | shift: 0, 1537 | number: previousSibling ? previousSibling._tree.number + 1 : 0 1538 | }; 1539 | }); 1540 | 1541 | // Compute the layout using Buchheim et al.'s algorithm. 1542 | firstWalk(root); 1543 | secondWalk(root, -root._tree.prelim); 1544 | 1545 | // Compute the left-most, right-most, and depth-most nodes for extents. 1546 | var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost), 1547 | right = d3_layout_treeSearch(root, d3_layout_treeRightmost), 1548 | deep = d3_layout_treeSearch(root, d3_layout_treeDeepest), 1549 | x0 = left.x - separation(left, right) / 2, 1550 | x1 = right.x + separation(right, left) / 2, 1551 | y1 = deep.depth || 1; 1552 | 1553 | // Clear temporary layout variables; transform x and y. 1554 | d3_layout_treeVisitAfter(root, function(node) { 1555 | node.x = (node.x - x0) / (x1 - x0) * size[0]; 1556 | node.y = node.depth / y1 * size[1]; 1557 | delete node._tree; 1558 | }); 1559 | 1560 | return nodes; 1561 | } 1562 | 1563 | tree.separation = function(x) { 1564 | if (!arguments.length) return separation; 1565 | separation = x; 1566 | return tree; 1567 | }; 1568 | 1569 | tree.size = function(x) { 1570 | if (!arguments.length) return size; 1571 | size = x; 1572 | return tree; 1573 | }; 1574 | 1575 | return d3_layout_hierarchyRebind(tree, hierarchy); 1576 | }; 1577 | 1578 | function d3_layout_treeSeparation(a, b) { 1579 | return a.parent == b.parent ? 1 : 2; 1580 | } 1581 | 1582 | // function d3_layout_treeSeparationRadial(a, b) { 1583 | // return (a.parent == b.parent ? 1 : 2) / a.depth; 1584 | // } 1585 | 1586 | function d3_layout_treeLeft(node) { 1587 | var children = node.children; 1588 | return children && children.length ? children[0] : node._tree.thread; 1589 | } 1590 | 1591 | function d3_layout_treeRight(node) { 1592 | var children = node.children, 1593 | n; 1594 | return children && (n = children.length) ? children[n - 1] : node._tree.thread; 1595 | } 1596 | 1597 | function d3_layout_treeSearch(node, compare) { 1598 | var children = node.children; 1599 | if (children && (n = children.length)) { 1600 | var child, 1601 | n, 1602 | i = -1; 1603 | while (++i < n) { 1604 | if (compare(child = d3_layout_treeSearch(children[i], compare), node) > 0) { 1605 | node = child; 1606 | } 1607 | } 1608 | } 1609 | return node; 1610 | } 1611 | 1612 | function d3_layout_treeRightmost(a, b) { 1613 | return a.x - b.x; 1614 | } 1615 | 1616 | function d3_layout_treeLeftmost(a, b) { 1617 | return b.x - a.x; 1618 | } 1619 | 1620 | function d3_layout_treeDeepest(a, b) { 1621 | return a.depth - b.depth; 1622 | } 1623 | 1624 | function d3_layout_treeVisitAfter(node, callback) { 1625 | function visit(node, previousSibling) { 1626 | var children = node.children; 1627 | if (children && (n = children.length)) { 1628 | var child, 1629 | previousChild = null, 1630 | i = -1, 1631 | n; 1632 | while (++i < n) { 1633 | child = children[i]; 1634 | visit(child, previousChild); 1635 | previousChild = child; 1636 | } 1637 | } 1638 | callback(node, previousSibling); 1639 | } 1640 | visit(node, null); 1641 | } 1642 | 1643 | function d3_layout_treeShift(node) { 1644 | var shift = 0, 1645 | change = 0, 1646 | children = node.children, 1647 | i = children.length, 1648 | child; 1649 | while (--i >= 0) { 1650 | child = children[i]._tree; 1651 | child.prelim += shift; 1652 | child.mod += shift; 1653 | shift += child.shift + (change += child.change); 1654 | } 1655 | } 1656 | 1657 | function d3_layout_treeMove(ancestor, node, shift) { 1658 | ancestor = ancestor._tree; 1659 | node = node._tree; 1660 | var change = shift / (node.number - ancestor.number); 1661 | ancestor.change += change; 1662 | node.change -= change; 1663 | node.shift += shift; 1664 | node.prelim += shift; 1665 | node.mod += shift; 1666 | } 1667 | 1668 | function d3_layout_treeAncestor(vim, node, ancestor) { 1669 | return vim._tree.ancestor.parent == node.parent 1670 | ? vim._tree.ancestor 1671 | : ancestor; 1672 | } 1673 | // Squarified Treemaps by Mark Bruls, Kees Huizing, and Jarke J. van Wijk 1674 | // Modified to support a target aspect ratio by Jeff Heer 1675 | d3.layout.treemap = function() { 1676 | var hierarchy = d3.layout.hierarchy(), 1677 | round = Math.round, 1678 | size = [1, 1], // width, height 1679 | padding = null, 1680 | pad = d3_layout_treemapPadNull, 1681 | sticky = false, 1682 | stickies, 1683 | ratio = 0.5 * (1 + Math.sqrt(5)); // golden ratio 1684 | 1685 | // Compute the area for each child based on value & scale. 1686 | function scale(children, k) { 1687 | var i = -1, 1688 | n = children.length, 1689 | child, 1690 | area; 1691 | while (++i < n) { 1692 | area = (child = children[i]).value * (k < 0 ? 0 : k); 1693 | child.area = isNaN(area) || area <= 0 ? 0 : area; 1694 | } 1695 | } 1696 | 1697 | // Recursively arranges the specified node's children into squarified rows. 1698 | function squarify(node) { 1699 | var children = node.children; 1700 | if (children && children.length) { 1701 | var rect = pad(node), 1702 | row = [], 1703 | remaining = children.slice(), // copy-on-write 1704 | child, 1705 | best = Infinity, // the best row score so far 1706 | score, // the current row score 1707 | u = Math.min(rect.dx, rect.dy), // initial orientation 1708 | n; 1709 | scale(remaining, rect.dx * rect.dy / node.value); 1710 | row.area = 0; 1711 | while ((n = remaining.length) > 0) { 1712 | row.push(child = remaining[n - 1]); 1713 | row.area += child.area; 1714 | if ((score = worst(row, u)) <= best) { // continue with this orientation 1715 | remaining.pop(); 1716 | best = score; 1717 | } else { // abort, and try a different orientation 1718 | row.area -= row.pop().area; 1719 | position(row, u, rect, false); 1720 | u = Math.min(rect.dx, rect.dy); 1721 | row.length = row.area = 0; 1722 | best = Infinity; 1723 | } 1724 | } 1725 | if (row.length) { 1726 | position(row, u, rect, true); 1727 | row.length = row.area = 0; 1728 | } 1729 | children.forEach(squarify); 1730 | } 1731 | } 1732 | 1733 | // Recursively resizes the specified node's children into existing rows. 1734 | // Preserves the existing layout! 1735 | function stickify(node) { 1736 | var children = node.children; 1737 | if (children && children.length) { 1738 | var rect = pad(node), 1739 | remaining = children.slice(), // copy-on-write 1740 | child, 1741 | row = []; 1742 | scale(remaining, rect.dx * rect.dy / node.value); 1743 | row.area = 0; 1744 | while (child = remaining.pop()) { 1745 | row.push(child); 1746 | row.area += child.area; 1747 | if (child.z != null) { 1748 | position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); 1749 | row.length = row.area = 0; 1750 | } 1751 | } 1752 | children.forEach(stickify); 1753 | } 1754 | } 1755 | 1756 | // Computes the score for the specified row, as the worst aspect ratio. 1757 | function worst(row, u) { 1758 | var s = row.area, 1759 | r, 1760 | rmax = 0, 1761 | rmin = Infinity, 1762 | i = -1, 1763 | n = row.length; 1764 | while (++i < n) { 1765 | if (!(r = row[i].area)) continue; 1766 | if (r < rmin) rmin = r; 1767 | if (r > rmax) rmax = r; 1768 | } 1769 | s *= s; 1770 | u *= u; 1771 | return s 1772 | ? Math.max((u * rmax * ratio) / s, s / (u * rmin * ratio)) 1773 | : Infinity; 1774 | } 1775 | 1776 | // Positions the specified row of nodes. Modifies `rect`. 1777 | function position(row, u, rect, flush) { 1778 | var i = -1, 1779 | n = row.length, 1780 | x = rect.x, 1781 | y = rect.y, 1782 | v = u ? round(row.area / u) : 0, 1783 | o; 1784 | if (u == rect.dx) { // horizontal subdivision 1785 | if (flush || v > rect.dy) v = v ? rect.dy : 0; // over+underflow 1786 | while (++i < n) { 1787 | o = row[i]; 1788 | o.x = x; 1789 | o.y = y; 1790 | o.dy = v; 1791 | x += o.dx = v ? round(o.area / v) : 0; 1792 | } 1793 | o.z = true; 1794 | o.dx += rect.x + rect.dx - x; // rounding error 1795 | rect.y += v; 1796 | rect.dy -= v; 1797 | } else { // vertical subdivision 1798 | if (flush || v > rect.dx) v = v ? rect.dx : 0; // over+underflow 1799 | while (++i < n) { 1800 | o = row[i]; 1801 | o.x = x; 1802 | o.y = y; 1803 | o.dx = v; 1804 | y += o.dy = v ? round(o.area / v) : 0; 1805 | } 1806 | o.z = false; 1807 | o.dy += rect.y + rect.dy - y; // rounding error 1808 | rect.x += v; 1809 | rect.dx -= v; 1810 | } 1811 | } 1812 | 1813 | function treemap(d) { 1814 | var nodes = stickies || hierarchy(d), 1815 | root = nodes[0]; 1816 | root.x = 0; 1817 | root.y = 0; 1818 | root.dx = size[0]; 1819 | root.dy = size[1]; 1820 | if (stickies) hierarchy.revalue(root); 1821 | scale([root], root.dx * root.dy / root.value); 1822 | (stickies ? stickify : squarify)(root); 1823 | if (sticky) stickies = nodes; 1824 | return nodes; 1825 | } 1826 | 1827 | treemap.size = function(x) { 1828 | if (!arguments.length) return size; 1829 | size = x; 1830 | return treemap; 1831 | }; 1832 | 1833 | treemap.padding = function(x) { 1834 | if (!arguments.length) return padding; 1835 | 1836 | function padFunction(node) { 1837 | var p = x.call(treemap, node, node.depth); 1838 | return p == null 1839 | ? d3_layout_treemapPadNull(node) 1840 | : d3_layout_treemapPad(node, typeof p === "number" ? [p, p, p, p] : p); 1841 | } 1842 | 1843 | function padConstant(node) { 1844 | return d3_layout_treemapPad(node, x); 1845 | } 1846 | 1847 | var type; 1848 | pad = (padding = x) == null ? d3_layout_treemapPadNull 1849 | : (type = typeof x) === "function" ? padFunction 1850 | : type === "number" ? (x = [x, x, x, x], padConstant) 1851 | : padConstant; 1852 | return treemap; 1853 | }; 1854 | 1855 | treemap.round = function(x) { 1856 | if (!arguments.length) return round != Number; 1857 | round = x ? Math.round : Number; 1858 | return treemap; 1859 | }; 1860 | 1861 | treemap.sticky = function(x) { 1862 | if (!arguments.length) return sticky; 1863 | sticky = x; 1864 | stickies = null; 1865 | return treemap; 1866 | }; 1867 | 1868 | treemap.ratio = function(x) { 1869 | if (!arguments.length) return ratio; 1870 | ratio = x; 1871 | return treemap; 1872 | }; 1873 | 1874 | return d3_layout_hierarchyRebind(treemap, hierarchy); 1875 | }; 1876 | 1877 | function d3_layout_treemapPadNull(node) { 1878 | return {x: node.x, y: node.y, dx: node.dx, dy: node.dy}; 1879 | } 1880 | 1881 | function d3_layout_treemapPad(node, padding) { 1882 | var x = node.x + padding[3], 1883 | y = node.y + padding[0], 1884 | dx = node.dx - padding[1] - padding[3], 1885 | dy = node.dy - padding[0] - padding[2]; 1886 | if (dx < 0) { x += dx / 2; dx = 0; } 1887 | if (dy < 0) { y += dy / 2; dy = 0; } 1888 | return {x: x, y: y, dx: dx, dy: dy}; 1889 | } 1890 | })(); -------------------------------------------------------------------------------- /XTreeIDAPro/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /XTreeIDAPro/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihilus/idatools/3f3b879f5e584f54312c23d27fd4e79fc5f18b47/XTreeIDAPro/style.css -------------------------------------------------------------------------------- /autoname_functions.py: -------------------------------------------------------------------------------- 1 | # autoname_functions.py 2 | # 3 | # Make a backup of your IDB before you run this. It will go through each string 4 | # that has a single cross reference to a function, take the first one, then 5 | # rename the function based on that string 6 | # 7 | # Weston Hopkins 8 | # August 2014 9 | # 10 | from __future__ import division 11 | from idaapi import * 12 | from idautils import * 13 | import re 14 | 15 | 16 | # Change this higher if you want less names associated with each other. 17 | # lower will give more false positives 18 | PROBABILITY_CUTTOFF = 0.50 19 | STRING_PROBABILITY_CUTTOFF = 0.10 20 | 21 | UNNAMED_REGEX = re.compile( r"^(sub|loc|flt|off|unk|byte|word|dword)_" ) 22 | AUTONAMED_REGEX = re.compile( r"^z[cdo]?_" ) 23 | 24 | 25 | ############################################################################## 26 | # MarkovModel 27 | ############################################################################## 28 | class MarkovModel: 29 | 30 | ############################################################################## 31 | def __init__(self, forStrings ): 32 | self.states = {} 33 | self.xrefs = {} 34 | self.forStrings = forStrings 35 | 36 | 37 | ############################################################################## 38 | def addTransition( self, fromStateID, toStateID ): 39 | 40 | if not fromStateID in self.states: 41 | newState = None 42 | if self.forStrings: 43 | newState = MarkovStateStrings(fromStateID, self) 44 | else: 45 | newState = MarkovStateCalls(fromStateID, self) 46 | self.states[fromStateID] = newState 47 | 48 | if not toStateID in self.xrefs: 49 | self.xrefs[toStateID] = 0 50 | 51 | self.xrefs[toStateID] += 1 52 | 53 | self.states[fromStateID].addTransition( toStateID ) 54 | 55 | ############################################################################## 56 | def cull( self, cutoffWeight ): 57 | for sourceID in self.states: 58 | source = self.states[sourceID] 59 | edges = source.edges 60 | cullList = [] 61 | for destID in edges: 62 | if source.probability(destID) < cutoffWeight: 63 | cullList.append(destID) 64 | for destID in cullList: 65 | del source.edges[destID] 66 | 67 | ############################################################################## 68 | # MarkovHashable 69 | ############################################################################## 70 | class MarkovHashable: 71 | 72 | ############################################################################## 73 | def __init__(self, stateID, model): 74 | self.stateID = stateID 75 | self.model = model 76 | 77 | ############################################################################## 78 | def __hash__( self ): 79 | return self.stateID 80 | 81 | ############################################################################## 82 | def __eq__(self, other ): 83 | return other.stateID == self.stateID 84 | 85 | ############################################################################## 86 | def __cmp__( self, other ): 87 | return self.stateID - other.stateID 88 | 89 | ############################################################################## 90 | def __repr__(self): 91 | return "%x" % self.stateID 92 | 93 | 94 | ############################################################################## 95 | # MarkovState 96 | ############################################################################## 97 | class MarkovState(MarkovHashable): 98 | 99 | def __init__(self, stateID, model): 100 | MarkovHashable.__init__(self, stateID, model) 101 | 102 | self.transistions_total = 0 103 | self.edges = {} 104 | self.model = model 105 | 106 | 107 | def addTransition( self, toStateID ): 108 | if not toStateID in self.edges: 109 | self.edges[toStateID] = 0 110 | 111 | self.edges[toStateID] += 1 112 | self.transistions_total += 1 113 | 114 | def probabilityToString( self, toStateID ): 115 | return ": prob %0.3f. %d edges, %d xrefs, %d transitions" % ( 116 | self.probability(toStateID), 117 | self.edges[toStateID], 118 | self.model.xrefs[toStateID], 119 | self.transistions_total 120 | ) 121 | 122 | ############################################################################## 123 | # MarkovStateStrings 124 | ############################################################################## 125 | class MarkovStateStrings(MarkovState): 126 | 127 | def __init__(self, stateID, model): 128 | MarkovState.__init__(self, stateID, model) 129 | 130 | def probability( self, toStateID ): 131 | return self.edges[toStateID] / self.model.xrefs[toStateID] 132 | 133 | 134 | ############################################################################## 135 | # MarkovStateCalls 136 | ############################################################################## 137 | class MarkovStateCalls(MarkovState): 138 | 139 | def __init__(self, stateID, model): 140 | MarkovState.__init__(self, stateID, model) 141 | 142 | def probability( self, toStateID ): 143 | return self.edges[toStateID] / self.transistions_total 144 | 145 | 146 | ############################################################################## 147 | # Stats 148 | ############################################################################## 149 | class Stats: 150 | renamesTotal = 0 151 | 152 | ############################################################################## 153 | # stripExistingPrefix() 154 | ############################################################################## 155 | def stripExistingPrefix( name ): 156 | if AUTONAMED_REGEX.match(name): 157 | return AUTONAMED_REGEX.sub( '', name ) 158 | else: 159 | return name 160 | 161 | ############################################################################## 162 | # safeName() 163 | ############################################################################## 164 | def safeName( addr, baseName, msg="" ): 165 | 166 | oldName = Name(addr) 167 | if not oldName: 168 | msg = "!!! Should not rename something that previously had no name: %s -> %s @ %x: %s" % (oldName, baseName, addr, msg) 169 | print( msg ) 170 | raise( msg ) 171 | # if oldName == baseName or oldName.startswith(baseName): 172 | 173 | 174 | newName = baseName 175 | Stats.renamesTotal += 1 176 | 177 | sc = MakeNameEx( addr, newName, SN_NOCHECK | SN_AUTO | SN_NOWARN ) 178 | i = 0 179 | while sc == 0: 180 | newName = baseName + str(i) 181 | sc = MakeNameEx( addr, newName, SN_NOCHECK | SN_AUTO | SN_NOWARN ) 182 | i += 1 183 | if i > 100000: 184 | errmsg = "Reached limit of autonaming. Trying to create name %s which mean it went through %d iterations" % (newName, i) 185 | print( errmsg ) 186 | raise( errmsg ) 187 | 188 | 189 | print( "%s -> %s%s" % (oldName, newName, msg) ) 190 | 191 | ############################################################################## 192 | # Thing 193 | ############################################################################## 194 | class Thing: 195 | 196 | ######################################################################### 197 | def __init__( self, addr ): 198 | self.isFunction = False 199 | self.xrefs = None 200 | self.endEA = None 201 | self.isCode = False 202 | self.isData = False 203 | 204 | segmentClazz = get_segm_class( getseg(addr) ) 205 | if "CODE" == segmentClazz: 206 | self.isCode = True 207 | if "DATA" == segmentClazz: 208 | self.isData = True 209 | 210 | funcAddr = get_func(addr) 211 | if funcAddr: 212 | self.addr = funcAddr.startEA 213 | self.endEA = funcAddr.endEA 214 | self.isFunction = True 215 | else: 216 | self.addr = addr 217 | self.endEA = addr + 4 218 | self.isFunction = False 219 | 220 | self.name = Name(self.addr) 221 | testName = Demangle( self.name, INF_LONG_DN) 222 | if testName: 223 | self.name = testName 224 | 225 | ######################################################################### 226 | # def xrefsFrom(self): 227 | # xrefThings = set() 228 | # map( lambda x: xrefThings.add(Thing(x)), self.xrefs_get() ) 229 | # return xrefThings 230 | 231 | ######################################################################### 232 | def getXrefs(self): 233 | if self.xrefs: 234 | return self.xrefs 235 | 236 | self.xrefs = [] 237 | if self.isFunction: 238 | fromAddrs = FuncItems( self.addr ) 239 | else: 240 | fromAddrs = [self.addr] 241 | 242 | for fromAddr in fromAddrs: 243 | for xrefFrom in XrefsFrom( fromAddr, 0 ): 244 | # Make sure it's not self referential 245 | # this does sometimes result in false positives because of the end 246 | # condition, but you can see why that might be beneficial most of the 247 | # time 248 | if xrefFrom.to < self.addr or xrefFrom.to > self.endEA: 249 | #self.xrefs.add(xrefFrom.to) 250 | self.xrefs.append(xrefFrom.to) 251 | 252 | return self.xrefs 253 | 254 | ######################################################################### 255 | def getPrefix(self): 256 | if self.isFunction: 257 | return "z_" 258 | elif self.isData: 259 | return "zd_" 260 | elif self.isCode: 261 | return "zc_" 262 | else: 263 | return "zo_" 264 | 265 | 266 | ######################################################################### 267 | def isNamed(self): 268 | return self.name and not UNNAMED_REGEX.match(self.name) 269 | 270 | ######################################################################### 271 | def suffix(self): 272 | if self.isFunction: 273 | return "()" 274 | else: 275 | return "@" 276 | 277 | def __repr__(self): 278 | return "%s%s" % (self.name, self.suffix()) 279 | 280 | def __hash__(self): 281 | return self.addr 282 | 283 | def __eq__(self, other): 284 | return self.addr == other.addr 285 | 286 | def __cmp__(self, other): 287 | return self.addr - other.addr 288 | 289 | ############################################################################## 290 | # 291 | ############################################################################## 292 | def resetExistingNames(): 293 | print("Resetting existing names...") 294 | for segment in Segments(): 295 | seg = getseg(segment) 296 | clazz = get_segm_class(seg) 297 | # We don't want to include functions since we'll do that in the next block 298 | for head in Heads( segment, SegEnd(segment) ): 299 | name = Name(head) 300 | if name and AUTONAMED_REGEX.match(name): 301 | print(name) 302 | MakeName( head, "" ) 303 | print("Done resetting names...") 304 | 305 | 306 | ############################################################################## 307 | # renameData() 308 | ############################################################################## 309 | def renameData(): 310 | print("Renaming Data...") 311 | changes = 0 312 | for segment in Segments(): 313 | seg = getseg(segment) 314 | clazz = get_segm_class(seg) 315 | if clazz == "CODE": 316 | continue 317 | for head in Heads( segment, SegEnd(segment) ): 318 | thing = Thing(head) 319 | if not thing.isFunction and thing.name and not thing.isNamed(): 320 | xrefs_from = thing.xrefsFrom() 321 | if len(xrefs_from) == 1: 322 | reffedThing = xrefs_from.pop() 323 | if( reffedThing.isNamed() ): 324 | newName = thing.getPrefix() + stripExistingPrefix(reffedThing.name) 325 | #print( "%s -> %s" % ( thing, newName) ) 326 | safeName( thing.addr, newName ) 327 | changes += 1 328 | return changes 329 | 330 | ############################################################################## 331 | # renameFunctions() 332 | ############################################################################## 333 | def renameFunctions(): 334 | print("Renaming Functions...") 335 | changes = 0 336 | allFunctionAddrs = Functions() 337 | for funcAddr in allFunctionAddrs: 338 | func = Thing(funcAddr) 339 | if not func.isNamed(): 340 | #xrefs_to = func.xrefsTo() 341 | xrefs_from = func.xrefsFrom() 342 | if len(xrefs_from) == 1: 343 | calledThing = xrefs_from.pop() 344 | if( calledThing.isNamed() ): 345 | newName = func.getPrefix() + stripExistingPrefix(calledThing.name) 346 | #print( "%s -> %s" % ( func, newName) ) 347 | changes += 1 348 | safeName(func.addr, newName ) 349 | return changes 350 | 351 | ############################################################################## 352 | # xrefToString() 353 | ############################################################################## 354 | def xrefToString(xref): 355 | return "%s -> %s : %s" % ( hex(xref.frm), hex(xref.to), XrefTypeName(xref.type)) 356 | 357 | ############################################################################## 358 | # sanitizeString() 359 | ############################################################################## 360 | def sanitizeString(s): 361 | if not s: 362 | return s 363 | ret = s 364 | ret = re.sub( r'\0+', '', ret ) 365 | ret = re.sub( r'%[\+ -#0]*[\d\.]*[lhLzjt]{0,2}[diufFeEgGxXoscpaAn]', '_', ret ) 366 | ret = re.sub( r'[^a-zA-Z0-9_]+', '_', ret ) 367 | ret = re.sub( r'_+', '_', ret ) 368 | return ret.strip('_') 369 | 370 | ############################################################################## 371 | # processStringXrefs() 372 | ############################################################################## 373 | def processStringXrefs(item, functionsHash): 374 | links = [] 375 | string = str(item) 376 | string = string.strip() 377 | 378 | for xref in XrefsTo(item.ea, 0): 379 | refAddr = xref.frm 380 | func = get_func(refAddr) 381 | if func: 382 | funcAddr = func.startEA 383 | functionName = Name(funcAddr) 384 | 385 | if( functionName.startswith( 'sub_' ) or AUTONAMED_REGEX.match(functionName) ): 386 | link = (funcAddr, refAddr, string ) 387 | links.append(link) 388 | 389 | 390 | if( len(links) == 1 ): 391 | link = links[0] 392 | funcAddr = link[0] 393 | lastLink = link 394 | try: 395 | lastLink = functionsHash[funcAddr] 396 | except: 397 | functionsHash[funcAddr] = link 398 | 399 | if( link[1] < lastLink[1] ): 400 | functionsHash[funcAddr] = link 401 | 402 | 403 | ############################################################################## 404 | # renameFunctionsBasedOnStrings() 405 | ############################################################################## 406 | def renameFunctionsBasedOnStrings(): 407 | print("Renaming functions based on strings...") 408 | allStrings = Strings(False) 409 | allStrings.setup( strtypes = Strings.STR_C |Strings.STR_UNICODE ) 410 | # key : function EA 411 | # value : ( Xref, string ) 412 | 413 | 414 | functionsHash = {} 415 | 416 | for index, stringItem in enumerate(allStrings) : 417 | if stringItem is None : 418 | print("Nothing for string #%d" % index ) 419 | else: 420 | processStringXrefs( stringItem, functionsHash ) 421 | 422 | for funcAddr in functionsHash.keys() : 423 | link = functionsHash[funcAddr] 424 | refAddr = link[1] 425 | string = link[2] 426 | oldThing = Thing(funcAddr) 427 | if( not oldThing.isNamed() ): 428 | newName = oldThing.getPrefix() + sanitizeString(string) 429 | safeName( funcAddr , newName ) 430 | 431 | 432 | ############################################################################## 433 | # fixupIdaStringNames() 434 | ############################################################################## 435 | def fixupIdaStringNames(): 436 | allStrings = Strings(False) 437 | allStrings.setup( strtypes = Strings.STR_C | Strings.STR_UNICODE ) 438 | for index, s in enumerate(allStrings): 439 | name = Name(s.ea) 440 | if name and not name.startswith('z'): 441 | newName = "z%s" % sanitizeString(str(s)) 442 | newName = newName[:256] 443 | safeName( s.ea, newName ); 444 | 445 | ############################################################################## 446 | # buildCallsModel() 447 | ############################################################################## 448 | def runCallsModel(): 449 | 450 | markovModel = MarkovModel(False) 451 | 452 | print("Building markov model for data...") 453 | for segment in Segments(): 454 | seg = getseg(segment) 455 | clazz = get_segm_class(seg) 456 | # We don't want to include functions since we'll do that in the next block 457 | if clazz == "CODE": 458 | continue 459 | for head in Heads( segment, SegEnd(segment) ): 460 | thing = Thing(head) 461 | if thing.name: 462 | for xref in thing.getXrefs(): 463 | markovModel.addTransition( thing.addr, xref ) 464 | 465 | 466 | print("Building markov model for functions...") 467 | print("... chill mon.. this may take a while...") 468 | changes = 0 469 | allFunctionAddrs = Functions() 470 | for funcAddr in allFunctionAddrs: 471 | func = Thing(funcAddr) 472 | if not func.isNamed(): 473 | for xref in func.getXrefs(): 474 | markovModel.addTransition( func.addr, xref ) 475 | 476 | 477 | print("Culling at %d %%" % (PROBABILITY_CUTTOFF*100) ) 478 | markovModel.cull(PROBABILITY_CUTTOFF) 479 | 480 | changes = 1 481 | iteration = 0 482 | while changes > 0: 483 | print(" Pass %d" % iteration ) 484 | changes = 0 485 | for sourceID in markovModel.states: 486 | sourceThing = Thing(sourceID) 487 | if sourceThing.isNamed(): 488 | continue 489 | source = markovModel.states[sourceID] 490 | edges = sorted( source.edges, key=source.probability ) 491 | for destID in edges: 492 | destThing = Thing(destID) 493 | if destThing.isNamed(): 494 | newName = sourceThing.getPrefix() + stripExistingPrefix( destThing.name ) 495 | msg = ": " + source.probabilityToString(destID) 496 | safeName( sourceThing.addr, newName, msg ) 497 | edge = source.edges[destID] 498 | changes += 1 499 | break 500 | if iteration==0: 501 | renameFunctionsBasedOnStrings() 502 | changes += 1 503 | iteration += 1 504 | 505 | print("Pass %d, %d changes" % (iteration, changes) ) 506 | return changes 507 | 508 | ############################################################################## 509 | # 510 | ############################################################################## 511 | def runStringsModel( filterEnabled ): 512 | validIdentifierRegex = re.compile( r"^[_a-zA-Z0-9]+$" ) 513 | 514 | stringModel = MarkovModel(True) 515 | 516 | suffix = None 517 | if filterEnabled: 518 | suffix = "enabled" 519 | else: 520 | suffix = "disabled" 521 | 522 | print("Building markov model for strings with filter %s." % suffix) 523 | allStrings = Strings(False) 524 | allStrings.setup( strtypes = Strings.STR_C | Strings.STR_UNICODE ) 525 | 526 | for index, stringItem in enumerate(allStrings): 527 | stringAddr = stringItem.ea 528 | string = str(stringItem) 529 | if not filterEnabled or validIdentifierRegex.match(string): 530 | for xref in XrefsTo(stringAddr, 0): 531 | thing = Thing(xref.frm) 532 | if thing.name: 533 | stringModel.addTransition( thing.addr, stringAddr ) 534 | 535 | 536 | print("Culling at %d %%" % (STRING_PROBABILITY_CUTTOFF*100) ) 537 | stringModel.cull(STRING_PROBABILITY_CUTTOFF) 538 | 539 | for sourceID in stringModel.states: 540 | sourceThing = Thing(sourceID) 541 | if sourceThing.isNamed(): 542 | continue 543 | source = stringModel.states[sourceID] 544 | edges = sorted( source.edges, key=source.probability ) 545 | for destID in edges: 546 | string = GetString(destID) 547 | if not string: 548 | continue 549 | string = sanitizeString(string) 550 | if len(string)>4: 551 | newName = sourceThing.getPrefix() + string 552 | #msg = ": probability = %0.3f" % source.probability(destID) 553 | msg = ": %s" % ( source.probabilityToString(destID) ) 554 | safeName( sourceThing.addr, newName, msg ) 555 | edge = source.edges[destID] 556 | #print( "\t%f probability: %d / %d" % ( source.probability(destID), edge, source.transistions_total) ) 557 | break 558 | 559 | 560 | ############################################################################## 561 | # main() 562 | ############################################################################## 563 | def main(): 564 | resetOld = AskYN( 0, "Reset any existing names generated from previous runs of this script?") 565 | if resetOld == -1: 566 | return 567 | if resetOld==1: 568 | resetExistingNames() 569 | fixupIdaStringNames() 570 | runStringsModel(True) 571 | runStringsModel(False) 572 | 573 | runCallsModel() 574 | 575 | print("DONE! %d changes total." % Stats.renamesTotal ) 576 | 577 | 578 | ############################################################################## 579 | # main_old() 580 | ############################################################################## 581 | def main_old(): 582 | 583 | fixupIdaStringNames() 584 | renameFunctionsBasedOnStrings() 585 | changes = 1 586 | iteration = 0 587 | while changes > 0: 588 | iteration += 1 589 | changes = 0 590 | print( "Pass %d." % iteration ) 591 | changes += renameData() 592 | print( "Pass %d." % iteration ) 593 | changes += renameFunctions() 594 | print( "Pass %d had %d changes" % (iteration, changes ) ) 595 | 596 | 597 | print("Done with a total of %d changes" % Stats.renamesTotal ) 598 | 599 | if __name__ == '__main__': 600 | main() 601 | 602 | -------------------------------------------------------------------------------- /compile_hexrays.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | 4 | UNDECLARED_IDENTITIER_REGEX = re.compile(r".*error: use of undeclared identifier '([^']+)'") 5 | REFERENCED_FUNCTION_REGEX = re.compile( r'.*"([^"]+)", referenced from:.*') 6 | 7 | 8 | def usage(): 9 | print("compile_hexrays.py ") 10 | 11 | 12 | def preprocess(f): 13 | 14 | currentlySkipping = False 15 | openCurlies = 0 16 | closeCurlies = 0 17 | 18 | for line in f.readlines(): 19 | line = line.rstrip() 20 | line = line.replace( "::", "__" ) 21 | 22 | if '#error' in line: 23 | continue 24 | 25 | if '#include ' in line: 26 | print('#include "defs.h"') 27 | print('#include "hexrays_kludge.h"') 28 | continue 29 | 30 | if '__asm' in line: 31 | currentlySkipping = True 32 | openCurlies = 0 33 | closeCurlies = 0 34 | 35 | if currentlySkipping: 36 | openCurlies += line.count('{') 37 | closeCurlies += line.count('}') 38 | if openCurlies>0 and openCurlies-closeCurlies == 0: 39 | currentlySkipping = False 40 | continue 41 | 42 | print(line) 43 | 44 | 45 | 46 | def main(): 47 | 48 | 49 | if len(sys.argv) != 2: 50 | usage() 51 | return 52 | 53 | f = sys.stdin 54 | if( "preprocess" in sys.argv[1] ): 55 | preprocess(f) 56 | elif( "header" in sys.argv[1] ): 57 | generateHeader(f) 58 | else: 59 | usage() 60 | 61 | 62 | def generateHeader(f): 63 | undeclaredIdentifiers = set() 64 | referencedFunctions = set() 65 | 66 | for line in f.readlines(): 67 | line = line.strip() 68 | match = UNDECLARED_IDENTITIER_REGEX.match(line) 69 | if match: 70 | undeclaredIdentifiers.add(match.group(1)) 71 | 72 | match = REFERENCED_FUNCTION_REGEX.match(line) 73 | if match: 74 | referencedFunctions.add(match.group(1)) 75 | 76 | for undec in undeclaredIdentifiers: 77 | print( "int %s;" % undec ); 78 | 79 | for func in referencedFunctions: 80 | print( "int %s(int x, ...) {return 0;}" % func[1:] ) 81 | 82 | 83 | if __name__ == "__main__": 84 | main() -------------------------------------------------------------------------------- /fix_function_detection_on_arm.py: -------------------------------------------------------------------------------- 1 | # 2 | # this will attempt to create functions that were missed by IDA 3 | # 4 | from idaapi import * 5 | from idautils import * 6 | 7 | def main(): 8 | 9 | totalCreations = 0 10 | prologMneumonics = ["PUSH", "STM"] # , "LDRB", "LDR"] 11 | epilogMneumonics = ["POP", "LDM"] 12 | for segment in Segments(): 13 | seg = getseg(segment) 14 | clazz = get_segm_class(seg) 15 | if clazz != "CODE": 16 | continue 17 | startAddr = BADADDR 18 | endAddr = BADADDR 19 | for head in Heads( segment, SegEnd(segment) ): 20 | if GetFunctionFlags(head) == -1: 21 | mneumonic = GetMnem(head) 22 | operand = GetOpnd( head, 0 ) 23 | if startAddr==BADADDR and mneumonic in prologMneumonics: 24 | if "LR" in operand: 25 | assembler = GetDisasm(head) 26 | print( "Start %x: %s" % (head, assembler) ) 27 | startAddr = head 28 | endAddr = BADADDR 29 | if startAddr!=BADADDR and mneumonic in epilogMneumonics: 30 | if "PC" in operand: 31 | assembler = GetDisasm(head) 32 | print( "End %x: %s" % (head, assembler) ) 33 | endAddr = head 34 | 35 | if startAddr!=BADADDR and endAddr!=BADADDR: 36 | MakeFunction( startAddr, endAddr ) 37 | newName = Name(startAddr) 38 | print( "Created %s : 0x%x ... 0x%x" % (newName, startAddr, endAddr) ) 39 | totalCreations += 1 40 | startAddr = BADADDR 41 | endAddr = BADADDR 42 | print("Done. Created %d functions" % totalCreations ) 43 | 44 | 45 | main() -------------------------------------------------------------------------------- /ida_hexrays.h: -------------------------------------------------------------------------------- 1 | 2 | #define _copy_from_user memcpy 3 | #define _memzero(a,l) memset((void*)a,0,l) 4 | #define copy_from_user memcpy 5 | #define vfree free 6 | 7 | #define vmalloc malloc 8 | #define printk printf -------------------------------------------------------------------------------- /kallsyms_to_idc.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os 3 | import sys 4 | 5 | 6 | def usage(): 7 | print("%s " % sys.argv[0] ) 8 | print("%s /proc/kallsyms" % sys.argv[0] ) 9 | 10 | 11 | def main(): 12 | prefix = """ 13 | #define UNLOADED_FILE 1 14 | #include 15 | 16 | static main(void) 17 | { 18 | """ 19 | 20 | suffix = '}' 21 | format = '\tMakeName\t (0x%s, "%s");' 22 | 23 | regex = re.compile( r'([a-fA-Z0-9]+) \w (\w+)' ) 24 | 25 | 26 | if( len(sys.argv) != 2 ): 27 | return usage() 28 | 29 | kallsymsPath = sys.argv[1] 30 | 31 | f = open( kallsymsPath, "r" ) 32 | if None == f: 33 | print("Unable to open %s.\n", kallsymsPath ) 34 | return -3 35 | 36 | 37 | print( "// Enabling addresses...") 38 | cmd = 'echo 1 > /proc/sys/kernel/kptr_restrict' 39 | os.system( cmd ) 40 | 41 | print(prefix) 42 | lines = f.readlines() 43 | for line in lines: 44 | match = regex.match(line) 45 | if( match != None ): 46 | print( format % (match.group(1), match.group(2)) ) 47 | 48 | f.close() 49 | 50 | print(suffix) 51 | return 0 52 | 53 | if __name__ == '__main__': 54 | main() 55 | -------------------------------------------------------------------------------- /xrefs_trees.py: -------------------------------------------------------------------------------- 1 | # xref_trees.py 2 | # 3 | # This generates callgraph trees to and from the current function. 4 | # It will ignore allocs and any 5 | # functions that start with zzz_ . You can add other functions you want to 6 | # ignore in BLACKLIST 7 | # 8 | # Weston Hopkins 9 | # August 2014 10 | # 11 | 12 | from idaapi import * 13 | from idautils import * 14 | 15 | functionAddresses = {} 16 | 17 | 18 | BLACKLIST = ['alloc', 'zzz_'] 19 | 20 | ###################################################################### 21 | # dumpShit() 22 | ###################################################################### 23 | def dumpShit(obj, name=""): 24 | return 25 | Message( "%s: %s\n" % (name, obj )) 26 | for attr in dir(obj): 27 | val = None 28 | try: 29 | val = eval( "obj." + attr ) 30 | except: 31 | pass 32 | Message( "\t%s: \t%s\n" % (attr, val)) 33 | 34 | 35 | ###################################################################### 36 | # dumpXrefsFrom() 37 | ###################################################################### 38 | def dumpXrefsFrom( pc, callStack, functionCallCounts ): 39 | func = get_func(pc) 40 | 41 | if func is None: 42 | # print( "0x%08x is not at a function\n" % pc ) 43 | return 44 | 45 | func = get_func(func.startEA) 46 | dumpShit(func) 47 | functionName = Name(func.startEA) 48 | if( functionName[0] == '_' ): 49 | return 50 | for blackList in BLACKLIST: 51 | if( blackList in functionName ): 52 | return 53 | if( functionName in callStack ): 54 | return 55 | else: 56 | callStack.append(functionName) 57 | 58 | if( not functionName in functionCallCounts ): 59 | functionCallCounts[functionName] = 0 60 | 61 | functionCallCounts[functionName] = functionCallCounts[functionName] + 1 62 | functionCallCount = functionCallCounts[functionName] 63 | 64 | prefix = " |" * len(callStack) 65 | Message( prefix[:-1] + "+ " + functionName + "()" ) 66 | 67 | if( functionCallCount>1 ): 68 | Message(" ... [%d]\n" % functionCallCount ) 69 | else: 70 | Message("\n") 71 | functionCallCounts[functionName] = True 72 | items = FuncItems( func.startEA ) 73 | xrefs = [] 74 | for i in items: 75 | for xref in XrefsFrom(i, 0): 76 | if xref.type==fl_CN or xref.type==fl_CF or xref.type==fl_JF or xref.type==fl_JN: 77 | xrefName = str(xref.to) 78 | if( not xrefName in xrefs ): 79 | dumpXrefsFrom( xref.to, list(callStack), functionCallCounts ) 80 | xrefs.append(xrefName) 81 | 82 | 83 | ###################################################################### 84 | # dumpXrefsFrom() 85 | ###################################################################### 86 | def generateCallsJSONTree( pc, callStack, functionCallCounts ): 87 | func = get_func(pc) 88 | 89 | if func is None: 90 | # print( "0x%08x is not at a function\n" % pc ) 91 | return 92 | 93 | func = get_func(func.startEA) 94 | dumpShit(func) 95 | functionName = Name(func.startEA) 96 | if( functionName[0] == '_' ): 97 | return 98 | for blackList in BLACKLIST: 99 | if( blackList in functionName ): 100 | return 101 | if( functionName in callStack ): 102 | return 103 | else: 104 | callStack.append(functionName) 105 | 106 | if( not functionName in functionCallCounts ): 107 | functionCallCounts[functionName] = 0 108 | 109 | functionCallCounts[functionName] = functionCallCounts[functionName] + 1 110 | functionCallCount = functionCallCounts[functionName] 111 | 112 | prefix = " |" * len(callStack) 113 | Message( prefix[:-1] + "+ " + functionName + "()" ) 114 | 115 | if( functionCallCount>1 ): 116 | Message(" ... [%d]\n" % functionCallCount ) 117 | else: 118 | Message("\n") 119 | functionCallCounts[functionName] = True 120 | items = FuncItems( func.startEA ) 121 | xrefs = [] 122 | for i in items: 123 | for xref in XrefsFrom(i, 0): 124 | if xref.type==fl_CN or xref.type==fl_CF or xref.type==fl_JF or xref.type==fl_JN: 125 | xrefName = str(xref.to) 126 | if( not xrefName in xrefs ): 127 | dumpXrefsFrom( xref.to, list(callStack), functionCallCounts ) 128 | xrefs.append(xrefName) 129 | 130 | 131 | 132 | ###################################################################### 133 | # dumpXrefsTo() 134 | ###################################################################### 135 | def dumpXrefsTo( pc, callStack, functionCallCounts ): 136 | func = get_func(pc) 137 | 138 | if func is None: 139 | # print( "0x%08x is not at a function\n" % pc ) 140 | return 141 | 142 | func = get_func(func.startEA) 143 | dumpShit(func) 144 | functionName = Name(func.startEA) 145 | if( functionName[0] == '_' ): 146 | return 147 | for blackList in BLACKLIST: 148 | if( blackList in functionName ): 149 | return 150 | if( functionName in callStack ): 151 | return 152 | else: 153 | callStack.append(functionName) 154 | 155 | if( not functionName in functionCallCounts ): 156 | functionCallCounts[functionName] = 0 157 | 158 | functionCallCounts[functionName] = functionCallCounts[functionName] + 1 159 | functionCallCount = functionCallCounts[functionName] 160 | 161 | prefix = " |" * len(callStack) 162 | Message( prefix[:-1] + "+ " + functionName + "()" ) 163 | 164 | if( functionCallCount>1 ): 165 | Message(" ... [%d]\n" % functionCallCount ) 166 | else: 167 | Message("\n") 168 | functionCallCounts[functionName] = True 169 | xrefs = [] 170 | 171 | for xref in XrefsTo(func.startEA, 0 ): 172 | if xref.type==fl_CN or xref.type==fl_CF or xref.type==fl_JF or xref.type==fl_JN: 173 | xrefName = str(xref.frm) 174 | if( not xrefName in xrefs ): 175 | dumpXrefsTo( xref.frm, list(callStack), functionCallCounts ) 176 | xrefs.append(xrefName) 177 | 178 | functionEA = ChooseFunction("Select function to generate graph") 179 | 180 | Message( "=" * 80 + "\n" ) 181 | Message("Cross References From\n") 182 | Message( "=" * 80 + "\n" ) 183 | dumpXrefsFrom(functionEA, [], {} ) 184 | 185 | Message( "=" * 80 + "\n" ) 186 | Message("Cross References To\n") 187 | Message( "=" * 80 + "\n" ) 188 | dumpXrefsTo(functionEA, [], {} ) 189 | --------------------------------------------------------------------------------