Example #1
0
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
Example #2
0
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
Example #3
0
    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)
Example #4
0
    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)
Example #5
0
    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)
Example #6
0
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
Example #7
0
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
Example #8
0
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
Example #9
0
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))
Example #10
0
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))
Example #11
0
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))
Example #12
0
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
Example #13
0
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)
Example #14
0
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))
Example #15
0
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))
Example #16
0
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)
Example #17
0
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))
Example #18
0
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