require 'metasm' class VM < Metasm::VirtualString attr_accessor :set def initialize(off=0, sz=1<<32, set=[]) @set = set super(off, sz) end def add_section(base, raw) @set << [base, raw] end def get_page(off, len=@pagelength) if s = @set.find { |ss| ss[0] <= off and ss[0] + ss[1].length > off } s[1][off-s[0], len] end end end class Heap attr_accessor :vm, :chunks def initialize(vm) @vm = vm # base (userdata_ptr) => len (userlen roundup(8)) @chunks = {} end def decode_uint(addr=nil) @ptr = addr if addr @ptr += 4 @vm[@ptr-4, 4].unpack('L').first end def decode_2uint(addr=nil) [decode_uint(addr), decode_uint] end # scan chunks from a heap base addr def scan_heap(base) psz, sz = decode_2uint(base) raise "bad heap base %x %x" % [psz, sz] if psz != 0 or sz & 1 == 0 until psz == 0 and sz == 0 ptr = @ptr len = (sz & -8) - 8 @ptr += len psz, sz = decode_2uint if sz & 1 > 0 puts "used #{'%x' % ptr} #{len}" if $VERBOSE @chunks[ptr] = len else puts "free #{'%x' % ptr} #{len}" if $VERBOSE end end end # scan chunks from a mmap base addr # big chunks are allocated on anonymous-mmap areas # for mmap chunks, pv_sz=0 pv_inuse=0, mmap=1, data starts at 8, and a dword is appendend for pv_sz # mmap size arg = data len + 4*sz roundup(4096) # one entry in /proc/pid/maps may point to multiple consecutive mmap chunks def scan_mmap(base) loop do # XXX could fall into non-mmapmalloc stuff psz, sz = decode_2uint(base) break if psz == 0 and sz == 0 raise "bad mmap base %x %x" % [psz, sz] if psz != 0 or sz & 2 == 0 ptr = @ptr len = (sz & -8) - 8 @ptr += len psz = decode_uint raise "bad mmap psz %x" % psz if psz & 1 != 1 puts "mmap #{'%x' % ptr} #{len}" if $VERBOSE @chunks[ptr] = len end end end vm = VM.new vm.add_section(0x0b3fb000, File.open("df_heap_0b3fb000.raw", 'rb') { |fd| fd.read }) heapbase = 0xb3fb000 heap = Heap.new(vm) heap.scan_heap(heapbase) puts heap.chunks.length __END__ def tm(name) t0 = Time.now r = yield puts "#{name} #{'%.02f' % (Time.now-t0)}" r end def scan_xrefs Metasm::DynLdr.new_func_c <= max) tab[i] = 0; } EOS dwd = heapraw.dup Metasm::DynLdr.filt(dwd, dwd.length/4, base, base+dwd.length) dwd = dwd.unpack('L*') dwd.delete 0 end