Exemple #1
0
def malloc_chunk(addr,fake=False):
    """
    Prints out the malloc_chunk at the specified address.
    """
    main_heap = pwndbg.heap.current

    if not isinstance(addr, six.integer_types):
        addr = int(addr)

    chunk = read_chunk(addr)
    size = int(chunk['size'])
    actual_size = size & ~7
    prev_inuse, is_mmapped, non_main_arena = main_heap.chunk_flags(size)
    arena = None
    if not fake and non_main_arena:
        arena = main_heap.get_heap(addr)['ar_ptr']

    fastbins = [] if fake else main_heap.fastbins(arena)
    header = M.get(addr)
    if fake:
        header += message.prompt(' FAKE')
    if prev_inuse:
        if actual_size in fastbins:
            header += message.hint(' FASTBIN')
        else:
            header += message.hint(' PREV_INUSE')
    if is_mmapped:
        header += message.hint(' IS_MMAPED')
    if non_main_arena:
        header += message.hint(' NON_MAIN_ARENA')
    print(header, chunk["value"])

    return chunk
Exemple #2
0
def configfile_print_scope(scope, show_all=False):
    params = pwndbg.config.get_params(scope)

    if not show_all:
        params = list(filter(lambda p: p.is_changed, params))

    if params:
        if not show_all:
            print(hint('Showing only changed values:'))
        for p in params:
            print('# %s: %s' % (p.optname, p.docstring))
            print('# default: %s' % p.native_default)
            print('set %s %s' % (p.optname, p.native_value))
            print()
    else:
        print(hint('No changed values. To see current values use `%s`.' % scope))
Exemple #3
0
def config(filter_pattern):
    values = get_config_parameters('config', filter_pattern)

    if not values:
        print(hint('No config parameter found with filter "{}"'.format(filter_pattern)))
        return

    longest_optname = max(map(len, [v.optname for v in values]))
    longest_value = max(map(len, [extend_value_with_default(repr(v.value), repr(v.default)) for v in values]))

    header = print_row('Name', 'Value', 'Def', 'Documentation', longest_optname, longest_value)
    print('-' * (len(header)))

    for v in sorted(values):
        print_row(v.optname, repr(v.value), repr(v.default), v.docstring, longest_optname, longest_value)

    print(hint('You can set config variable with `set <config-var> <value>`'))
    print(hint('You can generate configuration file using `configfile` '
               '- then put it in your .gdbinit after initializing pwndbg'))
Exemple #4
0
    def __str__(self):
        res = []
        prefix = '[%%%ds]    ' % (pwndbg.arch.ptrsize * 2)
        prefix_len = len(prefix % (''))
        arena_name = hex(self.addr) if self.addr != pwndbg.heap.current.main_arena.address else 'main'
        res.append(message.hint(prefix % (arena_name)) + str(self.heaps[0]))
        for h in self.heaps[1:]:
            res.append(' ' * prefix_len + str(h))

        return '\n'.join(res)
Exemple #5
0
def probeleak(address=None, count=0x40, max_distance=0x0):

    address = int(address)
    address &= pwndbg.arch.ptrmask
    ptrsize = pwndbg.arch.ptrsize
    count   = max(int(count), ptrsize)
    off_zeros = int(math.ceil(math.log(count,2)/4))

    if count > address > 0x10000: # in case someone puts in an end address and not a count (smh)
        print(message.warn("Warning: you gave an end address, not a count. Substracting 0x%x from the count." % (address)))
        count -= address

    try:
        data = pwndbg.memory.read(address, count, partial=True)
    except gdb.error as e:
        print(message.error(str(e)))
        return

    if not data:
        print(message.error("Couldn't read memory at 0x%x. See 'probeleak -h' for the usage." % (address,)))
        return

    found = False
    for i in range(0, len(data) - ptrsize + 1):
        p = pwndbg.arch.unpack(data[i:i+ptrsize])
        page = find_module(p, max_distance)
        if page:
            if not found:
                print(M.legend())
                found = True

            mod_name = page.objfile
            if not mod_name:
                mod_name = '[anon]'

            if p >= page.end:
                right_text = '(%s) %s + 0x%x + 0x%x (outside of the page)' % (page.permstr, mod_name, page.memsz, p - page.end)
            elif p < page.start:
                right_text = '(%s) %s - 0x%x (outside of the page)' % (page.permstr, mod_name, page.start - p)
            else:
                right_text = '(%s) %s + 0x%x' % (page.permstr, mod_name, p - page.start)

            offset_text = '0x%0*x' % (off_zeros, i)
            p_text = '0x%0*x' % (int(ptrsize*2), p)
            text = '%s: %s = %s' % (offset_text, M.get(p, text=p_text), M.get(p, text=right_text))

            symbol = pwndbg.symbol.get(p)
            if symbol:
                text += ' (%s)' % symbol

            print(text)

    if not found:
        print(message.hint('No leaks found at 0x%x-0x%x :(' % (address, address+count)))
Exemple #6
0
    def __str__(self):
        res = []
        prefix = '[%%%ds]    ' % (pwndbg.arch.ptrsize * 2)
        prefix_len = len(prefix % (''))
        arena_name = hex(
            self.addr
        ) if self.addr != pwndbg.heap.current.main_arena.address else 'main'
        res.append(message.hint(prefix % (arena_name)) + str(self.heaps[0]))
        for h in self.heaps[1:]:
            res.append(' ' * prefix_len + str(h))

        return '\n'.join(res)
