def validate_heaps(db): """ A simple routine that works like the built in windows heap checkers to show where blocks and/or freelist is potentially dorked. """ trace = db.getTrace() db.vprint("Validating:") for heap in win32heap.getHeaps(trace): db.vprint("%s: 0x%.8x" % ("heap".rjust(9), heap.address)) try: f = heap.getFreeLists() except Exception as e: db.vprint("%s: %s" % (e.__class__.__name__, e)) for seg in heap.getSegments(): db.vprint("%s: 0x%.8x" % ("segment".rjust(9), seg.address)) try: blist = seg.getChunks() for i, chunk in enumerate(blist): if i == 0: continue if heap._win7_heap: continue pchunk = blist[i - 1] if chunk.chunk.PreviousSize != pchunk.chunk.Size: db.vprint( 'Corruption! (block at 0x%.8x (size: %d) block at 0x%.8x (prevsize: %d)' % (pchunk.address, pchunk.chunk.Size, chunk.address, chunk.chunk.PreviousSize)) break except Exception as e: db.vprint("%s: %s" % (e.__class__.__name__, e))
def validate_heaps(db): """ A simple routine that works like the built in windows heap checkers to show where blocks and/or freelist is potentially dorked. """ trace = db.getTrace() db.vprint("Validating:") for heap in win32heap.getHeaps(trace): db.vprint("%s: 0x%.8x" % ("heap".rjust(9), heap.address)) try: f = heap.getFreeLists() except Exception, e: #import traceback #traceback.print_exc() db.vprint("%s: %s" % (e.__class__.__name__,e)) for seg in heap.getSegments(): db.vprint("%s: 0x%.8x" % ("segment".rjust(9),seg.address)) try: blist = seg.getChunks() for i,chunk in enumerate(blist): if i == 0: continue if heap._win7_heap: continue pchunk = blist[i-1] if chunk.chunk.PreviousSize != pchunk.chunk.Size: db.vprint('Corruption! (block at 0x%.8x (size: %d) block at 0x%.8x (prevsize: %d)' % (pchunk.address, pchunk.chunk.Size, chunk.address, chunk.chunk.PreviousSize)) break except Exception, e: db.vprint("%s: %s" % (e.__class__.__name__,e))
def updateHeapTree(self, tree=None): if tree == None: tree = self.getWidget("Win32HeapTree") t = self.vdb.getTrace() model = tree.get_model() model.clear() if not t.isAttached(): return # Populate the heap list for h in win32heap.getHeaps(t): #i = model.append(None, (h,"0x%.8x" % h.address,"0x%.8x" % int(h.heap.Flags),"")) for s in h.getSegments(): i = model.append(None, (s, "0x%.8x" % h.address,"0x%.8x" % s.address)) model.append(i, (None, repr(h.heap), repr(s.seg)))
def updateHeapTree(self, tree=None): if tree == None: tree = self.getWidget("Win32HeapTree") t = self.vdb.getTrace() model = tree.get_model() model.clear() if not t.isAttached(): return # Populate the heap list for h in win32heap.getHeaps(t): #i = model.append(None, (h,"0x%.8x" % h.address,"0x%.8x" % int(h.heap.Flags),"")) for s in h.getSegments(): i = model.append( None, (s, "0x%.8x" % h.address, "0x%.8x" % s.address)) model.append(i, (None, repr(h.heap), repr(s.seg)))
def validate_heaps(db): """ A simple routine that works like the built in windows heap checkers to show where blocks and/or freelist is potentially dorked. """ trace = db.getTrace() db.vprint("Validating:") for heap in win32heap.getHeaps(trace): db.vprint("%s: 0x%.8x" % ("heap".rjust(9), heap.address)) try: f = heap.getFreeLists() except Exception, e: db.vprint("%s: %s" % (e.__class__.__name__, e)) for seg in heap.getSegments(): db.vprint("%s: 0x%.8x" % ("segment".rjust(9), seg.address)) try: blist = seg.getChunks() except Exception, e: db.vprint("%s: %s" % (e.__class__.__name__, e))
def validate_heaps(db): """ A simple routine that works like the built in windows heap checkers to show where blocks and/or freelist is potentially dorked. """ trace = db.getTrace() db.vprint("Validating:") for heap in win32heap.getHeaps(trace): db.vprint("%s: 0x%.8x" % ("heap".rjust(9), heap.address)) try: f = heap.getFreeLists() except Exception, e: db.vprint("%s: %s" % (e.__class__.__name__,e)) for seg in heap.getSegments(): db.vprint("%s: 0x%.8x" % ("segment".rjust(9),seg.address)) try: blist = seg.getChunks() except Exception, e: db.vprint("%s: %s" % (e.__class__.__name__,e))
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) 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) 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 != None: haddrs = [h.address for h in win32heap.getHeaps(t)] if uncommit_heap not in haddrs:
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))
chunklist_seg = t.parseExpression(optarg) if freelist_heap != None: print "NOT YET" elif chunkfind_addr != 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 != 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) else:
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) 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)
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 as e: 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 != 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 != 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 != 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 != 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 != 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 != 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 != 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": chunklist_seg = t.parseExpression(optarg) if freelist_heap != None: print "NOT YET" elif chunkfind_addr != 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 != 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) else: vdb.vprint("Heap\t\tSegment") for heap in win32heap.getHeaps(t):