"""diff_owner_scans.py Run the owner-vtable scan internally on two dumps and emit a diff: which vtables show up disproportionately in the high-leak dump versus the low-leak baseline. The leaders are residual-leak suspects. Output is ranked by ratio (high_count / low_count) with min thresholds to filter noise. """ import struct, sys from collections import Counter, defaultdict from minidump.minidumpfile import MinidumpFile def _ei(v): if v is None: return 0 if hasattr(v, 'value'): return int(v.value) return int(v) def scan(path): md = MinidumpFile.parse(path) reader = md.get_reader().get_buffered_reader() mods = [(m.baseaddress, m.size, m.name) for m in md.modules.modules] def mod_of(addr): for b, s, n in mods: if b <= addr < b + s: return n.split("\\")[-1] return None image_ranges = [] for r in md.memory_info.infos: st, ty = _ei(r.State), _ei(r.Type) if st == 0x1000 and ty == 0x1000000: image_ranges.append((r.BaseAddress, r.BaseAddress + r.RegionSize)) image_ranges.sort() def is_image(addr): for lo, hi in image_ranges: if lo <= addr < hi: return True if addr < lo: return False return False leaked = [] for r in md.memory_info.infos: st, ty, pr = _ei(r.State), _ei(r.Type), _ei(r.Protect) & 0xff if st == 0x1000 and ty == 0x20000 and pr in (0x04, 0x40) \ and 256*1024 <= r.RegionSize < 512*1024: leaked.append((r.BaseAddress, r.RegionSize)) deltas = [0, 8, 0x10, 0x18, 0x20, 0x28, 0x30, 0x40, 0x50, 0x60] cand_to_region = {} for base, _sz in leaked: for d in deltas: cand_to_region[base + d] = base scan_regions = [] for r in md.memory_info.infos: st, ty, pr = _ei(r.State), _ei(r.Type), _ei(r.Protect) & 0xff if st != 0x1000: continue if ty == 0x1000000: continue if pr not in (0x04, 0x40): continue scan_regions.append((r.BaseAddress, r.RegionSize)) hits = [] for base, size in scan_regions: try: reader.move(base) buf = reader.read(size) except Exception: continue if not buf: continue end = (len(buf) // 4) * 4 for off in range(0, end, 4): v = struct.unpack_from(" 0x10000000: continue if is_image(v): vtable_only[v] += 1 field_per_vt[v][off - back] += 1 break return { "leaked_regions": len(leaked), "vtable_counts": vtable_only, "field_per_vt": field_per_vt, "mod_of": mod_of, } def main(): low_path, high_path = sys.argv[1], sys.argv[2] print(f"Scanning low-leak control: {low_path}") low = scan(low_path) print(f" leaked regions: {low['leaked_regions']}") print(f" unique vtables: {len(low['vtable_counts'])}") print() print(f"Scanning high-leak target: {high_path}") high = scan(high_path) print(f" leaked regions: {high['leaked_regions']}") print(f" unique vtables: {len(high['vtable_counts'])}") print() # Baseline scale = ratio of leaked-region counts. Anything growing # significantly faster than this is suspect. scale = high['leaked_regions'] / max(low['leaked_regions'], 1) print(f"Baseline scale (high_leaked / low_leaked): {scale:.1f}x") print(f" vtables with ratio >> {scale:.0f}x are the residual-leak suspects") print() # Combine vtable counts all_vts = set(low['vtable_counts']) | set(high['vtable_counts']) # Rank by ratio with a minimum high-count floor so we ignore one-offs MIN_HIGH = 20 rows = [] for vt in all_vts: lc = low['vtable_counts'].get(vt, 0) hc = high['vtable_counts'].get(vt, 0) if hc < MIN_HIGH: continue # Avoid divide-by-zero; treat 0 baseline as huge ratio ratio = hc / max(lc, 0.5) delta = hc - lc rows.append((vt, lc, hc, ratio, delta)) # Top by ratio rows.sort(key=lambda r: r[3], reverse=True) print(f"=== Top vtables by ratio (high/low), min high-count {MIN_HIGH} ===") print(f"{'vtable':<12} {'low':>6} {'high':>6} {'ratio':>8} {'delta':>7} module fields") for vt, lc, hc, ratio, delta in rows[:30]: mod = high['mod_of'](vt) or "?" top_offs = high['field_per_vt'][vt].most_common(3) offs_str = " ".join(f"+0x{o:x}={c}" for o, c in top_offs) print(f"0x{vt:08x} {lc:>6} {hc:>6} {ratio:>8.1f} {delta:>7} {mod:<40} {offs_str}") # Top by absolute delta (also informative) print() print(f"=== Top vtables by absolute count delta (high - low) ===") rows.sort(key=lambda r: r[4], reverse=True) print(f"{'vtable':<12} {'low':>6} {'high':>6} {'ratio':>8} {'delta':>7} module") for vt, lc, hc, ratio, delta in rows[:20]: mod = high['mod_of'](vt) or "?" print(f"0x{vt:08x} {lc:>6} {hc:>6} {ratio:>8.1f} {delta:>7} {mod}") if __name__ == "__main__": main()