Exemple #7
0
def inform_report_issue(exception_msg):
    """
    Informs user that he can report an issue.
    The use of `memoize` makes it reporting only once for a given exception message.
    """
    print(
        message.notice(
            "If that is an issue, you can report it on https://github.com/pwndbg/pwndbg/issues\n"
            "(Please don't forget to search if it hasn't been reported before)\n"
            "To generate the report and open a browser, you may run ") +
        message.hint("`bugreport --run-browser`") +
        message.notice("\nPS: Pull requests are welcome"))
Exemple #8
0
def inform_report_issue(exception_msg):
    """
    Informs user that he can report an issue.
    The use of `memoize` makes it reporting only once for a given exception message.
    """
    print(message.notice(
        "If that is an issue, you can report it on https://github.com/pwndbg/pwndbg/issues\n"
        "(Please don't forget to search if it hasn't been reported before)\n"
        "To generate the report and open a browser, you may run ") +
        message.hint("`bugreport --run-browser`") +
        message.notice("\nPS: Pull requests are welcome")
    )
Exemple #9
0
def malloc_chunk(addr,fake=False):
    """
    Prints out the malloc_chunk at the specified address.
    """
    main_heap = pwndbg.heap.current

    if not isinstance(addr, six.integer_types):
        addr = int(addr)

    chunk = read_chunk(addr)
    size = int(chunk['size'])
    actual_size = size & ~7
    prev_inuse, is_mmapped, non_main_arena = main_heap.chunk_flags(size)
    arena = None
    if not fake and non_main_arena:
        arena = main_heap.get_heap(addr)['ar_ptr']

    fastbins = [] if fake else main_heap.fastbins(arena)
    header = M.get(addr)
    if fake:
        header += message.prompt(' FAKE')
    if prev_inuse:
        if actual_size in fastbins:
            header += message.hint(' FASTBIN')
        else:
            header += message.hint(' PREV_INUSE')
    if is_mmapped:
        header += message.hint(' IS_MMAPED')
    if non_main_arena:
        header += message.hint(' NON_MAIN_ARENA')

    chunk_str='{\n'
    for key in chunk["value"].type.keys():
        chunk_str+='  %s = %s,\n'%(str(key),hex(int(chunk["value"][key])))
    chunk_str+='}'

    print(header, chunk_str)
    #print(header, chunk["value"])

    return chunk
Exemple #10
0
def format_bin(bins, verbose=False):
    main_heap = pwndbg.heap.current
    fd_offset = main_heap.chunk_key_offset('fd')

    result = []
    for size in bins:
        chain = bins[size]

        if not verbose and chain == [0]:
            continue

        formatted_chain = pwndbg.chain.format(chain, offset=fd_offset)

        if isinstance(size, int):
            size = hex(size)

        result.append((message.hint(size) + ': ').ljust(13) + formatted_chain)

    if not result:
        result.append(message.hint('empty'))

    return result
Exemple #11
0
def format_bin(bins, verbose=False, offset=None):
    main_heap = pwndbg.heap.current
    if offset is None:
        offset = main_heap.chunk_key_offset('fd')

    result = []
    bins_type = bins.pop('type')

    for size in bins:
        b = bins[size]
        count, is_chain_corrupted = None, False

        # fastbins consists of only single linked list
        if bins_type == 'fastbins':
            chain_fd = b
        # tcachebins consists of single linked list and entries count
        elif bins_type == 'tcachebins':
            chain_fd, count = b
        # normal bins consists of double linked list and may be corrupted (we can detect corruption)
        else:  # normal bin
            chain_fd, chain_bk, is_chain_corrupted = b

        if not verbose and (chain_fd == [0] and not count) and not is_chain_corrupted:
            continue
        
        if bins_type == 'tcachebins':
            limit = 8
            if count <= 7:
                limit = count + 1
            formatted_chain = pwndbg.chain.format(chain_fd[0], offset=offset, limit=limit)
        else:
            formatted_chain = pwndbg.chain.format(chain_fd[0], offset=offset)


        if isinstance(size, int):
            size = hex(size)

        if is_chain_corrupted:
            line = message.hint(size) + message.error(' [corrupted]') + '\n'
            line += message.hint('FD: ') + formatted_chain + '\n'
            line += message.hint('BK: ') + pwndbg.chain.format(chain_bk[0], offset=main_heap.chunk_key_offset('bk'))
        else:
            if count is not None:
                line = (message.hint(size) + message.hint(' [%3d]' % count) + ': ').ljust(13)
            else:
                line = (message.hint(size) + ': ').ljust(13)
            line += formatted_chain

        result.append(line)

    if not result:
        result.append(message.hint('empty'))

    return result
