def heaps(vdb, line): """ Show Win32 Heap Information. Usage: heaps [-F <heapaddr>] [-C <address>] [-L <segmentaddr>] -F <heapaddr> print the freelist for the heap -C <address> Find and print the heap chunk containing <address> -S <segmentaddr> Print the chunks for the given heap segment -L <heapaddr> Print the look aside list for the given heap -V Validate the heaps (check next/prev sizes and free list) -l <heapaddr> Leak detection (list probable leaked chunks) -U <heapaddr> Show un-commited ranges for the specified heap -b <heapaddr|all> Print LFH information for given heap or all (no options lists heaps and segments) """ t = vdb.getTrace() t.requireAttached() if t.getMeta('Architecture') == 'amd64': vdb.vprint("WARNING: not all 64bit heap stuff works quite right yet!") argv = e_cli.splitargs(line) freelist_heap = None chunkfind_addr = None chunklist_seg = None lookaside_heap = None leakfind_heap = None uncommit_heap = None buckets_heap = None try: opts, args = getopt.getopt(argv, "F:C:S:L:l:U:V:b:") except Exception: return vdb.do_help('heaps') for opt, optarg in opts: if opt == "-F": freelist_heap = t.parseExpression(optarg) elif opt == "-C": chunkfind_addr = t.parseExpression(optarg) elif opt == "-L": lookaside_heap = t.parseExpression(optarg) elif opt == "-S": chunklist_seg = t.parseExpression(optarg) elif opt == "-V": return validate_heaps(vdb) elif opt == "-l": leakfind_heap = t.parseExpression(optarg) elif opt == '-U': uncommit_heap = t.parseExpression(optarg) elif opt == '-b': if optarg == 'all': buckets_heap = 'all' else: buckets_heap = t.parseExpression(optarg) if lookaside_heap is not None: haddrs = [h.address for h in win32heap.getHeaps(t)] if lookaside_heap not in haddrs: vdb.vprint("0x%.8x is NOT a valid heap!" % lookaside_heap) return heap = win32heap.Win32Heap(t, lookaside_heap) vdb.vprint('[Index] [Chunks]') for i, l in enumerate(heap.getLookAsideLists()): vdb.vprint("[%d]" % i) for c in l: vdb.vprint(" %s" % (repr(c))) elif uncommit_heap is not None: haddrs = [h.address for h in win32heap.getHeaps(t)] if uncommit_heap not in haddrs: vdb.vprint("0x%.8x is NOT a valid heap!" % uncommit_heap) return heap = win32heap.Win32Heap(t, uncommit_heap) ucrdict = heap.getUCRDict() addrs = list(ucrdict.keys()) addrs.sort() if len(addrs) == 0: vdb.vprint('Heap 0x%.8x has 0 uncommited-ranges!' % uncommit_heap) return vdb.vprint('Uncommited ranges for heap: 0x%.8x' % uncommit_heap) for ucraddr in addrs: size = ucrdict.get(ucraddr) vdb.vprint('0x%.8x (%d)' % (ucraddr, size)) return elif freelist_heap is not None: haddrs = [h.address for h in win32heap.getHeaps(t)] if freelist_heap not in haddrs: vdb.vprint("0x%.8x is NOT a valid heap!" % freelist_heap) return heap = win32heap.Win32Heap(t, freelist_heap) for i, l in enumerate(heap.getFreeLists()): if len(l): vdb.vprint("Freelist Index: %d" % i) for c in l: vdb.vprint(" %s" % repr(c)) elif chunkfind_addr is not None: heap, seg, chunk = win32heap.getHeapSegChunk(t, chunkfind_addr) vdb.vprint("Address 0x%.8x found in:" % (chunkfind_addr, )) vdb.vprint("Heap: 0x%.8x" % (heap.address)) vdb.vprint("Segment: 0x%.8x" % (seg.address)) vdb.vprint("Chunk: 0x%.8x (%d) FLAGS: %s" % (chunk.address, len(chunk), chunk.reprFlags())) elif chunklist_seg is not None: for heap in win32heap.getHeaps(t): for seg in heap.getSegments(): if chunklist_seg == seg.address: vdb.vprint("Chunks for segment at 0x%.8x (X == in use)" % chunklist_seg) for chunk in seg.getChunks(): c = " " if chunk.isBusy(): c = "X" vdb.vprint("0x%.8x %s (%d)" % (chunk.address, c, len(chunk))) return vdb.vprint("Segment 0x%.8x not found!" % chunklist_seg) elif leakfind_heap is not None: # FIXME do this the slow way for now... haddrs = [h.address for h in win32heap.getHeaps(t)] if leakfind_heap not in haddrs: vdb.vprint("0x%.8x is NOT a valid heap!" % leakfind_heap) return h = win32heap.Win32Heap(t, leakfind_heap) for seg in h.getSegments(): for chunk in seg.getChunks(): if chunk.address == seg.address: continue # Obviously, only check for leaks if they are in use... # FIXME we will need to check the lookaside also... if not chunk.isBusy(): continue addr = chunk.getDataAddress() # FIXME get size and endian from trace pat = e_bits.buildbytes(addr, 4) l = t.searchMemory(pat) if len(l) == 0: vdb.vprint("0x%.8x may be leaked!" % addr) elif buckets_heap is not None: headers = '{0:10} {1:10} {2:10} {3:>8} {4:>8} {5:>8} {6:>8}'.format( 'Heap', 'SubSeg', 'UserData', 'Index', 'Size', 'Total', 'Free') vdb.vprint(headers) for heap in win32heap.getHeaps(t): if buckets_heap != heap.address and buckets_heap != 'all': continue if heap.isLFH(): lfh = heap.getLFH() for s in lfh.getSubsegments(): row = '{0:<#10x} {1:<#10x} {2:<#10x} {3:>#8x} {4:>#8x} {5:>#8x} {6:>#8x}'.format( heap.address, s.address, s.getUserBlocks(), s.getSizeIndex(), s.getBucketSize(), s.getBlockCount(), s.getFreeBlockCount()) vdb.vprint(row) else: row = '{0:<#10x} {1:<10} {2:^10} {3:>8} {4:>8} {5:>8} {5:>8}'.format( heap.address, 'No LFH', '-', '-', '-', '-') vdb.vprint(row) else: vdb.vprint("Heap\t\tSegment") for heap in win32heap.getHeaps(t): lfh = '' flags = " ".join(heap.getFlagNames()) if heap.isLFH(): lfh = 'LFH' for s in heap.getSegments(): vdb.vprint("0x%.8x\t0x%.8x\t%s\t%s" % (heap.address, s.address, flags, lfh))
elif opt == "-L": lookaside_heap = t.parseExpression(optarg) elif opt == "-S": chunklist_seg = t.parseExpression(optarg) elif opt == "-V": return validate_heaps(vdb) elif opt == "-l": leakfind_heap = t.parseExpression(optarg) if lookaside_heap != None: haddrs = [h.address for h in win32heap.getHeaps(t)] if lookaside_heap not in haddrs: vdb.vprint("0x%.8x is NOT a valid heap!" % lookaside_heap) return heap = win32heap.Win32Heap(t, lookaside_heap) for i, l in enumerate(heap.getLookAsideLists()): if len(l): vdb.vprint("Lookaside Index: %d" % i) for c in l: vdb.vprint(" %s" % (repr(c))) elif freelist_heap != None: haddrs = [h.address for h in win32heap.getHeaps(t)] if freelist_heap not in haddrs: vdb.vprint("0x%.8x is NOT a valid heap!" % freelist_heap) return heap = win32heap.Win32Heap(t, freelist_heap) for i, l in enumerate(heap.getFreeLists()): if len(l):