Add commands to parse mangling pointers glibc heap ##heap (#17534)

* Add dmhtm for tcache mangling pointers

* Add dmhfm for fastbins with mang ptrs

* Add dmhfm <n> for fastbins

* Pointer mangling support to dmh

* Fix spacing for PROTECT_PTR

* Add fastbins checks for dmh

* Clean code for next_pointer

* Use ternary operator when possible

* Add dbg.glibc.demangle option

* Set correct initial_brk for multi-glibc support

* Remove comments and unneeded variables

* Remove 'm' commands, use demangle variable

* Update description of dbg.glibc.demangle

* Test for tcache 2.32 and remove old ones

* Force tests

* Solve insidious bug of filenames

* Fix dmh test and more tcache testing

* Remove unused variables
This commit is contained in:
meowmeowxw 2020-08-31 16:45:12 +00:00 committed by GitHub
parent 7e28739835
commit 3ffe3f88d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 37 deletions

View File

@ -2968,6 +2968,7 @@ R_API int r_core_config_init(RCore *core) {
SETI ("dbg.glibc.ma_offset", 0x1bb000, "Main_arena offset from his symbol");
SETI ("dbg.glibc.fc_offset", 0x148, "First chunk offset from brk_start");
#endif
SETBPREF ("dbg.glibc.demangle", "false", "Demangle linked-lists pointers introduced in glibc 2.32");
SETPREF ("dbg.libc.dbglib", "", "Set libc debug library file");
SETBPREF ("esil.prestep", "true", "Step before esil evaluation in `de` commands");

View File

@ -2,6 +2,7 @@
#ifndef INCLUDE_HEAP_GLIBC_C
#define INCLUDE_HEAP_GLIBC_C
#include "r_config.h"
#define HEAP32 1
#include "linux_heap_glibc.c"
#undef HEAP32
@ -67,6 +68,10 @@ static inline GHT GH(align_address_to_size)(ut64 addr, ut64 align) {
return addr + ((align - (addr % align)) % align);
}
static inline GHT GH(get_next_pointer)(RCore *core, GHT pos, GHT next) {
return (core->dbg->glibc_version < 232) ? next : PROTECT_PTR (pos, next);
}
static GHT GH(get_main_arena_with_symbol)(RCore *core, RDebugMap *map) {
r_return_val_if_fail (core && map, GHT_MAX);
GHT base_addr = map->addr;
@ -104,9 +109,12 @@ static bool GH(is_tcache)(RCore *core) {
RListIter *iter;
r_debug_map_sync (core->dbg);
r_list_foreach (core->dbg->maps, iter, map) {
fp = strstr (map->name, "libc-");
if (fp) {
break;
// In case the binary is named *libc-*
if (strncmp (map->name, core->bin->file, strlen(map->name)) != 0) {
fp = strstr (map->name, "libc-");
if (fp) {
break;
}
}
}
} else {
@ -116,7 +124,7 @@ static bool GH(is_tcache)(RCore *core) {
}
if (fp) {
v = r_num_get_float (NULL, fp + 5);
core->dbg->glibc_version = (int) (v * 100);
core->dbg->glibc_version = (int) round((v * 100));
}
return (v > 2.25);
}
@ -747,7 +755,7 @@ static void GH(print_heap_bin)(RCore *core, GHT m_arena, MallocState *main_arena
}
}
static int GH(print_single_linked_list_bin)(RCore *core, MallocState *main_arena, GHT m_arena, GHT offset, GHT bin_num) {
static int GH(print_single_linked_list_bin)(RCore *core, MallocState *main_arena, GHT m_arena, GHT offset, GHT bin_num, bool demangle) {
if (!core || !core->dbg || !core->dbg->maps) {
return -1;
}
@ -789,7 +797,7 @@ static int GH(print_single_linked_list_bin)(RCore *core, MallocState *main_arena
PRINTF_BA ("0x%"PFMT64x, (ut64)next);
while (double_free == GHT_MAX && next_tmp && next_tmp >= brk_start && next_tmp <= main_arena->GH(top)) {
r_io_read_at (core->io, next_tmp, (ut8 *)cnk, sizeof (GH(RHeapChunk)));
next_tmp = cnk->fd;
next_tmp = (!demangle) ? cnk->fd : PROTECT_PTR (next_tmp, cnk->fd);
if (cnk->prev_size > size || ((cnk->size >> 3) << 3) > size) {
break;
}
@ -799,7 +807,7 @@ static int GH(print_single_linked_list_bin)(RCore *core, MallocState *main_arena
}
}
r_io_read_at (core->io, next, (ut8 *)cnk, sizeof (GH(RHeapChunk)));
next = cnk->fd;
next = (!demangle) ? cnk->fd : PROTECT_PTR (next, cnk->fd);
PRINTF_BA ("%s", next ? "->fd = " : "");
if (cnk->prev_size > size || ((cnk->size >> 3) << 3) > size) {
PRINTF_RA (" 0x%"PFMT64x, (ut64)next);
@ -832,7 +840,7 @@ static int GH(print_single_linked_list_bin)(RCore *core, MallocState *main_arena
return 0;
}
void GH(print_heap_fastbin)(RCore *core, GHT m_arena, MallocState *main_arena, GHT global_max_fast, const char *input) {
void GH(print_heap_fastbin)(RCore *core, GHT m_arena, MallocState *main_arena, GHT global_max_fast, const char *input, bool demangle) {
int i;
GHT num_bin = GHT_MAX, offset = sizeof (int) * 2;
const int tcache = r_config_get_i (core->config, "dbg.glibc.tcache");
@ -854,7 +862,7 @@ void GH(print_heap_fastbin)(RCore *core, GHT m_arena, MallocState *main_arena, G
} else {
PRINTF_RA (" Fastbin %02d\n", i);
}
if (GH(print_single_linked_list_bin) (core, main_arena, m_arena, offset, i - 1)) {
if (GH(print_single_linked_list_bin) (core, main_arena, m_arena, offset, i - 1, demangle)) {
PRINT_GA (" Empty bin");
PRINT_BA (" 0x0\n");
}
@ -867,7 +875,7 @@ void GH(print_heap_fastbin)(RCore *core, GHT m_arena, MallocState *main_arena, G
eprintf ("Error: 0 < bin <= %d\n", NFASTBINS);
break;
}
if (GH(print_single_linked_list_bin)(core, main_arena, m_arena, offset, num_bin)) {
if (GH(print_single_linked_list_bin)(core, main_arena, m_arena, offset, num_bin, demangle)) {
PRINT_GA (" Empty bin");
PRINT_BA (" 0x0\n");
}
@ -917,7 +925,7 @@ static GHT GH (tcache_get_entry) (GH (RTcache)* tcache, int index) {
: tcache->RHeapTcache.heap_tcache_pre_230->entries[index];
}
static void GH (tcache_print) (RCore *core, GH (RTcache)* tcache) {
static void GH (tcache_print) (RCore *core, GH (RTcache)* tcache, bool demangle) {
r_return_if_fail (core && tcache);
GHT tcache_fd = GHT_MAX;
GHT tcache_tmp = GHT_MAX;
@ -942,7 +950,9 @@ static void GH (tcache_print) (RCore *core, GH (RTcache)* tcache) {
if (!r) {
break;
}
tcache_tmp = read_le (&tcache_tmp);
tcache_tmp = (!demangle)
? read_le (&tcache_tmp)
: PROTECT_PTR (tcache_fd, read_le (&tcache_tmp));
PRINTF_BA ("->0x%"PFMT64x, tcache_tmp - TC_HDR_SZ);
tcache_fd = tcache_tmp;
}
@ -952,7 +962,7 @@ static void GH (tcache_print) (RCore *core, GH (RTcache)* tcache) {
}
}
static void GH (print_tcache_instance)(RCore *core, GHT m_arena, MallocState *main_arena) {
static void GH (print_tcache_instance)(RCore *core, GHT m_arena, MallocState *main_arena, bool demangle) {
r_return_if_fail (core && core->dbg && core->dbg->maps);
const int tcache = r_config_get_i (core->config, "dbg.glibc.tcache");
@ -982,7 +992,7 @@ static void GH (print_tcache_instance)(RCore *core, GHT m_arena, MallocState *ma
PRINT_GA("Tcache main arena @");
PRINTF_BA (" 0x%"PFMT64x"\n", (ut64)m_arena);
GH (tcache_print) (core, r_tcache);
GH (tcache_print) (core, r_tcache, demangle);
if (main_arena->GH (next) != m_arena) {
GHT mmap_start = GHT_MAX, tcache_start = GHT_MAX;
@ -1008,7 +1018,7 @@ static void GH (print_tcache_instance)(RCore *core, GHT m_arena, MallocState *ma
if (ta->attached_threads) {
PRINT_BA ("\n");
GH (tcache_read) (core, tcache_start, r_tcache);
GH (tcache_print) (core, r_tcache);
GH (tcache_print) (core, r_tcache, demangle);
} else {
PRINT_GA (" free\n");
}
@ -1032,19 +1042,22 @@ static void GH(print_heap_segment)(RCore *core, MallocState *main_arena,
const int tcache = r_config_get_i (core->config, "dbg.glibc.tcache");
const int offset = r_config_get_i (core->config, "dbg.glibc.fc_offset");
RConsPrintablePalette *pal = &r_cons_singleton ()->context->pal;
bool is_main_arena = true;
int glibc_version = core->dbg->glibc_version;
if (m_arena == m_state) {
GH(get_brks) (core, &brk_start, &brk_end);
if (tcache) {
//tcache_initial_brk = ((brk_start >> 12) << 12) + GH(HDR_SZ);
GHT fc_offset = GH(tcache_chunk_size) (core, brk_start);
initial_brk = ((brk_start >> 12) << 12) + fc_offset;
initial_brk = ((brk_start >> 12) << 12) + GH(HDR_SZ);
if (r_config_get_i (core->config, "cfg.debug")) {
tcache_initial_brk = initial_brk;
}
initial_brk += (glibc_version < 230)
? sizeof (GH (RHeapTcachePre230))
: sizeof (GH (RHeapTcache));
} else {
initial_brk = (brk_start >> 12) << 12;
}
} else {
is_main_arena = false;
brk_start = ((m_state >> 16) << 16) ;
brk_end = brk_start + main_arena->GH(system_mem);
if (tcache) {
@ -1171,7 +1184,7 @@ static void GH(print_heap_segment)(RCore *core, MallocState *main_arena,
int i = (size_tmp / (SZ * 2)) - 2;
GHT idx = (GHT)main_arena->GH(fastbinsY)[i];
(void)r_io_read_at (core->io, idx, (ut8 *)cnk, sizeof (GH(RHeapChunk)));
GHT next = cnk->fd;
GHT next = GH (get_next_pointer) (core, idx, cnk->fd);
if (prev_chunk == idx && idx && !next) {
is_free = true;
}
@ -1183,7 +1196,7 @@ static void GH(print_heap_segment)(RCore *core, MallocState *main_arena,
break;
}
(void)r_io_read_at (core->io, next, (ut8 *)cnk_next, sizeof (GH(RHeapChunk)));
GHT next_node = cnk_next->fd;
GHT next_node = GH (get_next_pointer) (core, next, cnk_next->fd);
// avoid triple while?
while (next_node && next_node >= brk_start && next_node < main_arena->GH(top)) {
if (prev_chunk == next_node) {
@ -1191,14 +1204,14 @@ static void GH(print_heap_segment)(RCore *core, MallocState *main_arena,
break;
}
(void)r_io_read_at (core->io, next_node, (ut8 *)cnk_next, sizeof (GH(RHeapChunk)));
next_node = cnk_next->fd;
next_node = GH (get_next_pointer) (core, next_node, cnk_next->fd);
}
if (double_free) {
break;
}
}
(void)r_io_read_at (core->io, next, (ut8 *)cnk, sizeof (GH(RHeapChunk)));
next = cnk->fd;
next = GH (get_next_pointer) (core, next, cnk->fd);
}
if (double_free) {
PRINT_RA (" Double free in simple-linked list detected ");
@ -1237,7 +1250,7 @@ static void GH(print_heap_segment)(RCore *core, MallocState *main_arena,
if (!r) {
break;
}
tcache_tmp = read_le (&tcache_tmp);
tcache_tmp = GH (get_next_pointer) (core, tcache_fd, read_le (&tcache_tmp));
if (tcache_tmp - SZ * 2 == prev_chunk) {
is_free = true;
prev_chunk_size = ((i + 1) * TC_HDR_SZ + GH(TC_SZ));
@ -1248,9 +1261,6 @@ static void GH(print_heap_segment)(RCore *core, MallocState *main_arena,
}
}
}
if (is_main_arena) {
tcache_initial_brk = brk_start + 0x10;
}
GH (tcache_free) (tcache_heap);
}
@ -1595,6 +1605,7 @@ static int GH(cmd_dbg_map_heap_glibc)(RCore *core, const char *input) {
break;
case 'f': // "dmhf"
if (GH(r_resolve_main_arena) (core, &m_arena)) {
bool demangle = r_config_get_i (core->config, "dbg.glibc.demangle");
char *m_state_str, *dup = strdup (input + 1);
if (*dup) {
strtok (dup, ":");
@ -1615,7 +1626,7 @@ static int GH(cmd_dbg_map_heap_glibc)(RCore *core, const char *input) {
free (dup);
break;
}
GH(print_heap_fastbin) (core, m_state, main_arena, global_max_fast, dup);
GH(print_heap_fastbin) (core, m_state, main_arena, global_max_fast, dup, demangle);
} else {
PRINT_RA ("This address is not part of the arenas\n");
free (dup);
@ -1666,7 +1677,8 @@ static int GH(cmd_dbg_map_heap_glibc)(RCore *core, const char *input) {
if (!GH(update_main_arena) (core, m_arena, main_arena)) {
break;
}
GH(print_tcache_instance) (core, m_arena, main_arena);
bool demangle = r_config_get_i (core->config, "dbg.glibc.demangle");
GH(print_tcache_instance) (core, m_arena, main_arena, demangle);
}
break;
case '?':

View File

@ -47,6 +47,10 @@ R_LIB_VERSION_HEADER(r_heap_glibc);
#define TC_SZ_32 0x0
#define TC_SZ_64 0x10
// Introduced with glibc 2.32
#define PROTECT_PTR(pos, ptr) \
((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
#define largebin_index_32(size) \
(((((ut32)(size)) >> 6) <= 38)? 56 + (((ut32)(size)) >> 6): \
((((ut32)(size)) >> 9) <= 20)? 91 + (((ut32)(size)) >> 9): \

View File

@ -1,26 +1,61 @@
NAME=check tcache list
FILE=bins/elf/tcache
ARGS=-d
NAME=check tcache mangling pointers on glibc 2.32
FILE=bins/elf/glibc-heap-2.32
ARGS=-Rsetenv=LD_PRELOAD=bins/elf/libc-2.32.so -d
CMDS=<<EOF
db 0x004011d0
db 0x004011fa
dc
dmht~?0xffffffffffff
e dbg.glibc.demangle = true
dmht~?0xffffffffffff
dmht~?items : 7
dc
dmht~?items : 6
dc
dmht~?items : 5
EOF
EXPECT=<<EOF
1
0
1
1
1
EOF
RUN
NAME=check tcache list on glibc 2.31
FILE=bins/elf/glibc-heap-2.31
ARGS=-Rsetenv=LD_PRELOAD=bins/elf/libc-2.31.so -d
CMDS=<<EOF
db 0x004011fa
dc
dmht~?items : 7
dc
dmht~?items : 6
dc
dmht~?items : 5
EOF
EXPECT=<<EOF
1
1
1
EOF
RUN
NAME=check tcache list older versions
FILE=bins/elf/tcache-2.27
NAME=check tcache list on glibc 2.27
FILE=bins/elf/glibc-heap-2.27
ARGS=-Rsetenv=LD_PRELOAD=bins/elf/libc-2.27.so -d
CMDS=<<EOF
db 0x004011d0
db 0x004011fa
dc
dmht~?items : 7
dc
dmht~?items : 6
dc
dmht~?items : 5
EOF
EXPECT=<<EOF
1
1
1
EOF
RUN