Exemple #12
0
def handle(name='Error'):
    """Displays an exception to the user, optionally displaying a full traceback
    and spawning an interactive post-moretem debugger.

    Notes:
        - ``set exception-verbose on`` enables stack traces.
        - ``set exception-debugger on`` enables the post-mortem debugger.
    """

    # This is for unit tests so they fail on exceptions instead of displaying them.
    if getattr(sys, '_pwndbg_unittest_run', False) is True:
        E, V, T = sys.exc_info()
        e = E(V)
        e.__traceback__ = T
        raise e

    # Display the error
    if debug or verbose:
        exception_msg = traceback.format_exc()
        print(exception_msg)
        inform_report_issue(exception_msg)

    else:
        exc_type, exc_value, exc_traceback = sys.exc_info()

        print(
            message.error('Exception occured: {}: {} ({})'.format(
                name, exc_value, exc_type)))

        print(
            message.notice('For more info invoke `') +
            message.hint('set exception-verbose on') + message.notice(
                '` and rerun the command\nor debug it by yourself with `') +
            message.hint('set exception-debugger on') + message.notice('`'))

    # Break into the interactive debugger
    if debug:
        with pwndbg.stdio.stdio:
            pdb.post_mortem()
Exemple #13
0
def handle(name='Error'):
    """Displays an exception to the user, optionally displaying a full traceback
    and spawning an interactive post-moretem debugger.

    Notes:
        - ``set exception-verbose on`` enables stack traces.
        - ``set exception-debugger on`` enables the post-mortem debugger.
    """

    # This is for unit tests so they fail on exceptions instead of displaying them.
    if getattr(sys, '_pwndbg_unittest_run', False) is True:
        E, V, T = sys.exc_info()
        e = E(V)
        e.__traceback__ = T
        raise e

    # Display the error
    if debug or verbose:
        exception_msg = traceback.format_exc()
        print(exception_msg)
        inform_report_issue(exception_msg)

    else:
        exc_type, exc_value, exc_traceback = sys.exc_info()

        print(message.error('Exception occured: {}: {} ({})'.format(name, exc_value, exc_type)))

        print(message.notice('For more info invoke `') +
              message.hint('set exception-verbose on') +
              message.notice('` and rerun the command\nor debug it by yourself with `') +
              message.hint('set exception-debugger on') +
              message.notice('`'))

    # Break into the interactive debugger
    if debug:
        with pwndbg.stdio.stdio:
            pdb.post_mortem()
Exemple #14
0
def heap(addr=None):
    """
    Prints out all chunks in the main_arena, or the arena specified by `addr`.
    """

    main_heap   = pwndbg.heap.current
    main_arena  = main_heap.get_arena(addr)

    if main_arena is None:
        return

    heap_region = main_heap.get_region(addr)

    if heap_region is None:
        print(message.error('Could not find the heap'))
        return

    top = main_arena['top']
    last_remainder = main_arena['last_remainder']

    print(message.hint('Top Chunk: ') + M.get(top))
    print(message.hint('Last Remainder: ') + M.get(last_remainder))
    print()

    # Print out all chunks on the heap
    # TODO: Add an option to print out only free or allocated chunks
    addr = heap_region.vaddr
    while addr <= top:
        chunk = malloc_chunk(addr)
        size = int(chunk['size'])

        # Clear the bottom 3 bits
        size &= ~7
        if size == 0:
            break
        addr += size
Exemple #15
0
def theme(filter_pattern):
    values = get_config_parameters('theme', filter_pattern)

    if not values:
        print(
            hint('No theme parameter found with filter "{}"'.format(
                filter_pattern)))
        return

    longest_optname = max(map(len, [v.optname for v in values]))
    longest_value = max(
        map(len, [
            extend_value_with_default(str(v.value), str(v.default))
            for v in values
        ]))

    header = print_row('Name', 'Value', 'Def', 'Documentation',
                       longest_optname, longest_value)
    print('-' * (len(header)))
    for v in sorted(values):
        if isinstance(v, pwndbg.color.theme.ColoredParameter):
            value = generateColorFunction(v.value)(v.value)
            default = generateColorFunction(v.default)(v.default)
        elif isinstance(v.value, str):
            value = "'%s'" % str(v.value)
            default = str(v.default)
        else:
            value = repr(v.value)
            default = repr(v.default)
        print_row(v.optname, value, default, v.docstring, longest_optname,
                  longest_value)

    print(hint('You can set theme variable with `set <theme-var> <value>`'))
    print(
        hint('You can generate theme config file using `themefile` '
             '- then put it in your .gdbinit after initializing pwndbg'))
Exemple #16
0
def format_bin(bins, verbose=False, offset=None):
    main_heap = pwndbg.heap.current
    if offset is None:
        offset = main_heap.chunk_key_offset('fd')

    result = []
    bins_type = bins.pop('type')

    for size in bins:
        b = bins[size]
        count, is_chain_corrupted = None, False

        # fastbins consists of only single linked list
        if bins_type == 'fastbins':
            chain_fd = b
        # tcachebins consists of single linked list and entries count
        elif bins_type == 'tcachebins':
            chain_fd, count = b
        # normal bins consists of double linked list and may be corrupted (we can detect corruption)
        else:  # normal bin
            chain_fd, chain_bk, is_chain_corrupted = b

        if not verbose and (chain_fd == [0] and not count) and not is_chain_corrupted:
            continue

        formatted_chain = pwndbg.chain.format(chain_fd[0], offset=offset)

        if isinstance(size, int):
            size = hex(size)

        if is_chain_corrupted:
            line = message.hint(size) + message.error(' [corrupted]') + '\n'
            line += message.hint('FD: ') + formatted_chain + '\n'
            line += message.hint('BK: ') + pwndbg.chain.format(chain_bk[0], offset=main_heap.chunk_key_offset('bk'))
        else:
            if count is not None:
                line = (message.hint(size) + message.hint(' [%3d]' % count) + ': ').ljust(13)
            else:
                line = (message.hint(size) + ': ').ljust(13)
            line += formatted_chain

        result.append(line)

    if not result:
        result.append(message.hint('empty'))

    return result
