def parse_general(): global jeheap try: jeheap.narenas = dbg.to_int(dbg.get_value("narenas")) except: print("[shadow] error: symbol narenas not found") sys.exit() try: jeheap.nbins = dbg.to_int(dbg.get_value("nbins")) except: # XXX: these are firefox specific, we must add support for more # jemalloc variants in the future if sys.platform == "darwin" or sys.platform == "win32": jeheap.ntbins = dbg.to_int(dbg.get_value("ntbins")) jeheap.nsbins = dbg.to_int(dbg.get_value("nsbins")) jeheap.nqbins = dbg.to_int(dbg.get_value("nqbins")) jeheap.nbins = jeheap.ntbins + jeheap.nsbins + jeheap.nqbins else: if jeheap.DWORD_SIZE == 4: jeheap.nbins = 36 elif jeheap.DWORD_SIZE == 8: jeheap.nbins = 35 # XXX: figure out how to calculate the chunk size correctly, this is # firefox specific jeheap.chunk_size = 1 << 20
def parse_general(): global jeheap try: jeheap.narenas = dbg.to_int(dbg.get_value('narenas')) except: print('[shadow] error: symbol narenas not found') sys.exit() try: jeheap.nbins = dbg.to_int(dbg.get_value('nbins')) except: # XXX: these are firefox specific, we must add support for more # jemalloc variants in the future if sys.platform == 'darwin' or sys.platform == 'win32': jeheap.ntbins = dbg.to_int(dbg.get_value('ntbins')) jeheap.nsbins = dbg.to_int(dbg.get_value('nsbins')) jeheap.nqbins = dbg.to_int(dbg.get_value('nqbins')) jeheap.nbins = jeheap.ntbins + jeheap.nsbins + jeheap.nqbins else: if jeheap.DWORD_SIZE == 4: jeheap.nbins = 36 elif jeheap.DWORD_SIZE == 8: jeheap.nbins = 35 # XXX: figure out how to calculate the chunk size correctly, this is # firefox specific jeheap.chunk_size = 1 << 20
def invoke(self, arg, from_tty): arg = arg.split() if len(arg) >= 2 and arg[0] == '-m': view_maps = True addr = arg[1] elif len(arg) == 1: view_maps = False addr = arg[0] else: print('[shadow] usage: jerun <address>') print('[shadow] usage: jerun -m <address>') print('[shadow] for example: jerun 0x087e1000') return try: if addr.startswith('0x'): addr = dbg.to_int(addr) else: addr = dbg.to_int('0x%s' % (addr)) except: print('[shadow] invalid address parameter') return shadow.dump_run(addr, view_maps)
def invoke(self, arg, from_tty): arg = arg.split() try: if arg[0].startswith('0x'): addr = dbg.to_int(arg[0]) else: addr = dbg.to_int('0x%s' % (arg[0])) except: print('[shadow] usage: jeinfo <address>') print('[shadow] for example: jeinfo 0x079e5440') return shadow.dump_address(addr)
def invoke(self, arg, from_tty): arg = arg.split() try: addr = arg[0] if addr.startswith('0x'): addr = dbg.to_int(addr) else: addr = dbg.to_int('0x%s' % (addr)) except: print('[shadow] usage: jechunk <address>') print('[shadow] for example: jechunk 0x900000') return shadow.dump_chunk(addr)
def parse_nursery(proc = none): '''Parse the current SpiderMonkey's JSRuntime GC nursery''' global dbg_engine global nursery_heap lines = dbg.eval_expr(dbg.nursery_expr).split('\n') if dbg_engine == 'pykd': for line in lines: if line.find('runtime_') != -1: start = line.find(': 0x') subline = line[(start + 2):] end = subline.find(' ') nursery_heap.jsruntime_addr = dbg.to_int(subline[:end]) continue if line.find('position_') != -1: start = line.find(': 0x') subline = line[(start + 2):] nursery_heap.next_free_addr = dbg.to_int(subline) continue if line.find('heapStart_') != -1: start = line.find(': 0x') subline = line[(start + 2):] nursery_heap.start_addr = dbg.to_int(subline) continue if line.find('heapEnd_') != -1: start = line.find(': 0x') subline = line[(start + 2):] nursery_heap.end_addr = dbg.to_int(subline) continue elif dbg_engine == 'gdb': # XXX: not implemented yet pass else: # lldb # XXX: not implemented yet pass nursery_heap.size = nursery_heap.end_addr - nursery_heap.start_addr
def parse_nursery(proc=none): '''Parse the current SpiderMonkey's JSRuntime GC nursery''' global dbg_engine global nursery_heap lines = dbg.eval_expr(dbg.nursery_expr).split('\n') if dbg_engine == 'pykd': for line in lines: if line.find('runtime_') != -1: start = line.find(': 0x') subline = line[(start + 2):] end = subline.find(' ') nursery_heap.jsruntime_addr = dbg.to_int(subline[:end]) continue if line.find('position_') != -1: start = line.find(': 0x') subline = line[(start + 2):] nursery_heap.next_free_addr = dbg.to_int(subline) continue if line.find('heapStart_') != -1: start = line.find(': 0x') subline = line[(start + 2):] nursery_heap.start_addr = dbg.to_int(subline) continue if line.find('heapEnd_') != -1: start = line.find(': 0x') subline = line[(start + 2):] nursery_heap.end_addr = dbg.to_int(subline) continue elif dbg_engine == 'gdb': # XXX: not implemented yet pass else: # lldb # XXX: not implemented yet pass nursery_heap.size = nursery_heap.end_addr - nursery_heap.start_addr
def parse_nursery(proc=none): """Parse the current SpiderMonkey's JSRuntime GC nursery""" global dbg_engine global nursery_heap lines = dbg.eval_expr(dbg.nursery_expr).split("\n") if dbg_engine == "pykd": for line in lines: if line.find("runtime_") != -1: start = line.find(": 0x") subline = line[(start + 2) :] end = subline.find(" ") nursery_heap.jsruntime_addr = dbg.to_int(subline[:end]) continue if line.find("position_") != -1: start = line.find(": 0x") subline = line[(start + 2) :] nursery_heap.next_free_addr = dbg.to_int(subline) continue if line.find("heapStart_") != -1: start = line.find(": 0x") subline = line[(start + 2) :] nursery_heap.start_addr = dbg.to_int(subline) continue if line.find("heapEnd_") != -1: start = line.find(": 0x") subline = line[(start + 2) :] nursery_heap.end_addr = dbg.to_int(subline) continue elif dbg_engine == "gdb": # XXX: not implemented yet pass else: # lldb # XXX: not implemented yet pass nursery_heap.size = nursery_heap.end_addr - nursery_heap.start_addr
def parse_chunks(): global jeheap global dbg_engine # delete the chunks' list jeheap.chunks[:] = [] try: root = dbg.to_int(dbg.eval_expr(dbg.chunk_rtree_root_expr)) height = dbg.to_int(dbg.eval_expr(dbg.chunk_rtree_height_expr)) level2bits = [] for i in range(0, height): expr = dbg.chunk_rtree_level2bits_expr % (i) level2bits.append(dbg.to_int(dbg.eval_expr(expr))) except: print("[shadow] error: cannot parse chunk radix tree") sys.exit() # XXX: check if we're running on x86_64, # not required for windbg/pykd (see the dp command) if jeheap.DWORD_SIZE == 8: if dbg_engine == "gdb": dw_fmt = "g" else: # lldb dw_fmt = "XXX" else: if dbg_engine == "gdb": dw_fmt = "w" else: # lldb dw_fmt = "XXX" # parse the radix tree using a stack stack = [(root, 0)] while len(stack): (node, node_height) = stack.pop() child_cnt = 1 << level2bits[node_height] if dbg_engine == "gdb": expr = dbg.chunk_radix_expr % (child_cnt, dw_fmt, node) elif dbg_engine == "pykd": if dbg.get_arch() == "x86": child_cnt = child_cnt / 6 expr = dbg.chunk_radix_expr % (node, child_cnt) else: # lldb expr = "" dump = dbg.execute(expr) for line in dump.split("\n"): line = line[line.find(dbg.address_separator) + len(dbg.address_separator) :] for address in line.split(): try: address = int(address, 16) except: address = 0 if address != 0: # leaf nodes hold pointers to actual values if node_height == height - 1: expr = dbg.chunk_arena_expr % address try: arena_addr = dbg.to_int(dbg.eval_expr(expr)) except: arena_addr = 0 exists = false if arena_addr in [i.addr for i in jeheap.arenas]: exists = true if exists: jeheap.chunks.append(jemalloc.arena_chunk(address, arena_addr)) else: jeheap.chunks.append(jemalloc.arena_chunk(address)) # non-leaf nodes are inserted in the stack else: stack.append((address, node_height + 1))
def parse_chunks(): global jeheap global dbg_engine # delete the chunks' list jeheap.chunks[:] = [] try: root = dbg.to_int(dbg.eval_expr(dbg.chunk_rtree_root_expr)) height = dbg.to_int(dbg.eval_expr(dbg.chunk_rtree_height_expr)) level2bits = [] for i in range(0, height): expr = dbg.chunk_rtree_level2bits_expr % (i) level2bits.append(dbg.to_int(dbg.eval_expr(expr))) except: print('[shadow] error: cannot parse chunk radix tree') sys.exit() # XXX: check if we're running on x86_64, # not required for windbg/pykd (see the dp command) if jeheap.DWORD_SIZE == 8: if dbg_engine == 'gdb': dw_fmt = 'g' else: # lldb dw_fmt = 'XXX' else: if dbg_engine == 'gdb': dw_fmt = 'w' else: # lldb dw_fmt = 'XXX' # parse the radix tree using a stack stack = [(root, 0)] while len(stack): (node, node_height) = stack.pop() child_cnt = 1 << level2bits[node_height] if dbg_engine == 'gdb': expr = dbg.chunk_radix_expr % (child_cnt, dw_fmt, node) elif dbg_engine == 'pykd': child_cnt = child_cnt / 6 # XXX: is this correct on 64-bits? expr = dbg.chunk_radix_expr % (node, child_cnt) else: # lldb expr = '' dump = dbg.execute(expr) for line in dump.split('\n'): line = line[line.find(dbg.address_separator) + \ len(dbg.address_separator):] for address in line.split(): try: address = int(address, 16) except: address = 0 if address != 0: # leaf nodes hold pointers to actual values if node_height == height - 1: expr = dbg.chunk_arena_expr % address try: arena_addr = dbg.to_int(dbg.eval_expr(expr)) except: arena_addr = 0 exists = false if arena_addr in [i.addr for i in jeheap.arenas]: exists = true if exists: jeheap.chunks.append( jemalloc.arena_chunk(address, arena_addr)) else: jeheap.chunks.append(jemalloc.arena_chunk(address)) # non-leaf nodes are inserted in the stack else: stack.append((address, node_height + 1))
def parse_all_runs(proc=none): global jeheap global dbg_engine # number of pages a chunk occupies chunk_npages = jeheap.chunk_size >> 12 # offset of bits in arena_chunk_map_t in double words bitmap_offset = dbg.offsetof('arena_chunk_map_t', 'bits') / jeheap.DWORD_SIZE # number of double words occupied by an arena_chunk_map_t chunk_map_dwords = (bitmap_offset / jeheap.DWORD_SIZE) + 1 if jeheap.DWORD_SIZE == 8: if dbg_engine == 'gdb': dword_fmt = 'g' else: # lldb dw_fmt = 'XXX' else: if dbg_engine == 'gdb': dword_fmt = 'w' else: # lldb dw_fmt = 'XXX' # the 12 least significant bits of each bitmap entry hold # various flags for the corresponding run flags_mask = (1 << 12) - 1 # delete the heap's runs' array jeheap.runs[:] = [] for chunk in jeheap.chunks: if not chunk.arena_addr: continue try: if dbg_engine == 'gdb': # parse the whole map at once to avoid gdb's delays expr = dbg.chunk_map_expr % \ (chunk_npages * chunk_map_dwords, dword_fmt, chunk.addr) elif dbg_engine == 'pykd': chunk_map_len = (chunk_npages * chunk_map_dwords) / 4 chunk_map_addr = dbg.to_int( dbg.eval_expr(dbg.chunk_map_expr % (chunk.addr))) expr = dbg.chunk_map_dump_expr % (chunk_map_addr, chunk_map_len) else: # lldb expr = '' except: print('[shadow] error: cannot read bitmap from chunk 0x%08x' % (chunk.addr)) sys.exit() lines = (dbg.execute(expr)).split('\n') dwords = [] i = 0 for line in lines: dwords += [int(dw, 16) for dw in \ line[line.find(dbg.address_separator) + \ len(dbg.address_separator):].split()] bitmap = [dwords[i] for i in range(int(bitmap_offset), \ int(len(dwords)), int(bitmap_offset + 1))] # traverse the bitmap for mapelm in bitmap: flags = mapelm & flags_mask # flags == 1 means the chunk is small and the rest of the bits # hold the actual run address if flags == 1: addr = mapelm & ~flags_mask size = dbg.get_page_size() # flags = 3 indicates a large chunk; calculate the run's address # directly from the map element index and extract the run's size elif flags == 3: addr = chunk.addr + i * dbg.get_page_size() size = mapelm & ~flags_mask # run is not allocated? skip it else: continue if addr not in [r.start for r in jeheap.runs]: new_run = parse_run(addr, proc) if new_run == none: pass else: jeheap.runs.append(copy.deepcopy(new_run))
def parse_run(run_addr, proc=none): '''Given a run's address return a jemalloc.arena_run object''' global jeheap new_run = jemalloc.arena_run() new_run.start = run_addr try: new_run.bin_addr = dbg.read_memory(new_run.start, jeheap.DWORD_SIZE, proc) if jeheap.STANDALONE == false: new_run.size = dbg.read_memory(new_run.bin_addr + \ (6 * jeheap.DWORD_SIZE), jeheap.DWORD_SIZE, proc) new_run.end = new_run.start + new_run.size new_run.region_size = dbg.read_memory(new_run.bin_addr + \ (5 * jeheap.DWORD_SIZE), jeheap.DWORD_SIZE, proc) new_run.total_regions = dbg.read_memory(new_run.bin_addr + \ (7 * jeheap.DWORD_SIZE), jemalloc.INT_SIZE, proc) if new_run.total_regions > 10000 or new_run.total_regions <= 0: return none except: # print('[shadow] error parsing the metadata of run 0x%08x' % (run_addr)) return none # XXX: this isn't correct on jemalloc standalone *debug* variant try: new_run.free_regions = dbg.read_memory(new_run.start + \ jeheap.DWORD_SIZE + jemalloc.INT_SIZE, jemalloc.INT_SIZE, proc) except: # print('[shadow] error parsing the free regions of run 0x%08x' % (run_addr)) new_run.free_regions = 0 if new_run.free_regions < 0: new_run.free_regions = 0 # delete the run's regions new_run.regions[:] = [] # parse the run's regions new_run.reg0_offset = dbg.read_memory(new_run.bin_addr + \ (9 * jeheap.DWORD_SIZE), jeheap.DWORD_SIZE, proc) if new_run.reg0_offset > 10000 or new_run.reg0_offset <= 0: return none first_region_addr = reg0_addr = run_addr + new_run.reg0_offset regs_mask_bits = (new_run.total_regions / 8) + 1 regs_mask_addr = 0 regs_mask_str = '' if dbg_engine == 'gdb': regs_mask_addr = dbg.to_int(dbg.execute(dbg.regs_mask_addr_expr % \ (run_addr))) regs_mask_str = dbg.execute(dbg.regs_mask_addr_bits_expr % \ (regs_mask_bits, regs_mask_addr)) elif dbg_engine == 'pykd': regs_mask_addr = dbg.to_int(dbg.eval_expr(dbg.regs_mask_addr_expr % \ (run_addr))) regs_mask_str = dbg.execute(dbg.regs_mask_addr_bits_expr % \ (regs_mask_addr, regs_mask_bits)) else: # lldb regs_mask_str = '' regs_mask = '' if dbg_engine == 'gdb': for line in regs_mask_str.splitlines(): line = line[line.find(dbg.address_separator) + \ len(dbg.address_separator) : line.find('\n')] line = line.replace('\n', '') line = line.replace('\t', '') line = line.replace(' ', '') regs_mask += line elif dbg_engine == 'pykd': lines = regs_mask_str.splitlines() lines = lines[2:] for line in lines: line = line[line.find(dbg.address_separator) + \ len(dbg.address_separator) : \ line.rfind(dbg.address_separator)] line = line.replace('\n', '') line = line.replace('\t', '') line = line.replace(' ', '') regs_mask += line else: # lldb regs_mask = '' new_run.regs_mask = regs_mask first_region = jemalloc.region(0, first_region_addr, \ int(new_run.regs_mask[0])) try: first_region.content_preview = hex(dbg.read_memory(first_region.addr, \ jemalloc.INT_SIZE, proc)).rstrip('L') except: print('[shadow] error reading the first dword of region 0x%08x' \ % (first_region.addr)) first_region.content_preview = '' new_run.regions.append(first_region) for i in range(1, new_run.total_regions): try: current_region = jemalloc.region(i, 0, int(new_run.regs_mask[i])) except: current_region = jemalloc.region(i, 0, 0) current_region.addr = reg0_addr + (i * new_run.region_size) try: current_region.content_preview = \ hex(dbg.read_memory(current_region.addr, jemalloc.INT_SIZE, proc)).rstrip('L') except: current_region.content_preview = '' new_run.regions.append(current_region) return new_run
def parse_arenas(): global jeheap jeheap.arenas[:] = [] for i in range(0, jeheap.narenas): current_arena = jemalloc.arena(0, i, []) try: current_arena.addr = \ dbg.to_int(dbg.eval_expr(dbg.arena_expr % (i))) except: print('[shadow] error: cannot evaluate arenas[%d]') % (i) sys.exit() for j in range(0, jeheap.nbins): nrg = 0 run_sz = 0 reg_size = 0 reg_offset = 0 end_addr = 0 try: expr = dbg.arena_reg_size_expr % (i, j) reg_size = dbg.to_int(dbg.eval_expr(expr)) expr = dbg.arena_reg0_offset_expr % (i, j) reg_offset = dbg.to_int(dbg.eval_expr(expr)) except: # XXX: for now assume it's a standalone variant; we # need to do some error checking here too. jeheap.STANDALONE = true expr = dbg.arena_bin_info_reg_size_expr % (j) reg_size = dbg.to_int(dbg.eval_expr(expr)) expr = dbg.arena_bin_info_nregs_expr % (j) nrg = dbg.to_int(dbg.eval_expr(expr)) expr = dbg.arena_bin_info_run_size_expr % (j) run_sz = dbg.to_int(dbg.eval_expr(expr)) try: expr = dbg.arena_runcur_expr % (i, j) runcur_addr = runcur = dbg.to_int(dbg.eval_expr(expr)) end_addr = runcur_addr + run_sz if runcur != 0: current_run = \ jemalloc.arena_run(runcur, end_addr, run_sz, 0, \ int(reg_size), reg_offset, nrg, 0, []) current_bin = jemalloc.arena_bin(0, j, current_run) current_bin.addr = \ dbg.to_int(dbg.eval_expr(dbg.arena_bin_addr_expr % (i, j))) current_arena.bins.append(current_bin) else: # no regions for this size class yet, therefore no runcur current_run = jemalloc.arena_run() current_bin = jemalloc.arena_bin(0, j, current_run) current_arena.bins.append(current_bin) except: current_run = jemalloc.arena_run() current_bin = jemalloc.arena_bin(0, j, current_run) current_arena.bins.append(current_bin) continue # add arena to the list of arenas jeheap.arenas.append(current_arena)
def search(search_for, region_size=0, search_current_runs=false, quick_search=false, proc=none): """Search the jemalloc heap for the given hex value""" global jeheap load_jeheap(proc) results = [] if search_current_runs == true: if region_size == 0: print("[shadow] searching all current runs for %s" % (search_for)) else: print("[shadow] searching all current runs of size class %d for %s" % (region_size, search_for)) for i in range(0, len(jeheap.arenas)): for j in range(0, len(jeheap.arenas[i].bins)): try: if region_size == 0: results.extend( dbg.search(jeheap.arenas[i].bins[j].run.start, jeheap.arenas[i].bins[j].run.end, search_for) ) else: if jeheap.arenas[i].bins[j].run.region_size == region_size: results.extend( dbg.search( jeheap.arenas[i].bins[j].run.start, jeheap.arenas[i].bins[j].run.end, search_for ) ) except: continue else: if region_size == 0: print("[shadow] searching all chunks for %s" % (search_for)) for chunk in jeheap.chunks: try: results.extend(dbg.search(chunk.addr, chunk.addr + jeheap.chunk_size, search_for)) except: continue else: print("[shadow] searching all non-current runs of size class %d for %s" % (region_size, search_for)) for i in range(0, len(jeheap.runs)): try: if region_size == 0: results.extend(dbg.search(jeheap.runs[i].start, jeheap.runs[i].end, search_for)) else: if jeheap.runs[i].region_size == region_size: results.extend(dbg.search(jeheap.runs[i].start, jeheap.runs[i].end, search_for)) except: continue # display results if not results: print("[shadow] value %s not found" % (search_for)) return for (where, start_addr) in results: if search_current_runs == true: ainfo = none if quick_search == false: ainfo = find_address(dbg.to_int(where)) if (ainfo != none) and (ainfo.parent_run != none) and (ainfo.parent_region != none): print( "[shadow] found %s at %s (run 0x%08x, region 0x%08x, region size %04d)" % ( search_for, where, ainfo.parent_run.start, ainfo.parent_region.addr, ainfo.parent_run.region_size, ) ) else: print("[shadow] found %s at %s (run 0x%08x)" % (search_for, where, start_addr)) else: ainfo = none if quick_search == false: ainfo = find_address(dbg.to_int(where)) if (ainfo != none) and (ainfo.parent_run != none) and (ainfo.parent_region != none): print( "[shadow] found %s at %s (run 0x%08x, region 0x%08x, region size %04d)" % ( search_for, where, ainfo.parent_run.start, ainfo.parent_region.addr, ainfo.parent_run.region_size, ) ) else: print("[shadow] found %s at %s (chunk 0x%08x)" % (search_for, where, start_addr))
def search(search_for, region_size = 0, search_current_runs = false, \ quick_search = false, proc = none): '''Search the jemalloc heap for the given hex value''' global jeheap load_jeheap(proc) results = [] if search_current_runs == true: if region_size == 0: print('[shadow] searching all current runs for %s' % (search_for)) else: print('[shadow] searching all current runs of size class %d for %s' \ % (region_size, search_for)) for i in range(0, len(jeheap.arenas)): for j in range(0, len(jeheap.arenas[i].bins)): try: if region_size == 0: results.extend(dbg.search(jeheap.arenas[i].bins[j].run.start, \ jeheap.arenas[i].bins[j].run.end, search_for)) else: if jeheap.arenas[i].bins[ j].run.region_size == region_size: results.extend(dbg.search(jeheap.arenas[i].bins[j].run.start, \ jeheap.arenas[i].bins[j].run.end, search_for)) except: continue else: if region_size == 0: print('[shadow] searching all chunks for %s' % (search_for)) for chunk in jeheap.chunks: try: results.extend(dbg.search(chunk.addr, \ chunk.addr + jeheap.chunk_size, search_for)) except: continue else: print('[shadow] searching all non-current runs of size class %d for %s' \ % (region_size, search_for)) for i in range(0, len(jeheap.runs)): try: if region_size == 0: results.extend(dbg.search(jeheap.runs[i].start, \ jeheap.runs[i].end, search_for)) else: if jeheap.runs[i].region_size == region_size: results.extend(dbg.search(jeheap.runs[i].start, \ jeheap.runs[i].end, search_for)) except: continue # display results if not results: print('[shadow] value %s not found' % (search_for)) return for (where, start_addr) in results: if search_current_runs == true: ainfo = none if quick_search == false: ainfo = find_address(dbg.to_int(where)) if (ainfo != none) and (ainfo.parent_run != none) and (ainfo.parent_region != none): print('[shadow] found %s at %s (run 0x%08x, region 0x%08x, region size %04d)' \ % (search_for, where, ainfo.parent_run.start, ainfo.parent_region.addr, \ ainfo.parent_run.region_size)) else: print('[shadow] found %s at %s (run 0x%08x)' % \ (search_for, where, start_addr)) else: ainfo = none if quick_search == false: ainfo = find_address(dbg.to_int(where)) if (ainfo != none) and (ainfo.parent_run != none) and (ainfo.parent_region != none): print('[shadow] found %s at %s (run 0x%08x, region 0x%08x, region size %04d)' \ % (search_for, where, ainfo.parent_run.start, ainfo.parent_region.addr, \ ainfo.parent_run.region_size)) else: print('[shadow] found %s at %s (chunk 0x%08x)' % \ (search_for, where, start_addr))
def parse_arenas(): global jeheap jeheap.arenas[:] = [] for i in range(0, jeheap.narenas): current_arena = jemalloc.arena(0, i, []) try: current_arena.addr = dbg.to_int(dbg.eval_expr(dbg.arena_expr % (i))) except: print("[shadow] error: cannot evaluate arenas[%d]") % (i) sys.exit() for j in range(0, jeheap.nbins): nrg = 0 run_sz = 0 reg_size = 0 reg_offset = 0 end_addr = 0 try: expr = dbg.arena_reg_size_expr % (i, j) reg_size = dbg.to_int(dbg.eval_expr(expr)) expr = dbg.arena_reg0_offset_expr % (i, j) reg_offset = dbg.to_int(dbg.eval_expr(expr)) except: # XXX: for now assume it's a standalone variant; we # need to do some error checking here too. jeheap.STANDALONE = true expr = dbg.arena_bin_info_reg_size_expr % (j) reg_size = dbg.to_int(dbg.eval_expr(expr)) expr = dbg.arena_bin_info_nregs_expr % (j) nrg = dbg.to_int(dbg.eval_expr(expr)) expr = dbg.arena_bin_info_run_size_expr % (j) run_sz = dbg.to_int(dbg.eval_expr(expr)) try: expr = dbg.arena_runcur_expr % (i, j) runcur_addr = runcur = dbg.to_int(dbg.eval_expr(expr)) end_addr = runcur_addr + run_sz if runcur != 0: current_run = jemalloc.arena_run(runcur, end_addr, run_sz, 0, int(reg_size), reg_offset, nrg, 0, []) current_bin = jemalloc.arena_bin(0, j, current_run) current_bin.addr = dbg.to_int(dbg.eval_expr(dbg.arena_bin_addr_expr % (i, j))) current_arena.bins.append(current_bin) else: # no regions for this size class yet, therefore no runcur current_run = jemalloc.arena_run() current_bin = jemalloc.arena_bin(0, j, current_run) current_arena.bins.append(current_bin) except: current_run = jemalloc.arena_run() current_bin = jemalloc.arena_bin(0, j, current_run) current_arena.bins.append(current_bin) continue # add arena to the list of arenas jeheap.arenas.append(current_arena)
def parse_all_runs(proc=none): global jeheap global dbg_engine # number of pages a chunk occupies chunk_npages = jeheap.chunk_size >> 12 # offset of bits in arena_chunk_map_t in double words if dbg_engine == "pykd": # this really speeds up parsing bitmap_offset = dbg.offsetof("mozglue!arena_chunk_map_t", "bits") / jeheap.DWORD_SIZE else: bitmap_offset = dbg.offsetof("arena_chunk_map_t", "bits") / jeheap.DWORD_SIZE # number of double words occupied by an arena_chunk_map_t chunk_map_dwords = (bitmap_offset / jeheap.DWORD_SIZE) + 1 if jeheap.DWORD_SIZE == 8: if dbg_engine == "gdb": dword_fmt = "g" else: # lldb dw_fmt = "XXX" else: if dbg_engine == "gdb": dword_fmt = "w" else: # lldb dw_fmt = "XXX" # the 12 least significant bits of each bitmap entry hold # various flags for the corresponding run flags_mask = (1 << 12) - 1 # delete the heap's runs' array jeheap.runs[:] = [] for chunk in jeheap.chunks: if not chunk.arena_addr: continue try: if dbg_engine == "gdb": # parse the whole map at once to avoid gdb's delays expr = dbg.chunk_map_expr % (chunk_npages * chunk_map_dwords, dword_fmt, chunk.addr) elif dbg_engine == "pykd": chunk_map_len = (chunk_npages * chunk_map_dwords) / 4 chunk_map_addr = dbg.to_int(dbg.eval_expr(dbg.chunk_map_expr % (chunk.addr))) expr = dbg.chunk_map_dump_expr % (chunk_map_addr, chunk_map_len) else: # lldb expr = "" except: print("[shadow] error: cannot read bitmap from chunk 0x%08x" % (chunk.addr)) sys.exit() lines = (dbg.execute(expr)).split("\n") dwords = [] i = 0 for line in lines: dwords += [ int(dw, 16) for dw in line[line.find(dbg.address_separator) + len(dbg.address_separator) :].split() ] bitmap = [dwords[i] for i in range(int(bitmap_offset), int(len(dwords)), int(bitmap_offset + 1))] # traverse the bitmap for mapelm in bitmap: flags = mapelm & flags_mask # flags == 1 means the chunk is small and the rest of the bits # hold the actual run address if flags == 1: addr = mapelm & ~flags_mask size = dbg.get_page_size() # flags = 3 indicates a large chunk; calculate the run's address # directly from the map element index and extract the run's size elif flags == 3: addr = chunk.addr + i * dbg.get_page_size() size = mapelm & ~flags_mask # run is not allocated? skip it else: continue if addr not in [r.start for r in jeheap.runs]: new_run = parse_run(addr, proc) if new_run == none: pass else: jeheap.runs.append(copy.deepcopy(new_run))
def parse_run(run_addr, proc=none): """Given a run's address return a jemalloc.arena_run object""" global jeheap new_run = jemalloc.arena_run() new_run.start = run_addr try: new_run.bin_addr = dbg.read_memory(new_run.start, jeheap.DWORD_SIZE, proc) if jeheap.STANDALONE == false: new_run.size = dbg.read_memory(new_run.bin_addr + (6 * jeheap.DWORD_SIZE), jeheap.DWORD_SIZE, proc) new_run.end = new_run.start + new_run.size new_run.region_size = dbg.read_memory(new_run.bin_addr + (5 * jeheap.DWORD_SIZE), jeheap.DWORD_SIZE, proc) new_run.total_regions = dbg.read_memory(new_run.bin_addr + (7 * jeheap.DWORD_SIZE), jemalloc.INT_SIZE, proc) if new_run.total_regions > 10000 or new_run.total_regions <= 0: return none except: # print('[shadow] error parsing the metadata of run 0x%08x' % (run_addr)) return none # XXX: this isn't correct on jemalloc standalone *debug* variant try: new_run.free_regions = dbg.read_memory( new_run.start + jeheap.DWORD_SIZE + jemalloc.INT_SIZE, jemalloc.INT_SIZE, proc ) except: # print('[shadow] error parsing the free regions of run 0x%08x' % (run_addr)) new_run.free_regions = 0 if new_run.free_regions < 0: new_run.free_regions = 0 # delete the run's regions new_run.regions[:] = [] # parse the run's regions new_run.reg0_offset = dbg.read_memory(new_run.bin_addr + (9 * jeheap.DWORD_SIZE), jeheap.DWORD_SIZE, proc) if new_run.reg0_offset > 10000 or new_run.reg0_offset <= 0: return none first_region_addr = reg0_addr = run_addr + new_run.reg0_offset regs_mask_bits = (new_run.total_regions / 8) + 1 regs_mask_addr = 0 regs_mask_str = "" if dbg_engine == "gdb": regs_mask_addr = dbg.to_int(dbg.execute(dbg.regs_mask_addr_expr % (run_addr))) regs_mask_str = dbg.execute(dbg.regs_mask_addr_bits_expr % (regs_mask_bits, regs_mask_addr)) elif dbg_engine == "pykd": regs_mask_addr = dbg.to_int(dbg.eval_expr(dbg.regs_mask_addr_expr % (run_addr))) regs_mask_str = dbg.execute(dbg.regs_mask_addr_bits_expr % (regs_mask_addr, regs_mask_bits)) else: # lldb regs_mask_str = "" regs_mask = "" if dbg_engine == "gdb": for line in regs_mask_str.splitlines(): line = line[line.find(dbg.address_separator) + len(dbg.address_separator) : line.find("\n")] line = line.replace("\n", "") line = line.replace("\t", "") line = line.replace(" ", "") regs_mask += line elif dbg_engine == "pykd": lines = regs_mask_str.splitlines() lines = lines[2:] for line in lines: line = line[ line.find(dbg.address_separator) + len(dbg.address_separator) : line.rfind(dbg.address_separator) ] line = line.replace("\n", "") line = line.replace("\t", "") line = line.replace(" ", "") regs_mask += line else: # lldb regs_mask = "" new_run.regs_mask = regs_mask first_region = jemalloc.region(0, first_region_addr, int(new_run.regs_mask[0])) try: first_region.content_preview = hex(dbg.read_memory(first_region.addr, jemalloc.INT_SIZE, proc)).rstrip("L") except: print("[shadow] error reading the first dword of region 0x%08x" % (first_region.addr)) first_region.content_preview = "" new_run.regions.append(first_region) for i in range(1, new_run.total_regions): try: current_region = jemalloc.region(i, 0, int(new_run.regs_mask[i])) except: current_region = jemalloc.region(i, 0, 0) current_region.addr = reg0_addr + (i * new_run.region_size) try: current_region.content_preview = hex(dbg.read_memory(current_region.addr, jemalloc.INT_SIZE, proc)).rstrip( "L" ) except: current_region.content_preview = "" new_run.regions.append(current_region) return new_run