add vis_heap_chunk testcase

pull/1086/head
disconnect3d 3 years ago
parent b51b07de77
commit 902d732816

@ -0,0 +1,21 @@
#include <stdio.h>
#include <stdlib.h>
void break_here() {}
int main() {
void* allocs[6] = {0};
allocs[0] = malloc(10);
allocs[1] = malloc(10);
break_here();
allocs[2] = malloc(40);
break_here();
free(allocs[1]);
break_here();
}

@ -81,6 +81,9 @@ heap_bins: heap_bins.c
-Wl,--dynamic-linker=${GLIBC_2_33}/ld-linux-x86-64.so.2 \
-g -O0 -o heap_bins.out heap_bins.c
heap_vis: heap_vis.c
${CC} -g -O0 -o heap_vis.out heap_vis.c
clean :
@echo "[+] Cleaning stuff"
@rm -f $(COMPILED) $(LINKED) $(COMPILED_ASM) $(LINKED_ASM) $(COMPILED_GO)

@ -12,6 +12,8 @@ HEAP_BINARY = tests.binaries.get('heap_bugs.out')
HEAP_CODE = tests.binaries.get('heap_bugs.c')
_, OUTPUT_FILE = tempfile.mkstemp()
HEAP_VIS = tests.binaries.get('heap_vis.out')
def binary_parse_breakpoints(binary_code):
"""
@ -214,3 +216,238 @@ def test_try_free_corrupted_unsorted_chunks(start_binary):
result = gdb.execute('try_free {}'.format(hex(chunks['f'])), to_string=True)
assert 'free(): corrupted unsorted chunks' in result
os.remove(OUTPUT_FILE)
def test_vis_heap_chunk_command(start_binary):
start_binary(HEAP_VIS)
gdb.execute('break break_here')
gdb.execute('continue')
# TODO/FIXME: Shall we have a standard method to do this kind of filtering?
# Note that we have `pages_filter` in pwndbg/pwndbg/commands/vmmap.py heh
heap_page = next(page for page in pwndbg.vmmap.get() if page.objfile == '[heap]')
# Just a sanity check...
assert (heap_page.start & 0xfff) == 0
result = gdb.execute('vis_heap_chunk 1', to_string=True).splitlines()
# We will use `heap_addr` variable to fill in proper addresses below
heap_addr = heap_page.start
heap_end = heap_page.end
def heap_iter(offset=0x10):
nonlocal heap_addr
heap_addr += offset
return heap_addr
def top_chunk_size(prev_inuse=True):
# We need to subtract -0x10 because we will execute this
# after a heap_iter call that will add +0x10 to heap_addr
size = heap_end - heap_addr #- 0x10
# Account for PREV_INUSE flag
if prev_inuse:
size |= 1
return size
def hexdump_16B(gdb_symbol):
from pwndbg.commands.heap import bin_ascii
first, second = gdb.execute('x/16xb %s' % gdb_symbol, to_string=True).splitlines()
first = [int(v, 16) for v in first.split(':')[1].split('\t')[1:]]
second = [int(v, 16) for v in second.split(':')[1].split('\t')[1:]]
return bin_ascii(first + second)
expected = ['',
'%#x\t0x0000000000000000\t0x0000000000000291\t................' % heap_iter(0),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000' % heap_iter()
]
assert result == expected
del result
## Test vis_heap_chunk with count=2
result2 = gdb.execute('vis_heap_chunk 2', to_string=True).splitlines()
# Note: we copy expected here but we truncate last line as it is easier
# to provide it in full here
expected2 = expected[:-1] + [
'%#x\t0x0000000000000000\t0x0000000000000021\t........!.......' % heap_iter(0),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000' % heap_iter()
]
assert result2 == expected2
del expected
del result2
## Test vis_heap_chunk with count=3
result3 = gdb.execute('vis_heap_chunk 3', to_string=True).splitlines()
# Note: we copy expected here but we truncate last line as it is easier
# to provide it in full here
expected3 = expected2[:-1] + [
'%#x\t0x0000000000000000\t0x0000000000000021\t........!.......' % heap_iter(0),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t%#018x\t........1.......\t <-- Top chunk' % (heap_iter(), top_chunk_size())
]
assert result3 == expected3
del expected2
del result3
## Test vis_heap_chunk with count=4
result4 = gdb.execute('vis_heap_chunk 4', to_string=True).splitlines()
# Since on this breakpoint we only have 4 chunks, the output should probably be the same?
# TODO/FIXME: Shall we maybe print user that there are only 3 chunks?
assert result4 == expected3
del result4
## Test vis_heap_chunk with no flags
result_all = gdb.execute('vis_heap_chunk', to_string=True).splitlines()
assert result_all == expected3
del result_all
# Continue, so that another allocation is made
gdb.execute('continue')
## Test vis_heap_chunk with count=4 again
result4_b = gdb.execute('vis_heap_chunk 4', to_string=True).splitlines()
expected4_b = expected3[:-1] + [
'%#x\t0x0000000000000000\t0x0000000000000031\t........1.......' % heap_iter(0),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................' % heap_iter(),
'%#x\t0x0000000000000000\t%#018x\t................\t <-- Top chunk' % (heap_iter(), top_chunk_size())
]
assert result4_b == expected4_b
del expected3
del result4_b
## Test vis_heap_chunk with no flags
result_all2 = gdb.execute('vis_heap_chunk', to_string=True).splitlines()
assert result_all2 == expected4_b
del result_all2
## Continue, so that alloc[1] is freed
gdb.execute('continue')
result_all3 = gdb.execute('vis_heap_chunk', to_string=True).splitlines()
# The tcache chunks have two fields: next and key
# We are fetching it from the glibc's TLS tcache variable :)
tcache_next = int(gdb.parse_and_eval('tcache->entries[0]->next'))
tcache_key = int(gdb.parse_and_eval('tcache->entries[0]->key'))
tcache_hexdump = hexdump_16B('tcache->entries[0]')
freed_chunk = '%#x\t%#018x\t%#018x\t%s\t ' % (heap_iter(-0x40), tcache_next, tcache_key, tcache_hexdump)
freed_chunk += '<-- tcachebins[0x20][0/1]'
heap_addr = heap_page.start
# This is not ideal, but hopefully it works on different builds // feel free to name it better
some_addr = heap_addr + 0x2c0
some_addr_hexdump = hexdump_16B(hex(heap_addr + 0x90))
expected_all3 = ['',
'%#x\t0x0000000000000000\t0x0000000000000291\t................'% heap_iter(0),
'%#x\t0x0000000000000001\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t%#018x\t0x0000000000000000\t%s' % (heap_iter(), some_addr, some_addr_hexdump),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000021\t........!.......'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000021\t........!.......'% heap_iter(),
'%#x\t%#018x\t%#018x\t%s\t <-- tcachebins[0x20][0/1]'% (heap_iter(), tcache_next, tcache_key, tcache_hexdump),
'%#x\t0x0000000000000000\t0x0000000000000031\t........1.......'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t0x0000000000000000\t................'% heap_iter(),
'%#x\t0x0000000000000000\t%#018x\t................\t <-- Top chunk'% (heap_iter(), top_chunk_size())
]
assert result_all3 == expected_all3

Loading…
Cancel
Save