Exemple #17
0
def probeleak(address=None, count=0x40):

    address = int(address)
    address &= pwndbg.arch.ptrmask
    count   = max(int(count), 0)
    ptrsize = pwndbg.arch.ptrsize
    off_zeros = int(math.ceil(math.log(count,2)/4))

    if count > address > 0x10000: # in case someone puts in an end address and not a count (smh)
        count -= address

    if count % ptrsize > 0:
        newcount = count - (count % ptrsize)
        print(message.warning("Warning: count 0x%x is not a multiple of 0x%x; truncating to 0x%x." % (count, ptrsize, newcount)))
        count = newcount

    try:
        data = pwndbg.memory.read(address, count, partial=True)
    except gdb.error as e:
        print(message.error(str(e)))
        return

    if not data:
        print(message.error("Couldn't read memory at 0x%x" % (address,)))
        return

    found = False
    for i in range(0, count, ptrsize):
        p = pwndbg.arch.unpack(data[i:i+ptrsize])
        page = find_module(p)
        if page:
            if not found:
                print(M.legend())
                found = True

            mod_name = page.objfile
            if not mod_name:
                mod_name = '[anon]'
            fmt = '+0x{offset:0{n1}x}: 0x{ptr:0{n2}x} = {page}'
            right_text = ('(%s) %s + 0x%x') % (page.permstr, mod_name, p - page.vaddr + page.offset)
            print(fmt.format(n1=off_zeros, n2=ptrsize*2, offset=i, ptr=p, page=M.get(p, text=right_text)))
    if not found:
        print(message.hint('No leaks found at 0x{:x}-0x{:x} :('.format(address, address+count)))
Exemple #18
0
def arenas():
    """
    Prints out allocated arenas
    """

    heap  = pwndbg.heap.current
    addr  = None
    arena = heap.get_arena(addr)
    main_arena_addr = int(arena.address)
    fmt = '[%%%ds]' % (pwndbg.arch.ptrsize *2)
    while addr != main_arena_addr:

        h = heap.get_region(addr)
        if not h:
            print(message.error('Could not find the heap'))
            return

        hdr = message.hint(fmt % (hex(addr) if addr else 'main'))
        print(hdr, M.heap(str(h)))
        addr = int(arena['next'])
        arena = heap.get_arena(addr)
Exemple #19
0
def got(name_filter=""):

    relro_status = pwndbg.wrappers.checksec.relro_status()
    pie_status = pwndbg.wrappers.checksec.pie_status()
    jmpslots = list(pwndbg.wrappers.readelf.get_jmpslots())
    if not len(jmpslots):
        print(message.error("NO JUMP_SLOT entries available in the GOT"))
        return

    if "PIE enabled" in pie_status:
        bin_base = pwndbg.elf.exe().address

    relro_color = message.off
    if "Partial" in relro_status:
        relro_color = message.warn
    elif "Full" in relro_status:
        relro_color = message.on
    print(
        "\nGOT protection: %s | GOT functions: %d\n " % (relro_color(relro_status), len(jmpslots))
    )

    for line in jmpslots:
        address, info, rtype, value, name = line.split()[:5]

        if name_filter not in name:
            continue

        address_val = int(address, 16)

        if (
            "PIE enabled" in pie_status
        ):  # if PIE, address is only the offset from the binary base address
            address_val = bin_base + address_val

        got_address = pwndbg.memory.pvoid(address_val)
        print(
            "[0x%x] %s -> %s" % (address_val, message.hint(name), pwndbg.chain.format(got_address))
        )
Exemple #20
0
def format_bin(bins, verbose=False, offset=None):
    allocator = pwndbg.heap.current
    if offset is None:
        offset = allocator.chunk_key_offset("fd")

    result = []
    bins_type = bins.pop("type")

    for size in bins:
        b = bins[size]
        count, is_chain_corrupted = None, False
        safe_lnk = False

        # fastbins consists of only single linked list
        if bins_type == "fastbins":
            chain_fd = b
            safe_lnk = pwndbg.glibc.check_safe_linking()
        # tcachebins consists of single linked list and entries count
        elif bins_type == "tcachebins":
            chain_fd, count = b
            safe_lnk = pwndbg.glibc.check_safe_linking()
        # normal bins consists of double linked list and may be corrupted (we can detect corruption)
        else:  # normal bin
            chain_fd, chain_bk, is_chain_corrupted = b

        if not verbose and (chain_fd == [0]
                            and not count) and not is_chain_corrupted:
            continue

        if bins_type == "tcachebins":
            limit = 8
            if count <= 7:
                limit = count + 1
            formatted_chain = pwndbg.chain.format(chain_fd[0],
                                                  offset=offset,
                                                  limit=limit,
                                                  safe_linking=safe_lnk)
        else:
            formatted_chain = pwndbg.chain.format(chain_fd[0],
                                                  offset=offset,
                                                  safe_linking=safe_lnk)

        if isinstance(size, int):
            size = hex(size)

        if is_chain_corrupted:
            line = message.hint(size) + message.error(" [corrupted]") + "\n"
            line += message.hint("FD: ") + formatted_chain + "\n"
            line += message.hint("BK: ") + pwndbg.chain.format(
                chain_bk[0], offset=allocator.chunk_key_offset("bk"))
        else:
            if count is not None:
                line = (message.hint(size) + message.hint(" [%3d]" % count) +
                        ": ").ljust(13)
            else:
                line = (message.hint(size) + ": ").ljust(13)
            line += formatted_chain

        result.append(line)

    if not result:
        result.append(message.hint("empty"))

    return result
Exemple #21
0
 def __str__(self):
     fmt = '[%%%ds]' % (pwndbg.arch.ptrsize * 2)
     return message.hint(fmt % (hex(self.first_chunk))) + M.heap(str(pwndbg.vmmap.find(self.addr)))
Exemple #22
0
def malloc_chunk(addr, fake=False, verbose=False, simple=False):
    """Print a malloc_chunk struct's contents."""
    # points to the real start of the chunk
    cursor = int(addr)

    allocator = pwndbg.heap.current
    ptr_size = allocator.size_sz

    size_field = pwndbg.memory.u(cursor + allocator.chunk_key_offset('size'))
    real_size = size_field & ~allocator.malloc_align_mask

    headers_to_print = []  # both state (free/allocated) and flags
    fields_to_print = set()  # in addition to addr and size
    out_fields = "Addr: {}\n".format(M.get(cursor))

    if fake:
        headers_to_print.append(message.on("Fake chunk"))
        verbose = True  # print all fields for fake chunks

    if simple:
        chunk = read_chunk(cursor)

        if not headers_to_print:
            headers_to_print.append(message.hint(M.get(cursor)))

        prev_inuse, is_mmapped, non_main_arena = allocator.chunk_flags(
            int(chunk['size']))
        if prev_inuse:
            headers_to_print.append(message.hint('PREV_INUSE'))
        if is_mmapped:
            headers_to_print.append(message.hint('IS_MMAPED'))
        if non_main_arena:
            headers_to_print.append(message.hint('NON_MAIN_ARENA'))

        print(' | '.join(headers_to_print))
        for key, val in chunk.items():
            print(message.system(key) + ": 0x{:02x}".format(int(val)))
        print('')
        return

    arena = allocator.get_arena_for_chunk(cursor)
    arena_address = None
    is_top = False
    if not fake and arena:
        arena_address = arena.address
        top_chunk = arena['top']
        if cursor == top_chunk:
            headers_to_print.append(message.off("Top chunk"))
            is_top = True

    if not is_top:
        fastbins = allocator.fastbins(arena_address) or {}
        smallbins = allocator.smallbins(arena_address) or {}
        largebins = allocator.largebins(arena_address) or {}
        unsortedbin = allocator.unsortedbin(arena_address) or {}
        if allocator.has_tcache():
            tcachebins = allocator.tcachebins(None)

        if real_size in fastbins.keys() and cursor in fastbins[real_size]:
            headers_to_print.append(message.on("Free chunk (fastbins)"))
            if not verbose:
                fields_to_print.add('fd')

        elif real_size in smallbins.keys() and cursor in bin_addrs(
                smallbins[real_size], "smallbins"):
            headers_to_print.append(message.on("Free chunk (smallbins)"))
            if not verbose:
                fields_to_print.update(['fd', 'bk'])

        elif real_size >= list(
                largebins.items())[0][0] and cursor in bin_addrs(
                    largebins[(list(
                        largebins.items())[allocator.largebin_index(real_size)
                                           - 64][0])], "largebins"):
            headers_to_print.append(message.on("Free chunk (largebins)"))
            if not verbose:
                fields_to_print.update(
                    ['fd', 'bk', 'fd_nextsize', 'bk_nextsize'])

        elif cursor in bin_addrs(unsortedbin['all'], "unsortedbin"):
            headers_to_print.append(message.on("Free chunk (unsortedbin)"))
            if not verbose:
                fields_to_print.update(['fd', 'bk'])

        elif allocator.has_tcache() and real_size in tcachebins.keys(
        ) and cursor + ptr_size * 2 in bin_addrs(tcachebins[real_size],
                                                 "tcachebins"):
            headers_to_print.append(message.on("Free chunk (tcache)"))
            if not verbose:
                fields_to_print.add('fd')

        else:
            headers_to_print.append(message.hint("Allocated chunk"))

    if verbose:
        fields_to_print.update(
            ['prev_size', 'size', 'fd', 'bk', 'fd_nextsize', 'bk_nextsize'])
    else:
        out_fields += "Size: 0x{:02x}\n".format(size_field)

    prev_inuse, is_mmapped, non_main_arena = allocator.chunk_flags(size_field)
    if prev_inuse:
        headers_to_print.append(message.hint('PREV_INUSE'))
    if is_mmapped:
        headers_to_print.append(message.hint('IS_MMAPED'))
    if non_main_arena:
        headers_to_print.append(message.hint('NON_MAIN_ARENA'))

    fields_ordered = [
        'prev_size', 'size', 'fd', 'bk', 'fd_nextsize', 'bk_nextsize'
    ]
    for field_to_print in fields_ordered:
        if field_to_print in fields_to_print:
            out_fields += message.system(
                field_to_print) + ": 0x{:02x}\n".format(
                    pwndbg.memory.u(
                        cursor + allocator.chunk_key_offset(field_to_print)))

    print(' | '.join(headers_to_print) + "\n" + out_fields)
def init_ida_rpc_client():
    global _ida, _ida_last_exception, _ida_last_connection_check

    if not ida_enabled:
        return

    now = time.time()
    if _ida is None and (now -
                         _ida_last_connection_check) < int(ida_timeout) + 5:
        return

    addr = 'http://{host}:{port}'.format(host=ida_rpc_host, port=ida_rpc_port)

    _ida = xmlrpclib.ServerProxy(addr)
    socket.setdefaulttimeout(int(ida_timeout))

    exception = None  # (type, value, traceback)
    try:
        _ida.here()
        print(
            message.success(
                "Pwndbg successfully connected to Ida Pro xmlrpc: %s" % addr))
    except socket.error as e:
        if e.errno != errno.ECONNREFUSED:
            exception = sys.exc_info()
        _ida = None
    except socket.timeout:
        exception = sys.exc_info()
        _ida = None
    except xmlrpclib.ProtocolError:
        exception = sys.exc_info()
        _ida = None

    if exception:
        if not isinstance(
                _ida_last_exception,
                exception[0]) or _ida_last_exception.args != exception[1].args:
            if hasattr(
                    pwndbg.config,
                    "exception_verbose") and pwndbg.config.exception_verbose:
                print(message.error("[!] Ida Pro xmlrpc error"))
                traceback.print_exception(*exception)
            else:
                exc_type, exc_value, _ = exception
                print(
                    message.error(
                        'Failed to connect to IDA Pro ({}: {})'.format(
                            exc_type.__qualname__, exc_value)))
                if exc_type is socket.timeout:
                    print(
                        message.notice(
                            'To increase the time to wait for IDA Pro use `') +
                        message.hint(
                            'set ida-timeout <new-timeout-in-seconds>') +
                        message.notice('`'))
                else:
                    print(
                        message.notice('For more info invoke `') +
                        message.hint('set exception-verbose on') +
                        message.notice('`'))
                print(
                    message.notice('To disable IDA Pro integration invoke `') +
                    message.hint('set ida-enabled off') + message.notice('`'))

    _ida_last_exception = exception and exception[1]
    _ida_last_connection_check = now
Exemple #24
0
 def __str__(self):
     fmt = '[%%%ds]' % (pwndbg.arch.ptrsize * 2)
     return message.hint(fmt % (hex(self.first_chunk))) + M.heap(str(pwndbg.vmmap.find(self.addr)))
Exemple #25
0
def probeleak(address=None,
              count=0x40,
              max_distance=0x0,
              point_to=None,
              max_ptrs=0,
              flags=None):

    address = int(address)
    address &= pwndbg.arch.ptrmask
    ptrsize = pwndbg.arch.ptrsize
    count = max(int(count), ptrsize)
    off_zeros = int(math.ceil(math.log(count, 2) / 4))
    if flags != None:
        require_flags = flags_str2int(flags)

    if count > address > 0x10000:  # in case someone puts in an end address and not a count (smh)
        print(
            message.warn(
                "Warning: you gave an end address, not a count. Substracting 0x%x from the count."
                % (address)))
        count -= address

    try:
        data = pwndbg.memory.read(address, count, partial=True)
    except gdb.error as e:
        print(message.error(str(e)))
        return

    if not data:
        print(
            message.error(
                "Couldn't read memory at 0x%x. See 'probeleak -h' for the usage."
                % (address, )))
        return

    found = False
    find_cnt = 0
    for i in range(0, len(data) - ptrsize + 1):
        p = pwndbg.arch.unpack(data[i:i + ptrsize])
        page = find_module(p, max_distance)
        if page:
            if point_to != None and point_to not in page.objfile:
                continue
            if flags != None and not satisfied_flags(require_flags,
                                                     page.flags):
                continue
            if not found:
                print(M.legend())
                found = True

            mod_name = page.objfile
            if not mod_name:
                mod_name = '[anon]'

            if p >= page.end:
                right_text = '(%s) %s + 0x%x + 0x%x (outside of the page)' % (
                    page.permstr, mod_name, page.memsz, p - page.end)
            elif p < page.start:
                right_text = '(%s) %s - 0x%x (outside of the page)' % (
                    page.permstr, mod_name, page.start - p)
            else:
                right_text = '(%s) %s + 0x%x' % (page.permstr, mod_name,
                                                 p - page.start)

            offset_text = '0x%0*x' % (off_zeros, i)
            p_text = '0x%0*x' % (int(ptrsize * 2), p)
            text = '%s: %s = %s' % (offset_text, M.get(
                p, text=p_text), M.get(p, text=right_text))

            symbol = pwndbg.symbol.get(p)
            if symbol:
                text += ' (%s)' % symbol
            print(text)

            find_cnt += 1
            if max_ptrs != 0 and find_cnt >= max_ptrs:
                break

    if not found:
        print(
            message.hint('No leaks found at 0x%x-0x%x :(' %
                         (address, address + count)))
Exemple #26
0
    def new_malloc_chunk(addr, fake=False, verbose=False, simple=False):
        idx, start, end, rest = (lambda idx, start, end, **rest:
                                 (idx, start, end, rest))(**scope)
        scope['idx'] += 1

        if start is not None and end is not None and idx not in range(
                start, end + 1):
            return

        ## original begin
        cursor = int(addr)

        allocator = pwndbg.heap.current
        ptr_size = allocator.size_sz

        size_field = pwndbg.memory.u(cursor +
                                     allocator.chunk_key_offset('size'))
        real_size = size_field & ~allocator.malloc_align_mask

        headers_to_print = []  # both state (free/allocated) and flags
        fields_to_print = set()  # in addition to addr and size
        out_fields = "Addr: {}\n".format(M.get(cursor))

        if fake:
            headers_to_print.append(message.on("Fake chunk"))
            verbose = True  # print all fields for fake chunks

        if simple:
            chunk = read_chunk(cursor)

            if not headers_to_print:
                headers_to_print.append(message.hint(M.get(cursor)))

            prev_inuse, is_mmapped, non_main_arena = allocator.chunk_flags(
                int(chunk['size']))
            if prev_inuse:
                headers_to_print.append(message.hint('PREV_INUSE'))
            if is_mmapped:
                headers_to_print.append(message.hint('IS_MMAPED'))
            if non_main_arena:
                headers_to_print.append(message.hint('NON_MAIN_ARENA'))

            print(' | '.join(headers_to_print))
            for key, val in chunk.items():
                print(message.system(key) + ": 0x{:02x}".format(int(val)))
            print('')
            return

        arena = allocator.get_arena_for_chunk(cursor)
        arena_address = None
        is_top = False
        if not fake and arena:
            arena_address = arena.address
            top_chunk = arena['top']
            if cursor == top_chunk:
                headers_to_print.append(message.off("Top".center(9, " ")))
                is_top = True

        if not is_top:
            fastbins = allocator.fastbins(arena_address) or {}
            smallbins = allocator.smallbins(arena_address) or {}
            largebins = allocator.largebins(arena_address) or {}
            unsortedbin = allocator.unsortedbin(arena_address) or {}
            if allocator.has_tcache():
                tcachebins = allocator.tcachebins(None)

            if real_size in fastbins.keys() and cursor in fastbins[real_size]:
                headers_to_print.append(message.on("F(fast)".center(9, " ")))
                if not verbose:
                    fields_to_print.add('fd')

            elif real_size in smallbins.keys() and cursor in bin_addrs(
                    smallbins[real_size], "smallbins"):
                headers_to_print.append(message.on("F(small)".center(9, " ")))
                if not verbose:
                    fields_to_print.update(['fd', 'bk'])

            elif real_size >= list(
                    largebins.items())[0][0] and cursor in bin_addrs(
                        largebins[(list(largebins.items())[
                            allocator.largebin_index(real_size) - 64][0])],
                        "largebins"):
                headers_to_print.append(message.on("F(large)".center(9, " ")))
                if not verbose:
                    fields_to_print.update(
                        ['fd', 'bk', 'fd_nextsize', 'bk_nextsize'])

            elif cursor in bin_addrs(unsortedbin['all'], "unsortedbin"):
                headers_to_print.append(message.on("F(unsort)".center(9, " ")))
                if not verbose:
                    fields_to_print.update(['fd', 'bk'])

            elif allocator.has_tcache() and real_size in tcachebins.keys(
            ) and cursor + ptr_size * 2 in bin_addrs(tcachebins[real_size],
                                                     "tcachebins"):
                headers_to_print.append(message.on("F(tcache)".center(9, " ")))
                if not verbose:
                    fields_to_print.add('fd')

            else:
                headers_to_print.append(
                    message.hint("Allocated".center(9, " ")))

        if verbose:
            fields_to_print.update([
                'prev_size', 'size', 'fd', 'bk', 'fd_nextsize', 'bk_nextsize'
            ])
        else:
            out_fields += "Size: 0x{:02x}\n".format(size_field)

        prev_inuse, is_mmapped, non_main_arena = allocator.chunk_flags(
            size_field)
        if prev_inuse:
            headers_to_print.append(message.hint('PREV_INUSE'))
        if is_mmapped:
            headers_to_print.append(message.hint('IS_MMAPED'))
        if non_main_arena:
            headers_to_print.append(message.hint('NON_MAIN_ARENA'))

        ## original end
        if not verbose:
            def ascii_char(byte):
                if byte >= 0x20 and byte < 0x7e:
                    return chr(byte)  # Ensure we return a str
                else:
                    return "."

            content = ""
            content += "[{:03d}] ".format(idx)

            content += M.get(cursor) + f" SIZE=0x{size_field:08x}"
            data = addr + pwndbg.arch.ptrsize * 2
            content += " DATA[" + hex(data) + "]"
            if size_field >= 0x20:
                bytes_read = pwndbg.memory.read(data, 0x20, partial=True)
            else:
                bytes_read = pwndbg.memory.read(data, size, partial=True)
            content += " |" + "".join([ascii_char(c) for c in bytes_read]) + "| "
            print(content + " | ".join(headers_to_print))
        else:
            print_chunk_detail(addr, size_field)
Exemple #27
0
def heap(addr=None, verbose=False):
    """Iteratively print chunks on a heap, default to the current thread's
    active heap.
    """
    allocator = pwndbg.heap.current
    heap_region = allocator.get_heap_boundaries(addr)
    arena = allocator.get_arena_for_chunk(
        addr) if addr else allocator.get_arena()

    top_chunk = arena['top']
    ptr_size = allocator.size_sz

    # Calculate where to start printing; if an address was supplied, use that,
    # if this heap belongs to the main arena, start at the beginning of the
    # heap's mapping, otherwise, compensate for the presence of a heap_info
    # struct and possibly an arena.
    if addr:
        cursor = int(addr)
    elif arena == allocator.main_arena:
        cursor = heap_region.start
    else:
        cursor = heap_region.start + allocator.heap_info.sizeof
        if pwndbg.vmmap.find(allocator.get_heap(
                heap_region.start)['ar_ptr']) == heap_region:
            # Round up to a 2-machine-word alignment after an arena to
            # compensate for the presence of the have_fastchunks variable
            # in GLIBC versions >= 2.27.
            cursor += (allocator.malloc_state.sizeof +
                       ptr_size) & ~allocator.malloc_align_mask

    # i686 alignment heuristic
    first_chunk_size = pwndbg.arch.unpack(
        pwndbg.memory.read(cursor + ptr_size, ptr_size))
    if first_chunk_size == 0:
        cursor += ptr_size * 2

    while cursor in heap_region:
        old_cursor = cursor
        size_field = pwndbg.memory.u(cursor +
                                     allocator.chunk_key_offset('size'))
        real_size = size_field & ~allocator.malloc_align_mask

        if cursor == top_chunk:
            out = message.off("Top chunk\n")
            out += "Addr: {}\nSize: 0x{:02x}".format(M.get(cursor), size_field)
            print(out)
            break

        fastbins = allocator.fastbins(arena.address)
        smallbins = allocator.smallbins(arena.address)
        largebins = allocator.largebins(arena.address)
        unsortedbin = allocator.unsortedbin(arena.address)
        if allocator.has_tcache():
            tcachebins = allocator.tcachebins(None)

        out = "Addr: {}\nSize: 0x{:02x}\n".format(M.get(cursor), size_field)

        if real_size in fastbins.keys() and cursor in fastbins[real_size]:
            out = message.on("Free chunk (fastbins)\n") + out
            if not verbose:
                out += message.system("fd: ") + "0x{:02x}\n".format(
                    pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd')))
        elif real_size in smallbins.keys() and cursor in bin_addrs(
                smallbins[real_size], "smallbins"):
            out = message.on("Free chunk (smallbins)\n") + out
            if not verbose:
                out += message.system("fd: ") + "0x{:02x}\n".format(
                    pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd')))
                out += message.system("bk: ") + "0x{:02x}\n".format(
                    pwndbg.memory.u(cursor + allocator.chunk_key_offset('bk')))
        elif real_size >= list(
                largebins.items())[0][0] and cursor in bin_addrs(
                    largebins[(list(
                        largebins.items())[allocator.largebin_index(real_size)
                                           - 64][0])], "largebins"):
            out = message.on("Free chunk (largebins)\n") + out
            if not verbose:
                out += message.system("fd: ") + "0x{:02x}\n".format(
                    pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd')))
                out += message.system("bk: ") + "0x{:02x}\n".format(
                    pwndbg.memory.u(cursor + allocator.chunk_key_offset('bk')))
                out += message.system("fd_nextsize: ") + "0x{:02x}\n".format(
                    pwndbg.memory.u(cursor +
                                    allocator.chunk_key_offset('fd_nextsize')))
                out += message.system("bk_nextsize: ") + "0x{:02x}\n".format(
                    pwndbg.memory.u(cursor +
                                    allocator.chunk_key_offset('bk_nextsize')))
        elif cursor in bin_addrs(unsortedbin['all'], "unsortedbin"):
            out = message.on("Free chunk (unsortedbin)\n") + out
            if not verbose:
                out += message.system("fd: ") + "0x{:02x}\n".format(
                    pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd')))
                out += message.system("bk: ") + "0x{:02x}\n".format(
                    pwndbg.memory.u(cursor + allocator.chunk_key_offset('bk')))
        elif allocator.has_tcache() and real_size in tcachebins.keys(
        ) and cursor + ptr_size * 2 in bin_addrs(tcachebins[real_size],
                                                 "tcachebins"):
            out = message.on("Free chunk (tcache)\n") + out
            if not verbose:
                out += message.system("fd: ") + "0x{:02x}\n".format(
                    pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd')))
        else:
            out = message.hint("Allocated chunk\n") + out

        if verbose:
            out += message.system("fd: ") + "0x{:02x}\n".format(
                pwndbg.memory.u(cursor + allocator.chunk_key_offset('fd')))
            out += message.system("bk: ") + "0x{:02x}\n".format(
                pwndbg.memory.u(cursor + allocator.chunk_key_offset('bk')))
            out += message.system("fd_nextsize: ") + "0x{:02x}\n".format(
                pwndbg.memory.u(cursor +
                                allocator.chunk_key_offset('fd_nextsize')))
            out += message.system("bk_nextsize: ") + "0x{:02x}\n".format(
                pwndbg.memory.u(cursor +
                                allocator.chunk_key_offset('bk_nextsize')))

        print(out)
        cursor += real_size

        # Avoid an infinite loop when a chunk's size is 0.
        if cursor == old_cursor:
            break