From 6fabb6d16afadd734cd7c443f04bbb0ab57e493c Mon Sep 17 00:00:00 2001 From: Pulak Malhotra <56169176+PulakIIIT@users.noreply.github.com> Date: Thu, 17 Jun 2021 20:17:10 +0530 Subject: [PATCH] Refactor `dm` commands ##refactor --- librz/core/cdebug.c | 237 ++- librz/core/cmd.c | 1 + librz/core/cmd_debug.c | 875 +++++----- librz/core/cmd_descs/cmd_debug.yaml | 198 ++- librz/core/cmd_descs/cmd_descs.c | 412 +++++ librz/core/cmd_descs/cmd_descs.h | 27 + librz/core/cmd_descs/cmd_heap_glibc.yaml | 85 + librz/core/cmd_descs/meson.build | 1 + librz/core/cmd_linux_heap_glibc.c | 53 + librz/core/linux_heap_glibc.c | 1951 ++++++++++++---------- librz/core/linux_heap_jemalloc.c | 10 +- librz/core/windows_heap.c | 14 +- librz/debug/dmap.c | 282 +--- librz/include/rz_core.h | 17 + librz/include/rz_debug.h | 11 +- librz/include/rz_heap_glibc.h | 8 +- test/db/archos/linux-x64/dbg_aslr | 2 +- test/db/archos/linux-x64/dbg_dmh | 54 +- test/db/json/json2 | 1 - 19 files changed, 2595 insertions(+), 1644 deletions(-) create mode 100644 librz/core/cmd_descs/cmd_heap_glibc.yaml create mode 100644 librz/core/cmd_linux_heap_glibc.c diff --git a/librz/core/cdebug.c b/librz/core/cdebug.c index b52ad338cf1..6228f574c27 100644 --- a/librz/core/cdebug.c +++ b/librz/core/cdebug.c @@ -297,7 +297,7 @@ RZ_IPI bool rz_core_debug_reg_list(RzCore *core, int type, int size, PJ *pj, int return false; } if (rad == 1 || rad == '*') { - dbg->cb_printf("fs+%s\n", RZ_FLAGS_FS_REGISTERS); + rz_cons_printf("fs+%s\n", RZ_FLAGS_FS_REGISTERS); } rz_list_foreach (head, iter, item) { ut64 value; @@ -442,7 +442,7 @@ RZ_IPI bool rz_core_debug_reg_list(RzCore *core, int type, int size, PJ *pj, int n++; } if (rad == 1 || rad == '*') { - dbg->cb_printf("fs-\n"); + rz_cons_printf("fs-\n"); } beach: if (isJson) { @@ -639,3 +639,236 @@ RZ_API RzCmdStatus rz_core_debug_plugins_print(RzCore *core, RzCmdStateOutput *s rz_cmd_state_output_array_end(state); return RZ_CMD_STATUS_OK; } + +/* Print out the JSON body for memory maps in the passed map region */ +static void print_debug_map_json(RzDebugMap *map, PJ *pj) { + pj_o(pj); + if (map->name && *map->name) { + pj_ks(pj, "name", map->name); + } + if (map->file && *map->file) { + pj_ks(pj, "file", map->file); + } + pj_kn(pj, "addr", map->addr); + pj_kn(pj, "addr_end", map->addr_end); + pj_ks(pj, "type", map->user ? "u" : "s"); + pj_ks(pj, "perm", rz_str_rwx_i(map->perm)); + pj_end(pj); +} + +/* Write a single memory map line to the console */ +static void print_debug_map_line(RzDebug *dbg, RzDebugMap *map, ut64 addr, RzOutputMode mode) { + char humansz[8]; + if (mode == RZ_OUTPUT_MODE_QUIET) { // "dmq" + char *name = (map->name && *map->name) + ? rz_str_newf("%s.%s", map->name, rz_str_rwx_i(map->perm)) + : rz_str_newf("%08" PFMT64x ".%s", map->addr, rz_str_rwx_i(map->perm)); + rz_name_filter(name, 0, true); + rz_num_units(humansz, sizeof(humansz), map->addr_end - map->addr); + rz_cons_printf("0x%016" PFMT64x " - 0x%016" PFMT64x " %6s %5s %s\n", + map->addr, + map->addr_end, + humansz, + rz_str_rwx_i(map->perm), + name); + free(name); + } else { + const char *fmtstr = dbg->bits & RZ_SYS_BITS_64 + ? "0x%016" PFMT64x " - 0x%016" PFMT64x " %c %s %6s %c %s %s %s%s%s\n" + : "0x%08" PFMT64x " - 0x%08" PFMT64x " %c %s %6s %c %s %s %s%s%s\n"; + const char *type = map->shared ? "sys" : "usr"; + const char *flagname = dbg->corebind.getName + ? dbg->corebind.getName(dbg->corebind.core, map->addr) + : NULL; + if (!flagname) { + flagname = ""; + } else if (map->name) { + char *filtered_name = strdup(map->name); + rz_name_filter(filtered_name, 0, true); + if (!strncmp(flagname, "map.", 4) && + !strcmp(flagname + 4, filtered_name)) { + flagname = ""; + } + free(filtered_name); + } + rz_num_units(humansz, sizeof(humansz), map->size); + rz_cons_printf(fmtstr, + map->addr, + map->addr_end, + (addr >= map->addr && addr < map->addr_end) ? '*' : '-', + type, + humansz, + map->user ? 'u' : 's', + rz_str_rwx_i(map->perm), + map->name ? map->name : "?", + map->file ? map->file : "?", + *flagname ? " ; " : "", + flagname); + } +} + +RZ_API void rz_debug_map_print(RzDebug *dbg, ut64 addr, RzCmdStateOutput *state) { + int i; + RzListIter *iter; + RzDebugMap *map; + PJ *pj = state->d.pj; + if (!dbg) { + return; + } + RzOutputMode mode = state->mode; + rz_cmd_state_output_array_start(state); + for (i = 0; i < 2; i++) { // Iterate over dbg::maps and dbg::maps_user + RzList *maps = rz_debug_map_list(dbg, (bool)i); + rz_list_foreach (maps, iter, map) { + switch (mode) { + case RZ_OUTPUT_MODE_JSON: // "dmj" + print_debug_map_json(map, pj); + break; + case RZ_OUTPUT_MODE_RIZIN: // "dm*" + { + char *name = (map->name && *map->name) + ? rz_str_newf("%s.%s", map->name, rz_str_rwx_i(map->perm)) + : rz_str_newf("%08" PFMT64x ".%s", map->addr, rz_str_rwx_i(map->perm)); + rz_name_filter(name, 0, true); + rz_cons_printf("f map.%s 0x%08" PFMT64x " 0x%08" PFMT64x "\n", + name, map->addr_end - map->addr + 1, map->addr); + free(name); + } break; + case RZ_OUTPUT_MODE_QUIET: // "dmq" + print_debug_map_line(dbg, map, addr, mode); + break; + case RZ_OUTPUT_MODE_LONG: // workaround for '.' + if (addr >= map->addr && addr < map->addr_end) { + print_debug_map_line(dbg, map, addr, mode); + } + break; + default: + print_debug_map_line(dbg, map, addr, mode); + break; + } + } + } + rz_cmd_state_output_array_end(state); +} + +static int cmp(const void *a, const void *b) { + RzDebugMap *ma = (RzDebugMap *)a; + RzDebugMap *mb = (RzDebugMap *)b; + return ma->addr - mb->addr; +} + +/** + * \brief Find the min and max addresses in an RzList of maps. + * \param maps RzList of maps that will be searched through + * \param min Pointer to a ut64 that the min will be stored in + * \param max Pointer to a ut64 that the max will be stored in + * \param skip How many maps to skip at the start of iteration + * \param width Divisor for the return value + * \return (max-min)/width + * + * Used to determine the min & max addresses of maps and + * scale the ascii bar to the width of the terminal + */ +static int findMinMax(RzList *maps, ut64 *min, ut64 *max, int skip, int width) { + RzDebugMap *map; + RzListIter *iter; + *min = UT64_MAX; + *max = 0; + rz_list_foreach (maps, iter, map) { + if (skip > 0) { + skip--; + continue; + } + if (map->addr < *min) { + *min = map->addr; + } + if (map->addr_end > *max) { + *max = map->addr_end; + } + } + return (int)(*max - *min) / width; +} + +static void print_debug_maps_ascii_art(RzDebug *dbg, RzList *maps, ut64 addr, int colors) { + ut64 mul; // The amount of address space a single console column will represent in bar graph + ut64 min = -1, max = 0; + int width = rz_cons_get_size(NULL) - 90; + RzListIter *iter; + RzDebugMap *map; + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + if (width < 1) { + width = 30; + } + rz_list_sort(maps, cmp); + mul = findMinMax(maps, &min, &max, 0, width); + ut64 last = min; + if (min != -1 && mul != 0) { + const char *color_prefix = ""; // Color escape code prefixed to string (address coloring) + const char *color_suffix = ""; // Color escape code appended to end of string + const char *fmtstr; + char humansz[8]; // Holds the human formatted size string [124K] + int skip = 0; // Number of maps to skip when re-calculating the minmax + rz_list_foreach (maps, iter, map) { + rz_num_units(humansz, sizeof(humansz), map->size); // Convert map size to human readable string + if (colors) { + color_suffix = Color_RESET; + if ((map->perm & 2) && (map->perm & 1)) { // Writable & Executable + color_prefix = pal->widget_sel; + } else if (map->perm & 2) { // Writable + color_prefix = pal->graph_false; + } else if (map->perm & 1) { // Executable + color_prefix = pal->graph_true; + } else { + color_prefix = ""; + color_suffix = ""; + } + } else { + color_prefix = ""; + color_suffix = ""; + } + if ((map->addr - last) > UT32_MAX) { // TODO: Comment what this is for + mul = findMinMax(maps, &min, &max, skip, width); // Recalculate minmax + } + skip++; + fmtstr = dbg->bits & RZ_SYS_BITS_64 // Prefix formatting string (before bar) + ? "map %4.8s %c %s0x%016" PFMT64x "%s |" + : "map %4.8s %c %s0x%08" PFMT64x "%s |"; + rz_cons_printf(fmtstr, humansz, + (addr >= map->addr && + addr < map->addr_end) + ? '*' + : '-', + color_prefix, map->addr, color_suffix); // * indicates map is within our current sought offset + int col; + for (col = 0; col < width; col++) { // Iterate over the available width/columns for bar graph + ut64 pos = min + (col * mul); // Current address space to check + ut64 npos = min + ((col + 1) * mul); // Next address space to check + if (map->addr < npos && map->addr_end > pos) { + rz_cons_printf("#"); // TODO: Comment what a # represents + } else { + rz_cons_printf("-"); + } + } + fmtstr = dbg->bits & RZ_SYS_BITS_64 ? // Suffix formatting string (after bar) + "| %s0x%016" PFMT64x "%s %s %s\n" + : "| %s0x%08" PFMT64x "%s %s %s\n"; + rz_cons_printf(fmtstr, color_prefix, map->addr_end, color_suffix, + rz_str_rwx_i(map->perm), map->name); + last = map->addr; + } + } +} + +RZ_API void rz_debug_map_list_visual(RzDebug *dbg, ut64 addr, const char *input, int colors) { + if (!dbg) { + return; + } + int i; + for (i = 0; i < 2; i++) { // Iterate over dbg::maps and dbg::maps_user + RzList *maps = rz_debug_map_list(dbg, (bool)i); + if (!maps) { + continue; + } + print_debug_maps_ascii_art(dbg, maps, addr, colors); + } +} diff --git a/librz/core/cmd.c b/librz/core/cmd.c index fb50ecc7688..8fdb0472abf 100644 --- a/librz/core/cmd.c +++ b/librz/core/cmd.c @@ -100,6 +100,7 @@ static bool lastcmd_repeat(RzCore *core, int next); #include "cmd_help.c" #include "cmd_remote.c" #include "cmd_tasks.c" +#include "cmd_linux_heap_glibc.c" static const char *help_msg_dollar[] = { "Usage:", "$alias[=cmd] [args...]", "Alias commands and strings (See ?$? for help on $variables)", diff --git a/librz/core/cmd_debug.c b/librz/core/cmd_debug.c index f4e07902a0e..290c359bfb2 100644 --- a/librz/core/cmd_debug.c +++ b/librz/core/cmd_debug.c @@ -10,9 +10,7 @@ #define SIGKILL 9 #endif -#if __linux__ && __GNU_LIBRARY__ && __GLIBC__ && __GLIBC_MINOR__ #include "rz_heap_glibc.h" -#endif #if HAVE_JEMALLOC #include "rz_heap_jemalloc.h" @@ -223,34 +221,6 @@ static const char *help_msg_dko[] = { NULL }; -static const char *help_msg_dm[] = { - "Usage:", "dm", " # Memory maps commands", - "dm", "", "List memory maps of target process", - "dm", " address size", "Allocate bytes at
(anywhere if address is -1) in child process", - "dm=", "", "List memory maps of target process (ascii-art bars)", - "dm.", "", "Show map name of current address", - "dm*", "", "List memmaps in rizin commands", - "dm-", " address", "Deallocate memory map of
", - "dmd", "[a] [file]", "Dump current (all) debug map region to a file (from-to.dmp) (see Sd)", - "dmh", "[?]", "Show map of heap", - "dmi", " [addr|libname] [symname]", "List symbols of target lib", - "dmi*", " [addr|libname] [symname]", "List symbols of target lib in rizin commands", - "dmi.", "", "List closest symbol to the current address", - "dmiv", "", "Show address of given symbol for given lib", - "dmj", "", "List memmaps in JSON format", - "dml", " ", "Load contents of file into the current map region", - "dmm", "[?][j*]", "List modules (libraries, binaries loaded in memory)", - "dmp", "[?]
", "Change page at
with , protection (perm)", - "dms", "[?] ", "Take memory snapshot", - "dms-", " ", "Restore memory snapshot", - "dmS", " [addr|libname] [sectname]", "List sections of target lib", - "dmS*", " [addr|libname] [sectname]", "List sections of target lib in rizin commands", - "dmL", " address size", "Allocate bytes at
and promote to huge page", - //"dm, " rw- esp 9K", "set 9KB of the stack as read+write (no exec)", - "TODO:", "", "map files in process memory. (dmf file @ [addr])", - NULL -}; - static const char *help_msg_dmi[] = { "Usage: dmi", "", " # List/Load Symbols", "dmi", "[j|q|*] [libname] [symname]", "List symbols of target lib", @@ -261,22 +231,6 @@ static const char *help_msg_dmi[] = { NULL }; -static const char *help_msg_dmm[] = { - "Usage:", "dmm", " # Module memory maps commands", - "dmm", "", "List modules of target process", - "dmm*", "", "List modules of target process (rizin commands)", - "dmm.", "", "List memory map of current module", - "dmmj", "", "List modules of target process (JSON)", - NULL -}; - -static const char *help_msg_dmp[] = { - "Usage:", "dmp", " Change page permissions", - "dmp", " [addr] [size] [perms]", "Change permissions", - "dmp", " [perms]", "Change dbg.map permissions", - NULL -}; - static const char *help_msg_do[] = { "Usage:", "do", " # Debug (re)open commands", "do", "", "Open process (reload, alias for 'oo')", @@ -1257,40 +1211,45 @@ static int dump_maps(RzCore *core, int perm, const char *filename) { return ret; } -static void cmd_debug_modules(RzCore *core, int mode) { // "dmm" +static void cmd_debug_current_modules(RzCore *core, RzOutputMode mode) { // "dmm" ut64 addr = core->offset; RzDebugMap *map; RzList *list; RzListIter *iter; - - /* avoid processing the list if the user only wants help */ - if (mode == '?') { - show_help: - rz_core_cmd_help(core, help_msg_dmm); - return; - } - PJ *pj = NULL; - if (mode == 'j') { - pj = pj_new(); - if (!pj) { - return; + list = rz_debug_modules_list(core->dbg); + rz_list_foreach (list, iter, map) { + if (!(addr >= map->addr && addr < map->addr_end)) { + continue; + } + if (mode == RZ_OUTPUT_MODE_STANDARD) { + rz_cons_printf("0x%08" PFMT64x " 0x%08" PFMT64x " %s\n", map->addr, map->addr_end, map->file); + } else if (mode == RZ_OUTPUT_MODE_RIZIN) { + /* Escape backslashes (e.g. for Windows). */ + char *escaped_path = rz_str_escape(map->file); + char *filtered_name = strdup(map->name); + rz_name_filter(filtered_name, 0, true); + rz_cons_printf("f mod.%s = 0x%08" PFMT64x "\n", + filtered_name, map->addr); + rz_cons_printf("oba 0x%08" PFMT64x " %s\n", map->addr, escaped_path); + free(escaped_path); + free(filtered_name); } - pj_a(pj); } - // TODO: honor mode + rz_list_free(list); +} + +static void cmd_debug_modules(RzCore *core, RzCmdStateOutput *state) { // "dmm" + RzDebugMap *map; + RzList *list; + RzListIter *iter; + PJ *pj = state->d.pj; + RzOutputMode mode = state->mode; + rz_cmd_state_output_array_start(state); list = rz_debug_modules_list(core->dbg); rz_list_foreach (list, iter, map) { - switch (mode) { - case 0: + if (mode == RZ_OUTPUT_MODE_STANDARD) { rz_cons_printf("0x%08" PFMT64x " 0x%08" PFMT64x " %s\n", map->addr, map->addr_end, map->file); - break; - case '.': - if (addr >= map->addr && addr < map->addr_end) { - rz_cons_printf("0x%08" PFMT64x " 0x%08" PFMT64x " %s\n", map->addr, map->addr_end, map->file); - goto beach; - } - break; - case 'j': { + } else if (mode == RZ_OUTPUT_MODE_JSON) { /* Escape backslashes (e.g. for Windows). */ pj_o(pj); pj_kn(pj, "addr", map->addr); @@ -1298,43 +1257,22 @@ static void cmd_debug_modules(RzCore *core, int mode) { // "dmm" pj_ks(pj, "file", map->file); pj_ks(pj, "name", map->name); pj_end(pj); - } break; - case ':': - case '*': - if (mode == '*' || (mode == ':' && addr >= map->addr && addr < map->addr_end)) { - /* Escape backslashes (e.g. for Windows). */ - char *escaped_path = rz_str_escape(map->file); - char *filtered_name = strdup(map->name); - rz_name_filter(filtered_name, 0, true); - rz_cons_printf("f mod.%s = 0x%08" PFMT64x "\n", - filtered_name, map->addr); - rz_cons_printf("oba 0x%08" PFMT64x " %s\n", map->addr, escaped_path); - // rz_cons_printf (".!rz-bin -rsB 0x%08"PFMT64x" \"%s\"\n", map->addr, escaped_path); - free(escaped_path); - free(filtered_name); - } - break; - default: - pj_free(pj); - rz_list_free(list); - goto show_help; - /* not reached */ + } else if (mode == RZ_OUTPUT_MODE_RIZIN) { + /* Escape backslashes (e.g. for Windows). */ + char *escaped_path = rz_str_escape(map->file); + char *filtered_name = strdup(map->name); + rz_name_filter(filtered_name, 0, true); + rz_cons_printf("f mod.%s = 0x%08" PFMT64x "\n", + filtered_name, map->addr); + rz_cons_printf("oba 0x%08" PFMT64x " %s\n", map->addr, escaped_path); + free(escaped_path); + free(filtered_name); } } -beach: - if (mode == 'j') { - pj_end(pj); - rz_cons_println(pj_string(pj)); - } - pj_free(pj); + rz_cmd_state_output_array_end(state); rz_list_free(list); } -#if __linux__ && __GNU_LIBRARY__ && __GLIBC__ && __GLIBC_MINOR__ - -static int cmd_dbg_map_heap_glibc_32(RzCore *core, const char *input); -static int cmd_dbg_map_heap_glibc_64(RzCore *core, const char *input); -#endif // __linux__ && __GNU_LIBRARY__ && __GLIBC__ && __GLIBC_MINOR__ #if __WINDOWS__ static int cmd_debug_map_heap_win(RzCore *core, const char *input); #endif // __WINDOWS__ @@ -1380,37 +1318,6 @@ static RzDebugMap *get_closest_map(RzCore *core, ut64 addr) { return NULL; } -static int rz_debug_heap(RzCore *core, const char *input) { - const char *m = rz_config_get(core->config, "dbg.malloc"); - if (m && !strcmp("glibc", m)) { -#if __linux__ && __GNU_LIBRARY__ && __GLIBC__ && __GLIBC_MINOR__ - if (core->rasm->bits == 64) { - cmd_dbg_map_heap_glibc_64(core, input + 1); - } else { - cmd_dbg_map_heap_glibc_32(core, input + 1); - } -#else - eprintf("glibc not supported for this platform\n"); -#endif -#if HAVE_JEMALLOC - } else if (m && !strcmp("jemalloc", m)) { - if (core->rasm->bits == 64) { - cmd_dbg_map_jemalloc_64(core, input + 1); - } else { - cmd_dbg_map_jemalloc_32(core, input + 1); - } -#endif - } else { -#if __WINDOWS__ - cmd_debug_map_heap_win(core, input + 1); -#else - eprintf("MALLOC algorithm not supported\n"); - return false; -#endif - } - return true; -} - static bool get_bin_info(RzCore *core, const char *file, ut64 baseaddr, PJ *pj, int mode, bool symbols_only, RzCoreBinFilter *filter) { int fd; if ((fd = rz_io_fd_open(core->io, file, RZ_PERM_R, 0)) == -1) { @@ -1443,376 +1350,411 @@ static bool get_bin_info(RzCore *core, const char *file, ut64 baseaddr, PJ *pj, return true; } -static int cmd_debug_map(RzCore *core, const char *input) { +// dm +RZ_IPI RzCmdStatus rz_cmd_debug_list_maps_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) { + CMD_CHECK_DEBUG_DEAD(core); + rz_debug_map_sync(core->dbg); // update process memory maps + rz_debug_map_print(core->dbg, core->offset, state); + return RZ_CMD_STATUS_OK; +} + +// dma +RZ_IPI RzCmdStatus rz_cmd_debug_allocate_maps_handler(RzCore *core, int argc, const char **argv) { + CMD_CHECK_DEBUG_DEAD(core); + ut64 addr = core->offset; + int size = (int)rz_num_math(core->num, argv[1]); + rz_debug_map_alloc(core->dbg, addr, size, false); + return RZ_CMD_STATUS_OK; +} + +// dmm +RZ_IPI RzCmdStatus rz_cmd_debug_modules_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) { + CMD_CHECK_DEBUG_DEAD(core); + cmd_debug_modules(core, state); + return RZ_CMD_STATUS_OK; +} + +// dmm. +RZ_IPI RzCmdStatus rz_cmd_debug_current_modules_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) { + CMD_CHECK_DEBUG_DEAD(core); + cmd_debug_current_modules(core, mode); + return RZ_CMD_STATUS_OK; +} + +// dm- +RZ_IPI RzCmdStatus rz_cmd_debug_deallocate_map_handler(RzCore *core, int argc, const char **argv) { + CMD_CHECK_DEBUG_DEAD(core); RzListIter *iter; RzDebugMap *map; ut64 addr = core->offset; + rz_list_foreach (core->dbg->maps, iter, map) { + if (addr >= map->addr && addr < map->addr_end) { + rz_debug_map_dealloc(core->dbg, map); + rz_debug_map_sync(core->dbg); + return RZ_CMD_STATUS_OK; + } + } + eprintf("The address doesn't match with any map.\n"); + return RZ_CMD_STATUS_ERROR; +} + +// dm= +RZ_IPI RzCmdStatus rz_cmd_debug_list_maps_ascii_handler(RzCore *core, int argc, const char **argv) { + CMD_CHECK_DEBUG_DEAD(core); + rz_debug_map_sync(core->dbg); + rz_debug_map_list_visual(core->dbg, core->offset, argv[0] + 2, + rz_config_get_i(core->config, "scr.color")); + return RZ_CMD_STATUS_OK; +} + +// dm. +RZ_IPI RzCmdStatus rz_cmd_debug_map_current_handler(RzCore *core, int argc, const char **argv) { + CMD_CHECK_DEBUG_DEAD(core); + ut64 addr = core->offset; + // RZ_OUTPUT_MODE_LONG is workaround for '.' + RzCmdStateOutput state = { 0 }; + state.mode = RZ_OUTPUT_MODE_LONG; + rz_debug_map_print(core->dbg, addr, &state); + return RZ_CMD_STATUS_OK; +} + +// dmd +RZ_IPI RzCmdStatus rz_cmd_debug_dump_maps_handler(RzCore *core, int argc, const char **argv) { + CMD_CHECK_DEBUG_DEAD(core); + if (argc == 2) { + dump_maps(core, -1, argv[1]); + } else if (argc == 1) { + dump_maps(core, -1, NULL); + } + return RZ_CMD_STATUS_OK; +} + +// dmda +RZ_IPI RzCmdStatus rz_cmd_debug_dump_maps_all_handler(RzCore *core, int argc, const char **argv) { + CMD_CHECK_DEBUG_DEAD(core); + dump_maps(core, 0, NULL); + return RZ_CMD_STATUS_OK; +} + +// dmdw +RZ_IPI RzCmdStatus rz_cmd_debug_dump_maps_writable_handler(RzCore *core, int argc, const char **argv) { + CMD_CHECK_DEBUG_DEAD(core); + dump_maps(core, RZ_PERM_RW, NULL); + return RZ_CMD_STATUS_OK; +} +// dmi +RZ_IPI int rz_cmd_debug_dmi(void *data, const char *input) { + RzCore *core = (RzCore *)data; + CMD_CHECK_DEBUG_DEAD(core); + RzListIter *iter; + RzDebugMap *map; + ut64 addr = core->offset; switch (input[0]) { - case '.': // "dm." - rz_debug_map_list(core->dbg, addr, input); - break; - case 'm': // "dmm" - if (!strcmp(input + 1, ".*")) { - cmd_debug_modules(core, ':'); - } else - cmd_debug_modules(core, input[1]); - break; - case '?': // "dm?" - rz_core_cmd_help(core, help_msg_dm); + case '\0': // "dmi" alias of "dmm" + { + RzCmdStateOutput state = { 0 }; + state.mode = RZ_OUTPUT_MODE_STANDARD; + cmd_debug_modules(core, &state); break; - case 'p': // "dmp" - if (input[1] == '?') { - rz_core_cmd_help(core, help_msg_dmp); - } else if (input[1] == ' ') { - int perms; - char *p, *q; - ut64 size = 0, addr; - p = strchr(input + 2, ' '); - if (p) { - *p++ = 0; - q = strchr(p, ' '); - if (q) { - *q++ = 0; - addr = rz_num_math(core->num, input + 2); - size = rz_num_math(core->num, p); - perms = rz_str_rwx(q); - // eprintf ("(%s)(%s)(%s)\n", input + 2, p, q); - // eprintf ("0x%08"PFMT64x" %d %o\n", addr, (int) size, perms); - rz_debug_map_protect(core->dbg, addr, size, perms); - } else - eprintf("See dmp?\n"); - } else { - rz_debug_map_sync(core->dbg); // update process memory maps - addr = UT64_MAX; - rz_list_foreach (core->dbg->maps, iter, map) { - if (core->offset >= map->addr && core->offset < map->addr_end) { - addr = map->addr; - size = map->size; - break; - } - } - perms = rz_str_rwx(input + 2); - if (addr != UT64_MAX && perms >= 0) { - rz_debug_map_protect(core->dbg, addr, size, perms); - } else { - eprintf("See dmp?\n"); - } + } + case ' ': // "dmi " + case '*': // "dmi*" + case 'v': // "dmiv" + case 'j': // "dmij" + case 'q': // "dmiq" + case 'a': // "dmia" + { + const char *libname = NULL, *symname = NULL, *a0; + int mode; + ut64 baddr = 0LL; + char *ptr; + int i = 1; + bool symbols_only = true; + if (input[0] == 'a') { + symbols_only = false; + input++; + } + PJ *pj = NULL; + switch (input[0]) { + case 's': + mode = RZ_MODE_SET; + break; + case '*': + mode = RZ_MODE_RIZINCMD; + break; + case 'j': + mode = RZ_MODE_JSON; + pj = pj_new(); + if (!pj) { + return false; } - } else { - eprintf("See dmp?\n"); - } - break; - case 'd': // "dmd" - switch (input[1]) { - case 'a': return dump_maps(core, 0, NULL); - case 'w': return dump_maps(core, RZ_PERM_RW, NULL); - case ' ': return dump_maps(core, -1, input + 2); - case 0: return dump_maps(core, -1, NULL); - case '?': + break; + case 'q': + mode = input[1] == 'q' ? input++, RZ_MODE_SIMPLEST : RZ_MODE_SIMPLE; + break; default: - eprintf("Usage: dmd[aw] - dump (all-or-writable) debug maps\n"); + mode = RZ_MODE_PRINT; break; } - break; - case 'l': // "dml" - if (input[1] != ' ') { - eprintf("Usage: dml [file]\n"); - return false; - } - rz_debug_map_sync(core->dbg); // update process memory maps - rz_list_foreach (core->dbg->maps, iter, map) { - if (addr >= map->addr && addr < map->addr_end) { - size_t sz; - char *buf = rz_file_slurp(input + 2, &sz); - //TODO: use mmap here. we need a portable implementation - if (!buf) { - eprintf("Cannot allocate 0x%08" PFMT64x " byte(s)\n", map->size); - return false; - } - rz_io_write_at(core->io, map->addr, (const ut8 *)buf, sz); - if (sz != map->size) - eprintf("File size differs from region size (%" PFMT64u " vs %" PFMT64d ")\n", - (ut64)sz, map->size); - eprintf("Loaded %" PFMT64u " byte(s) into the map region at 0x%08" PFMT64x "\n", - (ut64)sz, map->addr); - free(buf); - return true; - } - } - eprintf("No debug region found here\n"); - return false; - case 'i': // "dmi" - switch (input[1]) { - case '\0': // "dmi" alias of "dmm" + ptr = strdup(rz_str_trim_head_ro(input + 1)); + if (!ptr || !*ptr) { rz_core_cmd(core, "dmm", 0); - break; - case ' ': // "dmi " - case '*': // "dmi*" - case 'v': // "dmiv" - case 'j': // "dmij" - case 'q': // "dmiq" - case 'a': // "dmia" - { - const char *libname = NULL, *symname = NULL, *a0; - int mode; - ut64 baddr = 0LL; - char *ptr; - int i = 1; - bool symbols_only = true; - if (input[1] == 'a') { - symbols_only = false; - input++; - } - PJ *pj = NULL; - switch (input[1]) { - case 's': - mode = RZ_MODE_SET; - break; - case '*': - mode = RZ_MODE_RIZINCMD; - break; - case 'j': - mode = RZ_MODE_JSON; - pj = pj_new(); - if (!pj) { - return false; - } - break; - case 'q': - mode = input[2] == 'q' ? input++, RZ_MODE_SIMPLEST : RZ_MODE_SIMPLE; - break; - default: - mode = RZ_MODE_PRINT; - break; - } - ptr = strdup(rz_str_trim_head_ro(input + 2)); - if (!ptr || !*ptr) { - rz_core_cmd(core, "dmm", 0); - free(ptr); - break; - } - if (symbols_only) { - i = rz_str_word_set0(ptr); - } - switch (i) { - case 2: - symname = rz_str_word_get0(ptr, 1); - // fall through - case 1: - a0 = rz_str_word_get0(ptr, 0); - addr = rz_num_get(core->num, a0); - if (!addr || addr == UT64_MAX) { - libname = rz_str_word_get0(ptr, 0); - } - break; - } - if (libname && !addr) { - addr = addroflib(core, rz_file_basename(libname)); - if (addr == UT64_MAX) { - eprintf("Unknown library, or not found in dm\n"); - } - } - map = get_closest_map(core, addr); - if (map) { - RzCoreBinFilter filter; - filter.offset = 0LL; - filter.name = (char *)symname; - baddr = map->addr; - - if (libname) { - const char *file = map->file ? map->file : map->name; - char *newfile = NULL; - if (!rz_file_exists(file)) { - newfile = rz_file_temp("memlib"); - if (newfile) { - file = newfile; - rz_core_cmdf(core, "wtf %s 0x%" PFMT64x " @ 0x%" PFMT64x " 2> %s", - file, map->size, baddr, RZ_SYS_DEVNULL); - } - } - get_bin_info(core, file, baddr, pj, mode, symbols_only, &filter); - if (newfile) { - if (!rz_file_rm(newfile)) { - eprintf("Error when removing %s\n", newfile); - } - free(newfile); - } - } else { - rz_bin_set_baddr(core->bin, map->addr); - rz_core_bin_info(core, RZ_CORE_BIN_ACC_SYMBOLS, pj, (input[1] == '*'), true, &filter, NULL); - rz_bin_set_baddr(core->bin, baddr); - } - } - if (mode == RZ_MODE_JSON) { - rz_cons_println(pj_string(pj)); - pj_free(pj); - } free(ptr); - } break; - case '.': // "dmi." - { - map = get_closest_map(core, addr); - if (map) { - ut64 closest_addr = UT64_MAX; - RzList *symbols = rz_bin_get_symbols(core->bin); - RzBinSymbol *symbol, *closest_symbol = NULL; - - rz_list_foreach (symbols, iter, symbol) { - if (symbol->vaddr > addr) { - if (symbol->vaddr - addr < closest_addr) { - closest_addr = symbol->vaddr - addr; - closest_symbol = symbol; - } - } else { - if (addr - symbol->vaddr < closest_addr) { - closest_addr = addr - symbol->vaddr; - closest_symbol = symbol; - } - } - } - if (closest_symbol) { - RzCoreBinFilter filter; - filter.offset = 0LL; - filter.name = (char *)closest_symbol->name; - - rz_bin_set_baddr(core->bin, map->addr); - rz_core_bin_info(core, RZ_CORE_BIN_ACC_SYMBOLS, NULL, false, true, &filter, NULL); - } - } - } break; - default: - rz_core_cmd_help(core, help_msg_dmi); break; } - break; - case 'S': // "dmS" - { // Move to a separate function - const char *libname = NULL, *sectname = NULL, *mode = ""; - ut64 baddr = 0LL; - char *ptr; - int i; - - if (input[1] == '*') { - ptr = strdup(rz_str_trim_head_ro((char *)input + 2)); - mode = "-r "; - } else { - ptr = strdup(rz_str_trim_head_ro((char *)input + 1)); + if (symbols_only) { + i = rz_str_word_set0(ptr); } - i = rz_str_word_set0(ptr); - - addr = UT64_MAX; switch (i) { - case 2: // get section name - sectname = rz_str_word_get0(ptr, 1); - /* fallthrou */ - case 1: // get addr|libname - if (IS_DIGIT(*ptr)) { - const char *a0 = rz_str_word_get0(ptr, 0); - addr = rz_num_math(core->num, a0); - } else { - addr = UT64_MAX; - } + case 2: + symname = rz_str_word_get0(ptr, 1); + // fall through + case 1: + a0 = rz_str_word_get0(ptr, 0); + addr = rz_num_get(core->num, a0); if (!addr || addr == UT64_MAX) { libname = rz_str_word_get0(ptr, 0); } break; } - rz_debug_map_sync(core->dbg); // update process memory maps - RzList *list = rz_debug_modules_list(core->dbg); - rz_list_foreach (list, iter, map) { - if ((!libname || - (addr != UT64_MAX && (addr >= map->addr && addr < map->addr_end)) || - (libname != NULL && (strstr(map->name, libname))))) { - baddr = map->addr; - char *res; + if (libname && !addr) { + addr = addroflib(core, rz_file_basename(libname)); + if (addr == UT64_MAX) { + eprintf("Unknown library, or not found in dm\n"); + } + } + map = get_closest_map(core, addr); + if (map) { + RzCoreBinFilter filter; + filter.offset = 0LL; + filter.name = (char *)symname; + baddr = map->addr; + + if (libname) { const char *file = map->file ? map->file : map->name; - char *name = rz_str_escape((char *)rz_file_basename(file)); - char *filesc = rz_str_escape(file); - /* TODO: do not spawn. use RzBin API */ - if (sectname) { - char *sect = rz_str_escape(sectname); - res = rz_sys_cmd_strf("env RZ_BIN_PREFIX=\"%s\" rz-bin %s-B 0x%08" PFMT64x " -S \"%s\" | grep \"%s\"", name, mode, baddr, filesc, sect); - free(sect); - } else { - res = rz_sys_cmd_strf("env RZ_BIN_PREFIX=\"%s\" rz-bin %s-B 0x%08" PFMT64x " -S \"%s\"", name, mode, baddr, filesc); + char *newfile = NULL; + if (!rz_file_exists(file)) { + newfile = rz_file_temp("memlib"); + if (newfile) { + file = newfile; + rz_core_dump(core, file, baddr, map->size, false); + } } - free(filesc); - rz_cons_println(res); - free(name); - free(res); - if (libname || addr != UT64_MAX) { //only single match requested - break; + get_bin_info(core, file, baddr, pj, mode, symbols_only, &filter); + if (newfile) { + if (!rz_file_rm(newfile)) { + eprintf("Error when removing %s\n", newfile); + } + free(newfile); } + } else { + rz_bin_set_baddr(core->bin, map->addr); + rz_core_bin_info(core, RZ_CORE_BIN_ACC_SYMBOLS, pj, (input[0] == '*'), true, &filter, NULL); + rz_bin_set_baddr(core->bin, baddr); } } + if (mode == RZ_MODE_JSON) { + rz_cons_println(pj_string(pj)); + pj_free(pj); + } free(ptr); } break; - case ' ': // "dm " + case '.': // "dmi." { - int size; - char *p = strchr(input + 2, ' '); - if (p) { - *p++ = 0; - addr = rz_num_math(core->num, input + 1); - size = rz_num_math(core->num, p); - rz_debug_map_alloc(core->dbg, addr, size, false); - } else { - eprintf("Usage: dm addr size\n"); - return false; + map = get_closest_map(core, addr); + if (map) { + ut64 closest_addr = UT64_MAX; + RzList *symbols = rz_bin_get_symbols(core->bin); + RzBinSymbol *symbol, *closest_symbol = NULL; + + rz_list_foreach (symbols, iter, symbol) { + if (symbol->vaddr > addr) { + if (symbol->vaddr - addr < closest_addr) { + closest_addr = symbol->vaddr - addr; + closest_symbol = symbol; + } + } else { + if (addr - symbol->vaddr < closest_addr) { + closest_addr = addr - symbol->vaddr; + closest_symbol = symbol; + } + } + } + if (closest_symbol) { + RzCoreBinFilter filter; + filter.offset = 0LL; + filter.name = (char *)closest_symbol->name; + + rz_bin_set_baddr(core->bin, map->addr); + rz_core_bin_info(core, RZ_CORE_BIN_ACC_SYMBOLS, NULL, false, true, &filter, NULL); + } } } break; - case '-': // "dm-" - if (input[1] != ' ') { - eprintf("|ERROR| Usage: dm- [addr]\n"); - break; - } - addr = rz_num_math(core->num, input + 2); + default: + rz_core_cmd_help(core, help_msg_dmi); + break; + } + return RZ_CMD_STATUS_OK; +} + +// dmp +RZ_IPI RzCmdStatus rz_debug_memory_permission_handler(RzCore *core, int argc, const char **argv) { + CMD_CHECK_DEBUG_DEAD(core); + RzListIter *iter; + RzDebugMap *map; + ut64 addr = 0, size = 0; + int perms; + if (argc == 3) { // dmp @ + addr = core->offset; + size = rz_num_math(core->num, argv[1]); + perms = rz_str_rwx(argv[2]); + rz_debug_map_protect(core->dbg, addr, (int)size, perms); + } else if (argc == 2) { // dmp + addr = UT64_MAX; rz_list_foreach (core->dbg->maps, iter, map) { - if (addr >= map->addr && addr < map->addr_end) { - rz_debug_map_dealloc(core->dbg, map); - rz_debug_map_sync(core->dbg); - return true; + if (core->offset >= map->addr && core->offset < map->addr_end) { + addr = map->addr; + size = map->size; + break; } } - eprintf("The address doesn't match with any map.\n"); - break; - case 'L': // "dmL" - { - int size; - char *p = strchr(input + 2, ' '); - if (p) { - *p++ = 0; - addr = rz_num_math(core->num, input + 1); - size = rz_num_math(core->num, p); - rz_debug_map_alloc(core->dbg, addr, size, true); + perms = rz_str_rwx(argv[1]); + if (addr != UT64_MAX && perms >= 0) { + rz_debug_map_protect(core->dbg, addr, (int)size, perms); } else { - eprintf("Usage: dmL addr size\n"); - return false; + return RZ_CMD_STATUS_ERROR; } - } break; - case '\0': // "dm" - case '*': // "dm*" - case 'j': // "dmj" - case 'q': // "dmq" - rz_debug_map_sync(core->dbg); // update process memory maps - rz_debug_map_list(core->dbg, core->offset, input); - break; - case '=': // "dm=" - rz_debug_map_sync(core->dbg); - rz_debug_map_list_visual(core->dbg, core->offset, input, - rz_config_get_i(core->config, "scr.color")); - break; - case 'h': // "dmh" - (void)rz_debug_heap(core, input); - break; + } else { + return RZ_CMD_STATUS_WRONG_ARGS; } - return true; + return RZ_CMD_STATUS_OK; +} + +RZ_IPI RzCmdStatus rz_cmd_debug_dmS_handler(RzCore *core, int argc, const char **argv, RzOutputMode m) { + CMD_CHECK_DEBUG_DEAD(core); + RzListIter *iter; + RzDebugMap *map; + ut64 addr; + const char *libname = NULL, *sectname = NULL, *mode = ""; + ut64 baddr = 0LL; + if (m == RZ_OUTPUT_MODE_RIZIN) { + mode = "-r "; + } + addr = UT64_MAX; + if (argc == 3) { + sectname = argv[2]; + } + if (argc >= 2) { + if (IS_DIGIT(*argv[1])) { + const char *a0 = argv[1]; + addr = rz_num_math(core->num, a0); + } else { + addr = UT64_MAX; + } + if (!addr || addr == UT64_MAX) { + libname = argv[1]; + } + } + rz_debug_map_sync(core->dbg); // update process memory maps + RzList *list = rz_debug_modules_list(core->dbg); + rz_list_foreach (list, iter, map) { + if ((!libname || + (addr != UT64_MAX && (addr >= map->addr && addr < map->addr_end)) || + (libname != NULL && (strstr(map->name, libname))))) { + baddr = map->addr; + char *res; + const char *file = map->file ? map->file : map->name; + char *name = rz_str_escape((char *)rz_file_basename(file)); + char *filesc = rz_str_escape(file); + /* TODO: do not spawn. use RzBin API */ + if (sectname) { + char *sect = rz_str_escape(sectname); + res = rz_sys_cmd_strf("env RZ_BIN_PREFIX=\"%s\" rz-bin %s-B 0x%08" PFMT64x " -S \"%s\" | grep \"%s\"", name, mode, baddr, filesc, sect); + free(sect); + } else { + res = rz_sys_cmd_strf("env RZ_BIN_PREFIX=\"%s\" rz-bin %s-B 0x%08" PFMT64x " -S \"%s\"", name, mode, baddr, filesc); + } + free(filesc); + rz_cons_println(res); + free(name); + free(res); + if (libname || addr != UT64_MAX) { //only single match requested + break; + } + } + } + return RZ_CMD_STATUS_OK; +} +// dml +RZ_IPI RzCmdStatus rz_cmd_debug_dml_handler(RzCore *core, int argc, const char **argv) { + CMD_CHECK_DEBUG_DEAD(core); + RzListIter *iter; + RzDebugMap *map; + ut64 addr = core->offset; + rz_debug_map_sync(core->dbg); // update process memory maps + rz_list_foreach (core->dbg->maps, iter, map) { + if (addr >= map->addr && addr < map->addr_end) { + size_t sz; + char *buf = rz_file_slurp(argv[1], &sz); + //TODO: use mmap here. we need a portable implementation + if (!buf) { + eprintf("Cannot allocate 0x%08" PFMT64x " byte(s)\n", map->size); + return RZ_CMD_STATUS_ERROR; + } + rz_io_write_at(core->io, map->addr, (const ut8 *)buf, sz); + if (sz != map->size) + eprintf("File size differs from region size (%" PFMT64u " vs %" PFMT64d ")\n", + (ut64)sz, map->size); + eprintf("Loaded %" PFMT64u " byte(s) into the map region at 0x%08" PFMT64x "\n", + (ut64)sz, map->addr); + free(buf); + return RZ_CMD_STATUS_OK; + } + } + eprintf("No debug region found here\n"); + return RZ_CMD_STATUS_ERROR; +} + +// dmL +RZ_IPI RzCmdStatus rz_cmd_debug_dmL_handler(RzCore *core, int argc, const char **argv) { + CMD_CHECK_DEBUG_DEAD(core); + int size; + ut64 addr; + addr = core->offset; + size = (int)rz_num_math(core->num, argv[1]); + rz_debug_map_alloc(core->dbg, addr, size, true); + return RZ_CMD_STATUS_OK; +} + +// dmw +RZ_IPI int rz_cmd_debug_heap_windows(void *data, const char *input) { + RzCore *core = (RzCore *)data; + CMD_CHECK_DEBUG_DEAD(core); +#if __WINDOWS__ + cmd_debug_map_heap_win(core, input); + return RZ_CMD_STATUS_OK; +#else + eprintf("MALLOC algorithm not supported\n"); + return RZ_CMD_STATUS_ERROR; +#endif +} + +// dmx +RZ_IPI int rz_cmd_debug_heap_jemalloc(void *data, const char *input) { + RzCore *core = (RzCore *)data; + CMD_CHECK_DEBUG_DEAD(core); +#if HAVE_JEMALLOC + if (core->rasm->bits == 64) { + return cmd_dbg_map_jemalloc_64(core, input); + } else { + return cmd_dbg_map_jemalloc_32(core, input); + } +#endif } -#if __linux__ && __GNU_LIBRARY__ && __GLIBC__ && __GLIBC_MINOR__ #include "linux_heap_glibc.c" -#elif __WINDOWS__ +#if __WINDOWS__ #include "windows_heap.c" #endif @@ -4871,9 +4813,6 @@ RZ_IPI int rz_cmd_debug(void *data, const char *input) { case 'c': // "dc" (void)rz_debug_continue_oldhandler(core, input + 1); break; - case 'm': // "dm" - cmd_debug_map(core, input + 1); - break; case 'r': // "dr" if (core->bin->is_debugger || input[1] == '?') { cmd_debug_reg(core, input + 1); @@ -5284,4 +5223,4 @@ RZ_IPI int rz_cmd_debug(void *data, const char *input) { dbg_follow_seek_register(core); } return 0; -} +} \ No newline at end of file diff --git a/librz/core/cmd_descs/cmd_debug.yaml b/librz/core/cmd_descs/cmd_debug.yaml index d64220ff6af..79ef01579a2 100644 --- a/librz/core/cmd_descs/cmd_debug.yaml +++ b/librz/core/cmd_descs/cmd_debug.yaml @@ -19,63 +19,187 @@ commands: cname: cmd_debug_step_until summary: Step until args: - - name: addr - type: RZ_CMD_ARG_TYPE_RZNUM + - name: addr + type: RZ_CMD_ARG_TYPE_RZNUM - name: dsui cname: cmd_debug_step_until_instr summary: Step until an instruction that matches args: - - name: instr - type: RZ_CMD_ARG_TYPE_STRING + - name: instr + type: RZ_CMD_ARG_TYPE_STRING - name: dsuir cname: cmd_debug_step_until_instr_regex summary: Step until an instruction that matches args: - - name: regex - type: RZ_CMD_ARG_TYPE_STRING + - name: regex + type: RZ_CMD_ARG_TYPE_STRING - name: dsuo cname: cmd_debug_step_until_optype summary: Step until an instruction matches one of the s args: - - name: optype - type: RZ_CMD_ARG_TYPE_STRING - flags: RZ_CMD_ARG_FLAG_ARRAY + - name: optype + type: RZ_CMD_ARG_TYPE_STRING + flags: RZ_CMD_ARG_FLAG_ARRAY - name: dsue cname: cmd_debug_step_until_esil summary: Step until expression matches args: - - name: esil - type: RZ_CMD_ARG_TYPE_STRING + - name: esil + type: RZ_CMD_ARG_TYPE_STRING - name: dsuf cname: cmd_debug_step_until_flag summary: Step until pc == matching name args: - - name: flag - type: RZ_CMD_ARG_TYPE_FLAG + - name: flag + type: RZ_CMD_ARG_TYPE_FLAG - name: dts summary: Debug trace session commands subcommands: - - name: dts+ - summary: Start trace session - cname: cmd_debug_start_trace_session - args: [] - - name: dts- - summary: Stop trace session - cname: cmd_debug_stop_trace_session - args: [] - - name: dtst - cname: cmd_debug_save_trace_session - summary: Save trace sessions to disk - args: - - name: dir - type: RZ_CMD_ARG_TYPE_FILE - - name: dtsf - cname: cmd_debug_load_trace_session - summary: Load trace sessions to disk - args: - - name: dir - type: RZ_CMD_ARG_TYPE_FILE - - name: dtsm - cname: cmd_debug_list_trace_session_mmap - summary: List current memory map and hash - args: [] \ No newline at end of file + - name: dts+ + summary: Start trace session + cname: cmd_debug_start_trace_session + args: [] + - name: dts- + summary: Stop trace session + cname: cmd_debug_stop_trace_session + args: [] + - name: dtst + cname: cmd_debug_save_trace_session + summary: Save trace sessions to disk + args: + - name: dir + type: RZ_CMD_ARG_TYPE_FILE + - name: dtsf + cname: cmd_debug_load_trace_session + summary: Load trace sessions to disk + args: + - name: dir + type: RZ_CMD_ARG_TYPE_FILE + - name: dtsm + cname: cmd_debug_list_trace_session_mmap + summary: List current memory map and hash + args: [] + - name: dm + summary: Memory map commands + subcommands: + - name: dm + cname: cmd_debug_list_maps + type: RZ_CMD_DESC_TYPE_ARGV_STATE + summary: > + List memory maps + modes: + - RZ_OUTPUT_MODE_STANDARD + - RZ_OUTPUT_MODE_RIZIN + - RZ_OUTPUT_MODE_JSON + - RZ_OUTPUT_MODE_QUIET + args: [] + - name: dm+ + cname: cmd_debug_allocate_maps + summary: > + Allocate bytes at current offset + args: + - name: size + type: RZ_CMD_ARG_TYPE_RZNUM + - name: dm= + cname: cmd_debug_list_maps_ascii + summary: List memory maps of current process with ASCII art bars + args: [] + - name: dm. + cname: cmd_debug_map_current + summary: Show map name of current address + args: [] + - name: dmm + summary: Module memory map commands + subcommands: + - name: dmm + summary: List modules (libraries, binaries loaded in memory) + cname: cmd_debug_modules + type: RZ_CMD_DESC_TYPE_ARGV_STATE + modes: + - RZ_OUTPUT_MODE_STANDARD + - RZ_OUTPUT_MODE_RIZIN + - RZ_OUTPUT_MODE_JSON + args: [] + - name: dmm. + cname: cmd_debug_current_modules + modes: + - RZ_OUTPUT_MODE_STANDARD + - RZ_OUTPUT_MODE_RIZIN + summary: List memory map of current module + args: [] + - name: dm- + summary: Deallocate memory map at current offset + cname: cmd_debug_deallocate_map + args: [] + - name: dmd + summary: Dump debug map regions to a file (from-to.dmp) + subcommands: + - name: dmd + summary: Dump debug maps to + cname: cmd_debug_dump_maps + args: + - name: filename + type: RZ_CMD_ARG_TYPE_FILE + optional: true + - name: dmda + cname: cmd_debug_dump_maps_all + summary: Dump all debug maps + args: [] + - name: dmdw + cname: cmd_debug_dump_maps_writable + summary: Dump writable debug maps + args: [] + - name: dmh + summary: Glibc heap commands + subcommands: cmd_heap_glibc + - name: dmi + summary: List/Load symbols + cname: cmd_debug_dmi + type: RZ_CMD_DESC_TYPE_OLDINPUT + - name: dml + summary: Load contents of file into current map region + cname: cmd_debug_dml + args: + - name: file + type: RZ_CMD_ARG_TYPE_FILE + - name: dmp + summary: > + Change page at current offset with , protection + / Change dbg.map permissions to + cname: debug_memory_permission + args: + - name: perms + type: RZ_CMD_ARG_TYPE_STRING + - name: size + type: RZ_CMD_ARG_TYPE_RZNUM + optional: true + - name: dmL + summary: > + Allocate bytes at current offset and promote to huge page + cname: cmd_debug_dmL + args: + - name: size + type: RZ_CMD_ARG_TYPE_RZNUM + - name: dmS + summary: List sections of target lib + cname: cmd_debug_dmS + modes: + - RZ_OUTPUT_MODE_STANDARD + - RZ_OUTPUT_MODE_RIZIN + args: + - name: addr|libname + type: RZ_CMD_ARG_TYPE_STRING + optional: true + - name: sectname + type: RZ_CMD_ARG_TYPE_STRING + optional: true + - name: dmw + summary: Windows heap commands + cname: cmd_debug_heap_windows + type: RZ_CMD_DESC_TYPE_OLDINPUT + args: [] + - name: dmx + summary: Jemalloc heap commands + cname: cmd_debug_heap_jemalloc + type: RZ_CMD_DESC_TYPE_OLDINPUT + args: [] diff --git a/librz/core/cmd_descs/cmd_descs.c b/librz/core/cmd_descs/cmd_descs.c index 15ece8a80eb..1f463c2e270 100644 --- a/librz/core/cmd_descs/cmd_descs.c +++ b/librz/core/cmd_descs/cmd_descs.c @@ -93,6 +93,19 @@ static const RzCmdDescArg cmd_debug_step_until_esil_args[2]; static const RzCmdDescArg cmd_debug_step_until_flag_args[2]; static const RzCmdDescArg cmd_debug_save_trace_session_args[2]; static const RzCmdDescArg cmd_debug_load_trace_session_args[2]; +static const RzCmdDescArg cmd_debug_allocate_maps_args[2]; +static const RzCmdDescArg cmd_debug_dump_maps_args[2]; +static const RzCmdDescArg cmd_heap_chunks_print_args[2]; +static const RzCmdDescArg cmd_heap_bins_list_print_args[2]; +static const RzCmdDescArg cmd_heap_arena_bins_print_args[2]; +static const RzCmdDescArg cmd_heap_fastbins_print_args[2]; +static const RzCmdDescArg cmd_heap_chunks_graph_args[2]; +static const RzCmdDescArg cmd_heap_info_print_args[2]; +static const RzCmdDescArg cmd_main_arena_print_args[2]; +static const RzCmdDescArg cmd_debug_dml_args[2]; +static const RzCmdDescArg debug_memory_permission_args[3]; +static const RzCmdDescArg cmd_debug_dmL_args[2]; +static const RzCmdDescArg cmd_debug_dmS_args[3]; static const RzCmdDescArg eval_getset_args[2]; static const RzCmdDescArg eval_list_args[2]; static const RzCmdDescArg eval_bool_invert_args[2]; @@ -1584,6 +1597,328 @@ static const RzCmdDescHelp cmd_debug_list_trace_session_mmap_help = { .args = cmd_debug_list_trace_session_mmap_args, }; +static const RzCmdDescHelp dm_help = { + .summary = "Memory map commands", +}; +static const RzCmdDescArg cmd_debug_list_maps_args[] = { + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_list_maps_help = { + .summary = "List memory maps", + .args = cmd_debug_list_maps_args, +}; + +static const RzCmdDescArg cmd_debug_allocate_maps_args[] = { + { + .name = "size", + .type = RZ_CMD_ARG_TYPE_RZNUM, + .flags = RZ_CMD_ARG_FLAG_LAST, + + }, + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_allocate_maps_help = { + .summary = "Allocate bytes at current offset", + .args = cmd_debug_allocate_maps_args, +}; + +static const RzCmdDescArg cmd_debug_list_maps_ascii_args[] = { + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_list_maps_ascii_help = { + .summary = "List memory maps of current process with ASCII art bars", + .args = cmd_debug_list_maps_ascii_args, +}; + +static const RzCmdDescArg cmd_debug_map_current_args[] = { + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_map_current_help = { + .summary = "Show map name of current address", + .args = cmd_debug_map_current_args, +}; + +static const RzCmdDescHelp dmm_help = { + .summary = "Module memory map commands", +}; +static const RzCmdDescArg cmd_debug_modules_args[] = { + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_modules_help = { + .summary = "List modules (libraries, binaries loaded in memory)", + .args = cmd_debug_modules_args, +}; + +static const RzCmdDescArg cmd_debug_current_modules_args[] = { + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_current_modules_help = { + .summary = "List memory map of current module", + .args = cmd_debug_current_modules_args, +}; + +static const RzCmdDescArg cmd_debug_deallocate_map_args[] = { + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_deallocate_map_help = { + .summary = "Deallocate memory map at current offset", + .args = cmd_debug_deallocate_map_args, +}; + +static const RzCmdDescHelp dmd_help = { + .summary = "Dump debug map regions to a file (from-to.dmp)", +}; +static const RzCmdDescArg cmd_debug_dump_maps_args[] = { + { + .name = "filename", + .type = RZ_CMD_ARG_TYPE_FILE, + .optional = true, + + }, + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_dump_maps_help = { + .summary = "Dump debug maps to ", + .args = cmd_debug_dump_maps_args, +}; + +static const RzCmdDescArg cmd_debug_dump_maps_all_args[] = { + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_dump_maps_all_help = { + .summary = "Dump all debug maps", + .args = cmd_debug_dump_maps_all_args, +}; + +static const RzCmdDescArg cmd_debug_dump_maps_writable_args[] = { + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_dump_maps_writable_help = { + .summary = "Dump writable debug maps", + .args = cmd_debug_dump_maps_writable_args, +}; + +static const RzCmdDescHelp dmh_help = { + .summary = "Glibc heap commands", +}; +static const RzCmdDescArg cmd_heap_chunks_print_args[] = { + { + .name = "malloc_state", + .type = RZ_CMD_ARG_TYPE_RZNUM, + .flags = RZ_CMD_ARG_FLAG_LAST, + .optional = true, + + }, + { 0 }, +}; +static const RzCmdDescHelp cmd_heap_chunks_print_help = { + .summary = "List heap chunks of an arena", + .args = cmd_heap_chunks_print_args, +}; + +static const RzCmdDescArg cmd_arena_print_args[] = { + { 0 }, +}; +static const RzCmdDescHelp cmd_arena_print_help = { + .summary = "List all the arenas", + .args = cmd_arena_print_args, +}; + +static const RzCmdDescArg cmd_heap_bins_list_print_args[] = { + { + .name = "bin_num|bin_num:malloc_state", + .type = RZ_CMD_ARG_TYPE_STRING, + .flags = RZ_CMD_ARG_FLAG_LAST, + .optional = true, + + }, + { 0 }, +}; +static const RzCmdDescHelp cmd_heap_bins_list_print_help = { + .summary = "Display double linked list for bins in an arena. Use dmhbg command for graphical representation.", + .args = cmd_heap_bins_list_print_args, +}; + +static const RzCmdDescArg cmd_heap_chunk_print_args[] = { + { 0 }, +}; +static const RzCmdDescHelp cmd_heap_chunk_print_help = { + .summary = "Get info about heap chunk at current offset", + .args = cmd_heap_chunk_print_args, +}; + +static const char *cmd_heap_arena_bins_print_bin_type_choices[] = { "small", "large", "fast", "unsorted", "tcache", NULL }; +static const RzCmdDescArg cmd_heap_arena_bins_print_args[] = { + { + .name = "bin_type", + .type = RZ_CMD_ARG_TYPE_CHOICES, + .optional = true, + .choices = cmd_heap_arena_bins_print_bin_type_choices, + + }, + { 0 }, +}; +static const RzCmdDescHelp cmd_heap_arena_bins_print_help = { + .summary = "Display state of bins in an arena. can be tcache/fast/unsorted/small/large", + .args = cmd_heap_arena_bins_print_args, +}; + +static const RzCmdDescArg cmd_heap_fastbins_print_args[] = { + { + .name = "fastbin_num|fastbin_num:malloc_state", + .type = RZ_CMD_ARG_TYPE_STRING, + .flags = RZ_CMD_ARG_FLAG_LAST, + .optional = true, + + }, + { 0 }, +}; +static const RzCmdDescHelp cmd_heap_fastbins_print_help = { + .summary = "Display all parsed fastbins of main_arena's or a particular arena fastbinY instance", + .args = cmd_heap_fastbins_print_args, +}; + +static const RzCmdDescArg cmd_heap_chunks_graph_args[] = { + { + .name = "malloc_state", + .type = RZ_CMD_ARG_TYPE_RZNUM, + .flags = RZ_CMD_ARG_FLAG_LAST, + .optional = true, + + }, + { 0 }, +}; +static const RzCmdDescHelp cmd_heap_chunks_graph_help = { + .summary = "Display heap graph of a particular arena", + .args = cmd_heap_chunks_graph_args, +}; + +static const RzCmdDescArg cmd_heap_info_print_args[] = { + { + .name = "malloc_state", + .type = RZ_CMD_ARG_TYPE_RZNUM, + .flags = RZ_CMD_ARG_FLAG_LAST, + .optional = true, + + }, + { 0 }, +}; +static const RzCmdDescHelp cmd_heap_info_print_help = { + .summary = "Display heap_info structure/structures for a given arena", + .args = cmd_heap_info_print_args, +}; + +static const RzCmdDescArg cmd_main_arena_print_args[] = { + { + .name = "malloc_state", + .type = RZ_CMD_ARG_TYPE_RZNUM, + .flags = RZ_CMD_ARG_FLAG_LAST, + .optional = true, + + }, + { 0 }, +}; +static const RzCmdDescHelp cmd_main_arena_print_help = { + .summary = "List all elements of struct malloc_state", + .args = cmd_main_arena_print_args, +}; + +static const RzCmdDescArg cmd_heap_tcache_print_args[] = { + { 0 }, +}; +static const RzCmdDescHelp cmd_heap_tcache_print_help = { + .summary = "Display all parsed thread cache bins of all arena's tcache instance", + .args = cmd_heap_tcache_print_args, +}; + +static const RzCmdDescHelp cmd_debug_dmi_help = { + .summary = "List/Load symbols", +}; + +static const RzCmdDescArg cmd_debug_dml_args[] = { + { + .name = "file", + .type = RZ_CMD_ARG_TYPE_FILE, + + }, + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_dml_help = { + .summary = "Load contents of file into current map region", + .args = cmd_debug_dml_args, +}; + +static const RzCmdDescArg debug_memory_permission_args[] = { + { + .name = "perms", + .type = RZ_CMD_ARG_TYPE_STRING, + + }, + { + .name = "size", + .type = RZ_CMD_ARG_TYPE_RZNUM, + .flags = RZ_CMD_ARG_FLAG_LAST, + .optional = true, + + }, + { 0 }, +}; +static const RzCmdDescHelp debug_memory_permission_help = { + .summary = "Change page at current offset with , protection / Change dbg.map permissions to ", + .args = debug_memory_permission_args, +}; + +static const RzCmdDescArg cmd_debug_dmL_args[] = { + { + .name = "size", + .type = RZ_CMD_ARG_TYPE_RZNUM, + .flags = RZ_CMD_ARG_FLAG_LAST, + + }, + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_dmL_help = { + .summary = "Allocate bytes at current offset and promote to huge page", + .args = cmd_debug_dmL_args, +}; + +static const RzCmdDescArg cmd_debug_dmS_args[] = { + { + .name = "addr|libname", + .type = RZ_CMD_ARG_TYPE_STRING, + .optional = true, + + }, + { + .name = "sectname", + .type = RZ_CMD_ARG_TYPE_STRING, + .flags = RZ_CMD_ARG_FLAG_LAST, + .optional = true, + + }, + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_dmS_help = { + .summary = "List sections of target lib", + .args = cmd_debug_dmS_args, +}; + +static const RzCmdDescArg cmd_debug_heap_windows_args[] = { + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_heap_windows_help = { + .summary = "Windows heap commands", + .args = cmd_debug_heap_windows_args, +}; + +static const RzCmdDescArg cmd_debug_heap_jemalloc_args[] = { + { 0 }, +}; +static const RzCmdDescHelp cmd_debug_heap_jemalloc_help = { + .summary = "Jemalloc heap commands", + .args = cmd_debug_heap_jemalloc_args, +}; + static const RzCmdDescHelp e_help = { .summary = "List/get/set config evaluable vars", }; @@ -4499,6 +4834,83 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) { RzCmdDesc *cmd_debug_list_trace_session_mmap_cd = rz_cmd_desc_argv_new(core->rcmd, dts_cd, "dtsm", rz_cmd_debug_list_trace_session_mmap_handler, &cmd_debug_list_trace_session_mmap_help); rz_warn_if_fail(cmd_debug_list_trace_session_mmap_cd); + RzCmdDesc *dm_cd = rz_cmd_desc_group_state_new(core->rcmd, cmd_debug_cd, "dm", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_RIZIN | RZ_OUTPUT_MODE_JSON | RZ_OUTPUT_MODE_QUIET, rz_cmd_debug_list_maps_handler, &cmd_debug_list_maps_help, &dm_help); + rz_warn_if_fail(dm_cd); + RzCmdDesc *cmd_debug_allocate_maps_cd = rz_cmd_desc_argv_new(core->rcmd, dm_cd, "dm+", rz_cmd_debug_allocate_maps_handler, &cmd_debug_allocate_maps_help); + rz_warn_if_fail(cmd_debug_allocate_maps_cd); + + RzCmdDesc *cmd_debug_list_maps_ascii_cd = rz_cmd_desc_argv_new(core->rcmd, dm_cd, "dm=", rz_cmd_debug_list_maps_ascii_handler, &cmd_debug_list_maps_ascii_help); + rz_warn_if_fail(cmd_debug_list_maps_ascii_cd); + + RzCmdDesc *cmd_debug_map_current_cd = rz_cmd_desc_argv_new(core->rcmd, dm_cd, "dm.", rz_cmd_debug_map_current_handler, &cmd_debug_map_current_help); + rz_warn_if_fail(cmd_debug_map_current_cd); + + RzCmdDesc *dmm_cd = rz_cmd_desc_group_state_new(core->rcmd, dm_cd, "dmm", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_RIZIN | RZ_OUTPUT_MODE_JSON, rz_cmd_debug_modules_handler, &cmd_debug_modules_help, &dmm_help); + rz_warn_if_fail(dmm_cd); + RzCmdDesc *cmd_debug_current_modules_cd = rz_cmd_desc_argv_modes_new(core->rcmd, dmm_cd, "dmm.", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_RIZIN, rz_cmd_debug_current_modules_handler, &cmd_debug_current_modules_help); + rz_warn_if_fail(cmd_debug_current_modules_cd); + + RzCmdDesc *cmd_debug_deallocate_map_cd = rz_cmd_desc_argv_new(core->rcmd, dm_cd, "dm-", rz_cmd_debug_deallocate_map_handler, &cmd_debug_deallocate_map_help); + rz_warn_if_fail(cmd_debug_deallocate_map_cd); + + RzCmdDesc *dmd_cd = rz_cmd_desc_group_new(core->rcmd, dm_cd, "dmd", rz_cmd_debug_dump_maps_handler, &cmd_debug_dump_maps_help, &dmd_help); + rz_warn_if_fail(dmd_cd); + RzCmdDesc *cmd_debug_dump_maps_all_cd = rz_cmd_desc_argv_new(core->rcmd, dmd_cd, "dmda", rz_cmd_debug_dump_maps_all_handler, &cmd_debug_dump_maps_all_help); + rz_warn_if_fail(cmd_debug_dump_maps_all_cd); + + RzCmdDesc *cmd_debug_dump_maps_writable_cd = rz_cmd_desc_argv_new(core->rcmd, dmd_cd, "dmdw", rz_cmd_debug_dump_maps_writable_handler, &cmd_debug_dump_maps_writable_help); + rz_warn_if_fail(cmd_debug_dump_maps_writable_cd); + + RzCmdDesc *dmh_cd = rz_cmd_desc_group_state_new(core->rcmd, dm_cd, "dmh", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON | RZ_OUTPUT_MODE_LONG | RZ_OUTPUT_MODE_RIZIN, rz_cmd_heap_chunks_print_handler, &cmd_heap_chunks_print_help, &dmh_help); + rz_warn_if_fail(dmh_cd); + RzCmdDesc *cmd_arena_print_cd = rz_cmd_desc_argv_new(core->rcmd, dmh_cd, "dmha", rz_cmd_arena_print_handler, &cmd_arena_print_help); + rz_warn_if_fail(cmd_arena_print_cd); + + RzCmdDesc *cmd_heap_bins_list_print_cd = rz_cmd_desc_oldinput_new(core->rcmd, dmh_cd, "dmhb", rz_cmd_heap_bins_list_print, &cmd_heap_bins_list_print_help); + rz_warn_if_fail(cmd_heap_bins_list_print_cd); + + RzCmdDesc *cmd_heap_chunk_print_cd = rz_cmd_desc_argv_new(core->rcmd, dmh_cd, "dmhc", rz_cmd_heap_chunk_print_handler, &cmd_heap_chunk_print_help); + rz_warn_if_fail(cmd_heap_chunk_print_cd); + + RzCmdDesc *cmd_heap_arena_bins_print_cd = rz_cmd_desc_argv_modes_new(core->rcmd, dmh_cd, "dmhd", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON, rz_cmd_heap_arena_bins_print_handler, &cmd_heap_arena_bins_print_help); + rz_warn_if_fail(cmd_heap_arena_bins_print_cd); + + RzCmdDesc *cmd_heap_fastbins_print_cd = rz_cmd_desc_oldinput_new(core->rcmd, dmh_cd, "dmhf", rz_cmd_heap_fastbins_print, &cmd_heap_fastbins_print_help); + rz_warn_if_fail(cmd_heap_fastbins_print_cd); + + RzCmdDesc *cmd_heap_chunks_graph_cd = rz_cmd_desc_argv_new(core->rcmd, dmh_cd, "dmhg", rz_cmd_heap_chunks_graph_handler, &cmd_heap_chunks_graph_help); + rz_warn_if_fail(cmd_heap_chunks_graph_cd); + + RzCmdDesc *cmd_heap_info_print_cd = rz_cmd_desc_argv_new(core->rcmd, dmh_cd, "dmhi", rz_cmd_heap_info_print_handler, &cmd_heap_info_print_help); + rz_warn_if_fail(cmd_heap_info_print_cd); + + RzCmdDesc *cmd_main_arena_print_cd = rz_cmd_desc_argv_modes_new(core->rcmd, dmh_cd, "dmhm", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_RIZIN, rz_cmd_main_arena_print_handler, &cmd_main_arena_print_help); + rz_warn_if_fail(cmd_main_arena_print_cd); + + RzCmdDesc *cmd_heap_tcache_print_cd = rz_cmd_desc_argv_new(core->rcmd, dmh_cd, "dmht", rz_cmd_heap_tcache_print_handler, &cmd_heap_tcache_print_help); + rz_warn_if_fail(cmd_heap_tcache_print_cd); + + RzCmdDesc *cmd_debug_dmi_cd = rz_cmd_desc_oldinput_new(core->rcmd, dm_cd, "dmi", rz_cmd_debug_dmi, &cmd_debug_dmi_help); + rz_warn_if_fail(cmd_debug_dmi_cd); + + RzCmdDesc *cmd_debug_dml_cd = rz_cmd_desc_argv_new(core->rcmd, dm_cd, "dml", rz_cmd_debug_dml_handler, &cmd_debug_dml_help); + rz_warn_if_fail(cmd_debug_dml_cd); + + RzCmdDesc *debug_memory_permission_cd = rz_cmd_desc_argv_new(core->rcmd, dm_cd, "dmp", rz_debug_memory_permission_handler, &debug_memory_permission_help); + rz_warn_if_fail(debug_memory_permission_cd); + + RzCmdDesc *cmd_debug_dmL_cd = rz_cmd_desc_argv_new(core->rcmd, dm_cd, "dmL", rz_cmd_debug_dmL_handler, &cmd_debug_dmL_help); + rz_warn_if_fail(cmd_debug_dmL_cd); + + RzCmdDesc *cmd_debug_dmS_cd = rz_cmd_desc_argv_modes_new(core->rcmd, dm_cd, "dmS", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_RIZIN, rz_cmd_debug_dmS_handler, &cmd_debug_dmS_help); + rz_warn_if_fail(cmd_debug_dmS_cd); + + RzCmdDesc *cmd_debug_heap_windows_cd = rz_cmd_desc_oldinput_new(core->rcmd, dm_cd, "dmw", rz_cmd_debug_heap_windows, &cmd_debug_heap_windows_help); + rz_warn_if_fail(cmd_debug_heap_windows_cd); + + RzCmdDesc *cmd_debug_heap_jemalloc_cd = rz_cmd_desc_oldinput_new(core->rcmd, dm_cd, "dmx", rz_cmd_debug_heap_jemalloc, &cmd_debug_heap_jemalloc_help); + rz_warn_if_fail(cmd_debug_heap_jemalloc_cd); + RzCmdDesc *e_cd = rz_cmd_desc_group_new(core->rcmd, root_cd, "e", rz_eval_getset_handler, &eval_getset_help, &e_help); rz_warn_if_fail(e_cd); RzCmdDesc *eval_list_cd = rz_cmd_desc_argv_modes_new(core->rcmd, e_cd, "el", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_RIZIN | RZ_OUTPUT_MODE_JSON | RZ_OUTPUT_MODE_QUIET | RZ_OUTPUT_MODE_LONG | RZ_OUTPUT_MODE_LONG_JSON, rz_eval_list_handler, &eval_list_help); diff --git a/librz/core/cmd_descs/cmd_descs.h b/librz/core/cmd_descs/cmd_descs.h index f38fc424b35..92589b9d752 100644 --- a/librz/core/cmd_descs/cmd_descs.h +++ b/librz/core/cmd_descs/cmd_descs.h @@ -111,6 +111,33 @@ RZ_IPI RzCmdStatus rz_cmd_debug_stop_trace_session_handler(RzCore *core, int arg RZ_IPI RzCmdStatus rz_cmd_debug_save_trace_session_handler(RzCore *core, int argc, const char **argv); RZ_IPI RzCmdStatus rz_cmd_debug_load_trace_session_handler(RzCore *core, int argc, const char **argv); RZ_IPI RzCmdStatus rz_cmd_debug_list_trace_session_mmap_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_cmd_debug_list_maps_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state); +RZ_IPI RzCmdStatus rz_cmd_debug_allocate_maps_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_cmd_debug_list_maps_ascii_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_cmd_debug_map_current_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_cmd_debug_modules_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state); +RZ_IPI RzCmdStatus rz_cmd_debug_current_modules_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode); +RZ_IPI RzCmdStatus rz_cmd_debug_deallocate_map_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_cmd_debug_dump_maps_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_cmd_debug_dump_maps_all_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_cmd_debug_dump_maps_writable_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_cmd_heap_chunks_print_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state); +RZ_IPI RzCmdStatus rz_cmd_arena_print_handler(RzCore *core, int argc, const char **argv); +RZ_IPI int rz_cmd_heap_bins_list_print(void *data, const char *input); +RZ_IPI RzCmdStatus rz_cmd_heap_chunk_print_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_cmd_heap_arena_bins_print_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode); +RZ_IPI int rz_cmd_heap_fastbins_print(void *data, const char *input); +RZ_IPI RzCmdStatus rz_cmd_heap_chunks_graph_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_cmd_heap_info_print_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_cmd_main_arena_print_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode); +RZ_IPI RzCmdStatus rz_cmd_heap_tcache_print_handler(RzCore *core, int argc, const char **argv); +RZ_IPI int rz_cmd_debug_dmi(void *data, const char *input); +RZ_IPI RzCmdStatus rz_cmd_debug_dml_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_debug_memory_permission_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_cmd_debug_dmL_handler(RzCore *core, int argc, const char **argv); +RZ_IPI RzCmdStatus rz_cmd_debug_dmS_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode); +RZ_IPI int rz_cmd_debug_heap_windows(void *data, const char *input); +RZ_IPI int rz_cmd_debug_heap_jemalloc(void *data, const char *input); RZ_IPI int rz_cmd_debug(void *data, const char *input); RZ_IPI RzCmdStatus rz_eval_getset_handler(RzCore *core, int argc, const char **argv); RZ_IPI RzCmdStatus rz_eval_list_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode); diff --git a/librz/core/cmd_descs/cmd_heap_glibc.yaml b/librz/core/cmd_descs/cmd_heap_glibc.yaml new file mode 100644 index 00000000000..dfbda4153f7 --- /dev/null +++ b/librz/core/cmd_descs/cmd_heap_glibc.yaml @@ -0,0 +1,85 @@ +--- +name: cmd_heap_glibc +commands: + - name: dmh + cname: cmd_heap_chunks_print + summary: List heap chunks of an arena + type: RZ_CMD_DESC_TYPE_ARGV_STATE + modes: + - RZ_OUTPUT_MODE_STANDARD + - RZ_OUTPUT_MODE_JSON + - RZ_OUTPUT_MODE_LONG + - RZ_OUTPUT_MODE_RIZIN + args: + - name: malloc_state + type: RZ_CMD_ARG_TYPE_RZNUM + optional: true + - name: dmha + cname: cmd_arena_print + summary: List all the arenas + args: [] + - name: dmhb + cname: cmd_heap_bins_list_print + summary: > + Display double linked list for bins in an arena. + Use dmhbg command for graphical representation. + type: RZ_CMD_DESC_TYPE_OLDINPUT + args: + - name: bin_num|bin_num:malloc_state + type: RZ_CMD_ARG_TYPE_STRING + optional: true + - name: dmhc + cname: cmd_heap_chunk_print + summary: Get info about heap chunk at current offset + args: [] + - name: dmhd + cname: cmd_heap_arena_bins_print + summary: > + Display state of bins in an arena. + can be tcache/fast/unsorted/small/large + modes: + - RZ_OUTPUT_MODE_STANDARD + - RZ_OUTPUT_MODE_JSON + args: + - name: bin_type + type: RZ_CMD_ARG_TYPE_CHOICES + optional: true + choices: ["small", "large", "fast", "unsorted", "tcache"] + - name: dmhf + cname: cmd_heap_fastbins_print + type: RZ_CMD_DESC_TYPE_OLDINPUT + summary: > + Display all parsed fastbins of main_arena's or a particular + arena fastbinY instance + args: + - name: fastbin_num|fastbin_num:malloc_state + type: RZ_CMD_ARG_TYPE_STRING + optional: true + - name: dmhg + cname: cmd_heap_chunks_graph + summary: Display heap graph of a particular arena + args: + - name: malloc_state + type: RZ_CMD_ARG_TYPE_RZNUM + optional: true + - name: dmhi + cname: cmd_heap_info_print + summary: Display heap_info structure/structures for a given arena + args: + - name: malloc_state + type: RZ_CMD_ARG_TYPE_RZNUM + optional: true + - name: dmhm + cname: cmd_main_arena_print + summary: List all elements of struct malloc_state + modes: + - RZ_OUTPUT_MODE_STANDARD + - RZ_OUTPUT_MODE_RIZIN + args: + - name: malloc_state + type: RZ_CMD_ARG_TYPE_RZNUM + optional: true + - name: dmht + cname: cmd_heap_tcache_print + summary: Display all parsed thread cache bins of all arena's tcache instance + args: [] diff --git a/librz/core/cmd_descs/meson.build b/librz/core/cmd_descs/meson.build index 32a3b69f561..f8b8e58cdc9 100644 --- a/librz/core/cmd_descs/meson.build +++ b/librz/core/cmd_descs/meson.build @@ -4,6 +4,7 @@ cmd_descs_yaml = files( 'cmd_debug.yaml', 'cmd_descs.yaml', 'cmd_eval.yaml', + 'cmd_heap_glibc.yaml', 'cmd_interpret.yaml', 'cmd_plugins.yaml', 'cmd_project.yaml', diff --git a/librz/core/cmd_linux_heap_glibc.c b/librz/core/cmd_linux_heap_glibc.c new file mode 100644 index 00000000000..ae32b43742b --- /dev/null +++ b/librz/core/cmd_linux_heap_glibc.c @@ -0,0 +1,53 @@ +#include +#define call_handler(fun, ...) \ + { \ + if (core->rasm->bits == 64) { \ + return fun##_64(core, ##__VA_ARGS__); \ + } else { \ + return fun##_32(core, ##__VA_ARGS__); \ + } \ + } +RZ_IPI RzCmdStatus rz_cmd_heap_chunks_print_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) { + call_handler(rz_cmd_heap_chunks_print_handler, argc, argv, state); +} + +RZ_IPI RzCmdStatus rz_cmd_arena_print_handler(RzCore *core, int argc, const char **argv) { + call_handler(rz_cmd_arena_print_handler, argc, argv); +} + +RZ_IPI RzCmdStatus rz_cmd_main_arena_print_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) { + call_handler(rz_cmd_main_arena_print_handler, argc, argv, mode); +} + +RZ_IPI RzCmdStatus rz_cmd_heap_chunk_print_handler(RzCore *core, int argc, const char **argv) { + call_handler(rz_cmd_heap_chunk_print_handler, argc, argv); +} + +RZ_IPI RzCmdStatus rz_cmd_heap_chunks_graph_handler(RzCore *core, int argc, const char **argv) { + // RZ_OUTPUT_MODE_LONG_JSON mode workaround for graph + RzCmdStateOutput state = { 0 }; + state.mode = RZ_OUTPUT_MODE_LONG_JSON; + call_handler(rz_cmd_heap_chunks_print_handler, argc, argv, &state); +} + +RZ_IPI RzCmdStatus rz_cmd_heap_info_print_handler(RzCore *core, int argc, const char **argv) { + call_handler(rz_cmd_heap_info_print_handler, argc, argv); +} + +RZ_IPI RzCmdStatus rz_cmd_heap_tcache_print_handler(RzCore *core, int argc, const char **argv) { + call_handler(rz_cmd_heap_tcache_print_handler, argc, argv); +} + +RZ_IPI int rz_cmd_heap_bins_list_print(void *data, const char *input) { + RzCore *core = (RzCore *)data; + call_handler(rz_cmd_heap_bins_list_print, input); +} + +RZ_IPI int rz_cmd_heap_fastbins_print(void *data, const char *input) { + RzCore *core = (RzCore *)data; + call_handler(rz_cmd_heap_fastbins_print, input); +} + +RZ_IPI RzCmdStatus rz_cmd_heap_arena_bins_print_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) { + call_handler(rz_cmd_heap_arena_bins_print_handler, argc, argv, mode); +} \ No newline at end of file diff --git a/librz/core/linux_heap_glibc.c b/librz/core/linux_heap_glibc.c index ea204acd677..2ac3aef5331 100644 --- a/librz/core/linux_heap_glibc.c +++ b/librz/core/linux_heap_glibc.c @@ -71,7 +71,7 @@ static inline GHT GH(align_address_to_size)(ut64 addr, ut64 align) { } static inline GHT GH(get_next_pointer)(RzCore *core, GHT pos, GHT next) { - return (core->dbg->glibc_version < 232) ? next : PROTECT_PTR(pos, next); + return (core->dbg->glibc_version < 232) ? next : (GHT)((pos >> 12) ^ next); } static GHT GH(get_main_arena_with_symbol)(RzCore *core, RzDebugMap *map) { @@ -188,7 +188,14 @@ static void GH(update_arena_without_tc)(GH(RzHeap_MallocState) * cmain_arena, Ma main_arena->GH(max_system_mem) = cmain_arena->max_system_mem; } -static bool GH(update_main_arena)(RzCore *core, GHT m_arena, MallocState *main_arena) { +/** + * \brief Store the MallocState struct of an arena with base address m_arena in main_arena + * \param core RzCore pointer + * \param m_arena The base address of malloc state struct of the arena + * \param main_arena The MallocState struct in which the data is stored + * \return True if the main_arena struct was successfully updated else False + */ +RZ_API bool GH(rz_heap_update_main_arena)(RzCore *core, GHT m_arena, MallocState *main_arena) { const int tcache = rz_config_get_i(core->config, "dbg.glibc.tcache"); if (tcache) { GH(RzHeap_MallocState_tcache) *cmain_arena = RZ_NEW0(GH(RzHeap_MallocState_tcache)); @@ -251,7 +258,7 @@ static void GH(print_arena_stats)(RzCore *core, GHT m_arena, MallocState *main_a } GHT apart[NSMALLBINS + 1] = { 0LL }; - if (format == '*') { + if (format == RZ_OUTPUT_MODE_RIZIN) { for (i = 0; i < NBINS * 2 - 2; i += 2) { GHT addr = m_arena + align + SZ * i - SZ * 2; GHT bina = main_arena->GH(bins)[i]; @@ -391,19 +398,22 @@ static void GH(print_arena_stats)(RzCore *core, GHT m_arena, MallocState *main_a PRINT_GA("}\n\n"); } -static bool GH(rz_resolve_main_arena)(RzCore *core, GHT *m_arena) { +/** + * \brief Store the base address of main arena at m_arena + * \param core RzCore pointer + * \param m_arena Store the location of main arena at this integer pointer + * \return True if a main arena was found else False + */ +RZ_API bool GH(rz_heap_resolve_main_arena)(RzCore *core, GHT *m_arena) { rz_return_val_if_fail(core && core->dbg && core->dbg->maps, false); - if (core->dbg->main_arena_resolved) { - return true; - } - GHT brk_start = GHT_MAX, brk_end = GHT_MAX; GHT libc_addr_sta = GHT_MAX, libc_addr_end = 0; GHT addr_srch = GHT_MAX, heap_sz = GHT_MAX; GHT main_arena_sym = GHT_MAX; bool is_debugged = rz_config_get_b(core->config, "cfg.debug"); bool first_libc = true; + rz_config_set_i(core->config, "dbg.glibc.tcache", GH(is_tcache)(core)); if (is_debugged) { RzListIter *iter; @@ -458,7 +468,7 @@ static bool GH(rz_resolve_main_arena)(RzCore *core, GHT *m_arena) { } if (main_arena_sym != GHT_MAX) { - GH(update_main_arena) + GH(rz_heap_update_main_arena) (core, main_arena_sym, ta); *m_arena = main_arena_sym; core->dbg->main_arena_resolved = true; @@ -466,7 +476,7 @@ static bool GH(rz_resolve_main_arena)(RzCore *core, GHT *m_arena) { return true; } while (addr_srch < libc_addr_end) { - GH(update_main_arena) + GH(rz_heap_update_main_arena) (core, addr_srch, ta); if (ta->GH(top) > brk_start && ta->GH(top) < brk_end && ta->GH(system_mem) == heap_sz) { @@ -485,9 +495,8 @@ static bool GH(rz_resolve_main_arena)(RzCore *core, GHT *m_arena) { return false; } -void GH(print_heap_chunk)(RzCore *core) { +void GH(print_heap_chunk)(RzCore *core, GHT chunk) { GH(RzHeapChunk) *cnk = RZ_NEW0(GH(RzHeapChunk)); - GHT chunk = core->offset; RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; if (!cnk) { @@ -540,56 +549,80 @@ void GH(print_heap_chunk)(RzCore *core) { } /** - * \brief Prints compact representation of a heap chunk. Format: Chunk(addr=, size=, flags=) + * \brief Get a heap chunk with base address * \param core RzCore pointer - * \param chunk Offset of the chunk in memory + * \param addr Base address of the chunk + * \return RzHeapChunk struct pointer of the chunk */ -void GH(print_heap_chunk_simple)(RzCore *core, GHT chunk, const char *status) { +RZ_API GH(RzHeapChunk) * GH(rz_heap_get_chunk_at_addr)(RzCore *core, GHT addr) { GH(RzHeapChunk) *cnk = RZ_NEW0(GH(RzHeapChunk)); - RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - if (!cnk) { - return; + return NULL; } + (void)rz_io_nread_at(core->io, addr, (ut8 *)cnk, sizeof(*cnk)); + return cnk; +} - (void)rz_io_read_at(core->io, chunk, (ut8 *)cnk, sizeof(*cnk)); - - PRINT_GA("Chunk"); - rz_cons_printf("("); - if (status) { - rz_cons_printf("status="); - if (!strcmp(status, "free")) { - PRINTF_GA("%s", status); - rz_cons_printf("%-6s", ","); - } else { - rz_cons_printf("%s,", status); - } - rz_cons_printf(" "); - } - rz_cons_printf("addr="); - PRINTF_YA("0x%" PFMT64x, (ut64)chunk); - rz_cons_printf(", size="); - PRINTF_BA("0x%" PFMT64x, (ut64)cnk->size & ~(NON_MAIN_ARENA | IS_MMAPPED | PREV_INUSE)); - rz_cons_printf(", flags="); - bool print_comma = false; - if (cnk->size & NON_MAIN_ARENA) { - PRINT_RA("NON_MAIN_ARENA"); - print_comma = true; +/** + * \brief Prints compact representation of a heap chunk. Format: Chunk(addr=, size=, flags=) + * \param core RzCore pointer + * \param chunk Offset of the chunk in memory + */ +void GH(print_heap_chunk_simple)(RzCore *core, GHT chunk, const char *status, PJ *pj) { + GH(RzHeapChunk) *cnk = GH(rz_heap_get_chunk_at_addr)(core, chunk); + if (!cnk) { + return; } - if (cnk->size & IS_MMAPPED) { - if (print_comma) { - PRINT_RA(","); + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + if (pj == NULL) { + PRINT_GA("Chunk"); + rz_cons_printf("("); + if (status) { + rz_cons_printf("status="); + if (!strcmp(status, "free")) { + PRINTF_GA("%s", status); + rz_cons_printf("%-6s", ","); + } else { + rz_cons_printf("%s,", status); + } + rz_cons_printf(" "); + } + rz_cons_printf("addr="); + PRINTF_YA("0x%" PFMT64x, (ut64)chunk); + rz_cons_printf(", size="); + PRINTF_BA("0x%" PFMT64x, (ut64)cnk->size & ~(NON_MAIN_ARENA | IS_MMAPPED | PREV_INUSE)); + rz_cons_printf(", flags="); + bool print_comma = false; + if (cnk->size & NON_MAIN_ARENA) { + PRINT_RA("NON_MAIN_ARENA"); + print_comma = true; + } + if (cnk->size & IS_MMAPPED) { + if (print_comma) { + PRINT_RA(","); + } + PRINT_RA("IS_MMAPPED"); + print_comma = true; } - PRINT_RA("IS_MMAPPED"); - print_comma = true; - } - if (cnk->size & PREV_INUSE) { - if (print_comma) { - PRINT_RA(","); + if (cnk->size & PREV_INUSE) { + if (print_comma) { + PRINT_RA(","); + } + PRINT_RA("PREV_INUSE"); } - PRINT_RA("PREV_INUSE"); + rz_cons_printf(")"); + } else { + pj_o(pj); + pj_kn(pj, "prev_size", cnk->prev_size); + pj_kn(pj, "addr", chunk); + pj_kn(pj, "size", (ut64)cnk->size & ~(NON_MAIN_ARENA | IS_MMAPPED | PREV_INUSE)); + pj_kn(pj, "non_main_arena", cnk->size & NON_MAIN_ARENA); + pj_kn(pj, "mmapped", cnk->size & IS_MMAPPED); + pj_kn(pj, "prev_inuse", cnk->size & PREV_INUSE); + pj_kn(pj, "fd", cnk->fd); + pj_kn(pj, "bk", cnk->bk); + pj_end(pj); } - rz_cons_printf(")"); free(cnk); } @@ -601,7 +634,7 @@ static bool GH(is_arena)(RzCore *core, GHT m_arena, GHT m_state) { if (!ta) { return false; } - if (!GH(update_main_arena)(core, m_arena, ta)) { + if (!GH(rz_heap_update_main_arena)(core, m_arena, ta)) { free(ta); return false; } @@ -610,7 +643,7 @@ static bool GH(is_arena)(RzCore *core, GHT m_arena, GHT m_state) { return true; } while (ta->GH(next) != GHT_MAX && ta->GH(next) != m_arena) { - if (!GH(update_main_arena)(core, ta->GH(next), ta)) { + if (!GH(rz_heap_update_main_arena)(core, ta->GH(next), ta)) { free(ta); return false; } @@ -761,16 +794,12 @@ static int GH(print_double_linked_list_bin)(RzCore *core, MallocState *main_aren initial_brk = (brk_start >> 12) << 12; } - switch (num_bin) { - case 0: + if (num_bin == 0) { PRINT_GA(" double linked list unsorted bin {\n"); - break; - case 1 ... NSMALLBINS - 1: + } else if (num_bin >= 1 && num_bin <= NSMALLBINS - 1) { PRINT_GA(" double linked list small bin {\n"); - break; - case NSMALLBINS ... NBINS - 2: + } else if (num_bin >= NSMALLBINS && num_bin <= NBINS - 2) { PRINT_GA(" double linked list large bin {\n"); - break; } if (!graph || graph == 1) { @@ -821,7 +850,7 @@ static void GH(print_heap_bin)(RzCore *core, GHT m_arena, MallocState *main_aren } } -static int GH(print_single_linked_list_bin)(RzCore *core, MallocState *main_arena, GHT m_arena, GHT offset, GHT bin_num) { +static int GH(print_single_linked_list_bin)(RzCore *core, MallocState *main_arena, GHT m_arena, GHT offset, GHT bin_num, PJ *pj) { if (!core || !core->dbg || !core->dbg->maps) { return -1; } @@ -833,7 +862,7 @@ static int GH(print_single_linked_list_bin)(RzCore *core, MallocState *main_aren return 0; } - if (!GH(update_main_arena)(core, m_arena, main_arena)) { + if (!GH(rz_heap_update_main_arena)(core, m_arena, main_arena)) { free(cnk); return 0; } @@ -854,16 +883,18 @@ static int GH(print_single_linked_list_bin)(RzCore *core, MallocState *main_aren free(cnk); return 0; } - - rz_cons_printf("\n -> "); - + if (!pj) { + rz_cons_printf("\n -> "); + } GHT size = main_arena->GH(top) - brk_start; GHT next_root = next, next_tmp = next, double_free = GHT_MAX; while (next && next >= brk_start && next < main_arena->GH(top)) { GH(print_heap_chunk_simple) - (core, (ut64)next, NULL); - rz_cons_newline(); + (core, (ut64)next, NULL, pj); + if (!pj) { + rz_cons_newline(); + } while (double_free == GHT_MAX && next_tmp && next_tmp >= brk_start && next_tmp <= main_arena->GH(top)) { rz_io_read_at(core->io, next_tmp, (ut8 *)cnk, sizeof(GH(RzHeapChunk))); next_tmp = GH(get_next_pointer)(core, next_tmp, cnk->fd); @@ -877,7 +908,9 @@ static int GH(print_single_linked_list_bin)(RzCore *core, MallocState *main_aren } rz_io_read_at(core->io, next, (ut8 *)cnk, sizeof(GH(RzHeapChunk))); next = GH(get_next_pointer)(core, next, cnk->fd); - rz_cons_printf("%s", next ? " -> " : ""); + if (!pj) { + rz_cons_printf("%s", next ? " -> " : ""); + } if (cnk->prev_size > size || ((cnk->size >> 3) << 3) > size) { PRINTF_RA(" 0x%" PFMT64x, (ut64)next); PRINT_RA(" Linked list corrupted\n"); @@ -905,7 +938,7 @@ static int GH(print_single_linked_list_bin)(RzCore *core, MallocState *main_aren return 0; } -void GH(print_heap_fastbin)(RzCore *core, GHT m_arena, MallocState *main_arena, GHT global_max_fast, const char *input, bool main_arena_only) { +void GH(print_heap_fastbin)(RzCore *core, GHT m_arena, MallocState *main_arena, GHT global_max_fast, const char *input, bool main_arena_only, PJ *pj) { size_t i, j, k; GHT num_bin = GHT_MAX, offset = sizeof(int) * 2; const int tcache = rz_config_get_i(core->config, "dbg.glibc.tcache"); @@ -924,17 +957,31 @@ void GH(print_heap_fastbin)(RzCore *core, GHT m_arena, MallocState *main_arena, if (!main_arena_only && core->offset != core->prompt_offset) { m_arena = core->offset; } - rz_cons_printf("Fast bins in Arena @ "); - PRINTF_YA("0x%" PFMT64x "\n", (ut64)m_arena); - + if (!pj) { + rz_cons_printf("Fast bins in Arena @ "); + PRINTF_YA("0x%" PFMT64x "\n", (ut64)m_arena); + } for (i = 0, j = 1, k = SZ * 4; i <= fastbin_count; i++, j++, k += SZ * 2) { - rz_cons_printf("Fast_bin["); - PRINTF_BA("%02zu", j); - rz_cons_printf("] [size: "); - PRINTF_BA("0x%" PFMT64x, (ut64)k); - rz_cons_printf("]"); - if (GH(print_single_linked_list_bin)(core, main_arena, m_arena, offset, i)) { - PRINT_RA(" Empty bin\n"); + if (!pj) { + rz_cons_printf("Fast_bin["); + PRINTF_BA("%02zu", j); + rz_cons_printf("] [size: "); + PRINTF_BA("0x%" PFMT64x, (ut64)k); + rz_cons_printf("]"); + } else { + pj_o(pj); + pj_ks(pj, "bin_type", "fast"); + pj_kn(pj, "bin_num", j); + pj_ka(pj, "chunks"); + } + if (GH(print_single_linked_list_bin)(core, main_arena, m_arena, offset, i, pj)) { + if (!pj) { + PRINT_RA(" Empty bin\n"); + } + } + if (pj) { + pj_end(pj); + pj_end(pj); } } break; @@ -949,7 +996,7 @@ void GH(print_heap_fastbin)(RzCore *core, GHT m_arena, MallocState *main_arena, rz_cons_printf("] [size: "); PRINTF_BA("0x%" PFMT64x, (ut64)FASTBIN_IDX_TO_SIZE(num_bin + 1)); rz_cons_printf("]"); - 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, pj)) { PRINT_RA(" Empty bin\n"); } break; @@ -969,7 +1016,7 @@ static GH(RTcache) * GH(tcache_new)(RzCore *core) { return tcache; } -static void GH(tcache_free)(GH(RTcache) * tcache) { +RZ_API void GH(tcache_free)(GH(RTcache) * tcache) { rz_return_if_fail(tcache); tcache->type == NEW ? free(tcache->RzHeapTcache.heap_tcache) @@ -998,7 +1045,7 @@ static GHT GH(tcache_get_entry)(GH(RTcache) * tcache, int index) { : tcache->RzHeapTcache.heap_tcache_pre_230->entries[index]; } -static void GH(tcache_print)(RzCore *core, GH(RTcache) * tcache) { +static void GH(tcache_print)(RzCore *core, GH(RTcache) * tcache, PJ *pj) { rz_return_if_fail(core && tcache); GHT tcache_fd = GHT_MAX; GHT tcache_tmp = GHT_MAX; @@ -1008,14 +1055,21 @@ static void GH(tcache_print)(RzCore *core, GH(RTcache) * tcache) { int count = GH(tcache_get_count)(tcache, i); GHT entry = GH(tcache_get_entry)(tcache, i); if (count > 0) { - PRINT_GA("Tcache_bin["); - PRINTF_BA("%02zu", i); - PRINT_GA("] Items:"); - PRINTF_BA("%2d", count); - rz_cons_newline(); - rz_cons_printf(" -> "); + if (!pj) { + rz_cons_printf("Tcache_bin["); + PRINTF_BA("%02zu", i); + rz_cons_printf("] Items:"); + PRINTF_BA("%2d", count); + rz_cons_newline(); + rz_cons_printf(" -> "); + } else { + pj_o(pj); + pj_ks(pj, "bin_type", "tcache"); + pj_kn(pj, "bin_num", i); + pj_ka(pj, "chunks"); + } GH(print_heap_chunk_simple) - (core, (ut64)(entry - GH(HDR_SZ)), NULL); + (core, (ut64)(entry - GH(HDR_SZ)), NULL, pj); if (count > 1) { tcache_fd = entry; size_t n; @@ -1025,55 +1079,65 @@ static void GH(tcache_print)(RzCore *core, GH(RTcache) * tcache) { break; } tcache_tmp = GH(get_next_pointer)(core, tcache_fd, read_le(&tcache_tmp)); - rz_cons_printf("\n -> "); + if (!pj) { + rz_cons_printf("\n -> "); + } GH(print_heap_chunk_simple) - (core, (ut64)(tcache_tmp - TC_HDR_SZ), NULL); + (core, (ut64)(tcache_tmp - TC_HDR_SZ), NULL, pj); tcache_fd = tcache_tmp; } } - PRINT_BA("\n"); + if (!pj) { + PRINT_BA("\n"); + } else { + pj_end(pj); + pj_end(pj); + } } } } -static void GH(print_tcache_instance)(RzCore *core, GHT m_arena, MallocState *main_arena, bool main_thread_only) { - rz_return_if_fail(core && core->dbg && core->dbg->maps); +/** + * \brief Get a list of RzTcache objects which contain information about tcache. + * First object in the returned list is the tcache info for main arena and then subsequent are for thread arena with order conserved + * \param core RzCore pointer + * \param m_arena Base address of main arena + * \param main_arena MallocState struct of main arena + * \param main_thread_only Only get tcache information for main thread + * \return RzList of RzTcache objects + */ +RZ_API RzList *GH(rz_heap_tcache_list)(RzCore *core, GHT m_arena, MallocState *main_arena, bool main_thread_only) { + RzList *tcache_list = rz_list_newf((RzListFree)GH(tcache_free)); + rz_return_val_if_fail(core && core->dbg && core->dbg->maps, tcache_list); const int tcache = rz_config_get_i(core->config, "dbg.glibc.tcache"); if (!tcache) { rz_cons_printf("No Tcache in this libc version\n"); - return; + return tcache_list; } GHT brk_start = GHT_MAX, brk_end = GHT_MAX, initial_brk = GHT_MAX; GH(get_brks) (core, &brk_start, &brk_end); GHT tcache_start = GHT_MAX; - RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - tcache_start = brk_start + 0x10; GHT fc_offset = GH(tcache_chunk_size)(core, brk_start); initial_brk = brk_start + fc_offset; if (brk_start == GHT_MAX || brk_end == GHT_MAX || initial_brk == GHT_MAX) { eprintf("No heap section\n"); - return; + return tcache_list; } GH(RTcache) *rz_tcache = GH(tcache_new)(core); if (!rz_tcache) { - return; + return tcache_list; } if (!GH(tcache_read)(core, tcache_start, rz_tcache)) { - return; + return tcache_list; } - - rz_cons_printf("Tcache bins in Main Arena @"); - PRINTF_YA(" 0x%" PFMT64x "\n", (ut64)m_arena); - GH(tcache_print) - (core, rz_tcache); + rz_list_append(tcache_list, rz_tcache); if (main_thread_only) { - return; + return tcache_list; } - if (main_arena->GH(next) != m_arena) { GHT mmap_start = GHT_MAX, tcache_start = GHT_MAX; MallocState *ta = RZ_NEW0(MallocState); @@ -1081,222 +1145,590 @@ static void GH(print_tcache_instance)(RzCore *core, GHT m_arena, MallocState *ma free(ta); GH(tcache_free) (rz_tcache); - return; + return tcache_list; } ta->GH(next) = main_arena->GH(next); while (GH(is_arena)(core, m_arena, ta->GH(next)) && ta->GH(next) != m_arena) { - PRINT_YA("Tcache in Thread Arena @ "); - PRINTF_BA(" 0x%" PFMT64x, (ut64)ta->GH(next)); mmap_start = ((ta->GH(next) >> 16) << 16); tcache_start = mmap_start + sizeof(GH(RzHeapInfo)) + sizeof(GH(RzHeap_MallocState_tcache)) + GH(MMAP_ALIGN); - if (!GH(update_main_arena)(core, ta->GH(next), ta)) { + if (!GH(rz_heap_update_main_arena)(core, ta->GH(next), ta)) { free(ta); GH(tcache_free) (rz_tcache); - return; + return tcache_list; } if (ta->attached_threads) { - PRINT_BA("\n"); + rz_tcache = GH(tcache_new)(core); GH(tcache_read) (core, tcache_start, rz_tcache); - GH(tcache_print) - (core, rz_tcache); - } else { - PRINT_GA(" free\n"); + rz_list_append(tcache_list, rz_tcache); } } } - GH(tcache_free) - (rz_tcache); + return tcache_list; } -static void GH(print_heap_segment)(RzCore *core, MallocState *main_arena, - GHT m_arena, GHT m_state, GHT global_max_fast, int format_out) { - - if (!core || !core->dbg || !core->dbg->maps) { +static void GH(print_tcache_instance)(RzCore *core, GHT m_arena, MallocState *main_arena, bool main_thread_only, PJ *pj) { + rz_return_if_fail(core && core->dbg && core->dbg->maps); + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + RzList *tcache_list = GH(rz_heap_tcache_list)(core, m_arena, main_arena, main_thread_only); + if (rz_list_length(tcache_list) == 0) { + rz_list_free(tcache_list); return; } - - int w, h; - GHT brk_start = GHT_MAX, brk_end = GHT_MAX, size_tmp, min_size = SZ * 4; - GHT tcache_fd = GHT_MAX, tcache_tmp = GHT_MAX; - GHT initial_brk = GHT_MAX, tcache_initial_brk = GHT_MAX; - - const int tcache = rz_config_get_i(core->config, "dbg.glibc.tcache"); - const int offset = rz_config_get_i(core->config, "dbg.glibc.fc_offset"); - RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - int glibc_version = core->dbg->glibc_version; - - if (m_arena == m_state) { - GH(get_brks) - (core, &brk_start, &brk_end); - if (tcache) { - initial_brk = ((brk_start >> 12) << 12) + GH(HDR_SZ); - if (rz_config_get_b(core->config, "cfg.debug")) { - tcache_initial_brk = initial_brk; - } - initial_brk += (glibc_version < 230) - ? sizeof(GH(RzHeapTcachePre230)) - : sizeof(GH(RzHeapTcache)); + RzList *arenas_list = GH(rz_heap_arenas_list)(core, m_arena, main_arena); + if (rz_list_length(tcache_list) > rz_list_length(arenas_list)) { + rz_list_free(tcache_list); + rz_list_free(arenas_list); + return; + } + RzListIter *iter; + GH(RTcache) * rz_tcache; + int count = 0; + rz_list_foreach (tcache_list, iter, rz_tcache) { + MallocState *prev_arena; + if (count == 0) { + prev_arena = arenas_list->tail->data; + rz_cons_printf("Tcache in Main Arena @ "); } else { - initial_brk = (brk_start >> 12) << 12; + prev_arena = rz_list_get_n(arenas_list, count - 1); + rz_cons_printf("Tcache in Thread Arena @ "); } - } else { - brk_start = ((m_state >> 16) << 16); - brk_end = brk_start + main_arena->GH(system_mem); - if (tcache) { - tcache_initial_brk = brk_start + sizeof(GH(RzHeapInfo)) + sizeof(GH(RzHeap_MallocState_tcache)) + GH(MMAP_ALIGN); - initial_brk = tcache_initial_brk + offset; - } else { - initial_brk = brk_start + sizeof(GH(RzHeapInfo)) + sizeof(GH(RzHeap_MallocState)) + MMAP_OFFSET; + PRINTF_YA(" 0x%" PFMT64x, (ut64)prev_arena->GH(next)); + rz_cons_newline(); + GH(tcache_print) + (core, rz_tcache, pj); + count += 1; + if (count > 0 && main_thread_only) { + break; } } - - if (brk_start == GHT_MAX || brk_end == GHT_MAX || initial_brk == GHT_MAX) { - eprintf("No Heap section\n"); - return; + rz_cons_newline(); + rz_list_free(tcache_list); + rz_list_free(arenas_list); + if (pj) { + pj_end(pj); + pj_end(pj); } +} - GHT next_chunk = initial_brk, prev_chunk = next_chunk; - GH(RzHeapChunk) *cnk = RZ_NEW0(GH(RzHeapChunk)); - if (!cnk) { +void GH(print_malloc_states)(RzCore *core, GHT m_arena, MallocState *main_arena, bool json) { + MallocState *ta = RZ_NEW0(MallocState); + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + + if (!ta) { return; } - GH(RzHeapChunk) *cnk_next = RZ_NEW0(GH(RzHeapChunk)); - if (!cnk_next) { - free(cnk); - return; + PJ *pj = pj_new(); + if (!json) { + rz_cons_printf("Main arena (addr="); + PRINTF_YA("0x%" PFMT64x, (ut64)m_arena); + rz_cons_printf(", lastRemainder="); + PRINTF_YA("0x%" PFMT64x, (ut64)main_arena->GH(last_remainder)); + rz_cons_printf(", top="); + PRINTF_YA("0x%" PFMT64x, (ut64)main_arena->GH(top)); + rz_cons_printf(", next="); + PRINTF_YA("0x%" PFMT64x, (ut64)main_arena->GH(next)); + rz_cons_printf(")\n"); + } else { + pj_o(pj); + pj_ka(pj, "arenas"); + pj_o(pj); + pj_kn(pj, "addr", m_arena); + pj_kn(pj, "last_rem", main_arena->GH(last_remainder)); + pj_kn(pj, "top", main_arena->GH(top)); + pj_kn(pj, "next", main_arena->GH(next)); + pj_ks(pj, "type", "main"); + pj_ks(pj, "state", "used"); + pj_end(pj); } - - RzConfigHold *hc = rz_config_hold_new(core->config); - if (!hc) { - free(cnk); - free(cnk_next); - return; + if (main_arena->GH(next) != m_arena) { + ta->GH(next) = main_arena->GH(next); + while (GH(is_arena)(core, m_arena, ta->GH(next)) && ta->GH(next) != m_arena) { + ut64 ta_addr = ta->GH(next); + if (!GH(rz_heap_update_main_arena)(core, ta->GH(next), ta)) { + free(ta); + return; + } + if (!json) { + rz_cons_printf("Thread arena(addr="); + PRINTF_YA("0x%" PFMT64x, ta_addr); + rz_cons_printf(", lastRemainder="); + PRINTF_YA("0x%" PFMT64x, (ut64)ta->GH(last_remainder)); + rz_cons_printf(", top="); + PRINTF_YA("0x%" PFMT64x, (ut64)ta->GH(top)); + rz_cons_printf(", next="); + PRINTF_YA("0x%" PFMT64x, (ut64)ta->GH(next)); + if (ta->attached_threads) { + rz_cons_printf(")\n"); + } else { + rz_cons_printf(" free)\n"); + } + } else { + pj_o(pj); + pj_kn(pj, "addr", (ut64)ta_addr); + pj_kn(pj, "last_rem", ta->GH(last_remainder)); + pj_kn(pj, "top", ta->GH(top)); + pj_kn(pj, "next", ta->GH(next)); + pj_ks(pj, "type", "thread"); + if (ta->attached_threads) { + pj_ks(pj, "state", "used"); + } else { + pj_ks(pj, "state", "free"); + } + pj_end(pj); + } + } } - - w = rz_cons_get_size(&h); - RzConsCanvas *can = rz_cons_canvas_new(w, h); - if (!can) { - free(cnk); - free(cnk_next); - rz_config_hold_free(hc); - return; + if (json) { + pj_end(pj); + pj_end(pj); + rz_cons_println(pj_string(pj)); + pj_free(pj); } + free(ta); +} - RzAGraph *g = rz_agraph_new(can); - if (!g) { - free(cnk); - free(cnk_next); - rz_cons_canvas_free(can); - rz_config_hold_restore(hc); - rz_config_hold_free(hc); - return; - } +void GH(print_inst_minfo)(GH(RzHeapInfo) * heap_info, GHT hinfo) { + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - RzANode *top = RZ_EMPTY, *chunk_node = RZ_EMPTY, *prev_node = RZ_EMPTY; - char *top_title, *top_data, *node_title, *node_data; - bool first_node = true; + PRINT_YA("malloc_info @ "); + PRINTF_BA("0x%" PFMT64x, (ut64)hinfo); + PRINT_YA(" {\n ar_ptr = "); + PRINTF_BA("0x%" PFMT64x "\n", (ut64)heap_info->ar_ptr); + PRINT_YA(" prev = "); + PRINTF_BA("0x%" PFMT64x "\n", (ut64)heap_info->prev); + PRINT_YA(" size = "); + PRINTF_BA("0x%" PFMT64x "\n", (ut64)heap_info->size); + PRINT_YA(" mprotect_size = "); + PRINTF_BA("0x%" PFMT64x "\n", (ut64)heap_info->mprotect_size); + PRINT_YA("}\n\n"); +} - top_data = rz_str_new(""); - top_title = rz_str_new(""); +void GH(print_malloc_info)(RzCore *core, GHT m_state, GHT malloc_state) { + GHT h_info; + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - (void)rz_io_read_at(core->io, next_chunk, (ut8 *)cnk, sizeof(GH(RzHeapChunk))); - size_tmp = (cnk->size >> 3) << 3; - ut64 prev_chunk_addr; - ut64 prev_chunk_size; - PJ *pj = NULL; + if (malloc_state == m_state) { + PRINT_RA("main_arena does not have an instance of malloc_info\n"); + } else if (GH(is_arena)(core, malloc_state, m_state)) { - switch (format_out) { - case 'j': - pj = pj_new(); - if (!pj) { + h_info = (malloc_state >> 16) << 16; + GH(RzHeapInfo) *heap_info = RZ_NEW0(GH(RzHeapInfo)); + if (!heap_info) { + return; + } + rz_io_read_at(core->io, h_info, (ut8 *)heap_info, sizeof(GH(RzHeapInfo))); + GH(print_inst_minfo) + (heap_info, h_info); + MallocState *ms = RZ_NEW0(MallocState); + if (!ms) { + free(heap_info); return; } - pj_o(pj); - pj_ka(pj, "chunks"); - break; - case '*': - rz_cons_printf("fs+heap.allocated\n"); - break; - case 'g': - can->linemode = rz_config_get_i(core->config, "graph.linemode"); - can->color = rz_config_get_i(core->config, "scr.color"); - core->cons->use_utf8 = rz_config_get_i(core->config, "scr.utf8"); - g->layout = rz_config_get_i(core->config, "graph.layout"); - rz_agraph_set_title(g, "Heap Layout"); - top_title = rz_str_newf("Top chunk @ 0x%" PFMT64x "\n", (ut64)main_arena->GH(top)); - case 'c': - case 'v': - rz_cons_printf("Arena @ "); - PRINTF_YA("0x%" PFMT64x, (ut64)m_state); - rz_cons_newline(); - } - while (next_chunk && next_chunk >= brk_start && next_chunk < main_arena->GH(top)) { - if (size_tmp < min_size || next_chunk + size_tmp > main_arena->GH(top)) { - const char *status = "corrupted"; - switch (format_out) { - case 'v': - GH(print_heap_chunk_simple) - (core, next_chunk, status); - rz_cons_newline(); - PRINTF_RA(" size: 0x%" PFMT64x "\n fd: 0x%" PFMT64x ", bk: 0x%" PFMT64x "\n", - (ut64)cnk->size, (ut64)cnk->fd, (ut64)cnk->bk); - int size = 0x10; - char *data = calloc(1, size); - if (data) { - rz_io_nread_at(core->io, (ut64)(next_chunk + SZ * 2), (ut8 *)data, size); - core->print->flags &= ~RZ_PRINT_FLAGS_HEADER; - core->print->pairs = false; - PRINT_GA(" "); - rz_print_hexdump(core->print, (ut64)(next_chunk + SZ * 2), (ut8 *)data, size, SZ * 2, 1, 1); - core->print->flags |= RZ_PRINT_FLAGS_HEADER; - core->print->pairs = true; - free(data); - } - break; - case 'c': - GH(print_heap_chunk_simple) - (core, next_chunk, status); - rz_cons_newline(); - PRINTF_RA(" size: 0x%" PFMT64x "\n fd: 0x%" PFMT64x ", bk: 0x%" PFMT64x "\n", - (ut64)cnk->size, (ut64)cnk->fd, (ut64)cnk->bk); - break; - case 'j': - pj_o(pj); - pj_kn(pj, "addr", next_chunk); - pj_kn(pj, "size", cnk->size); - pj_ks(pj, "status", status); - pj_kN(pj, "fd", cnk->fd); - pj_kN(pj, "bk", cnk->bk); - pj_end(pj); - break; - case '*': - rz_cons_printf("fs heap.corrupted\n"); - char *name = rz_str_newf("chunk.corrupted.%06" PFMT64x, ((prev_chunk >> 4) & 0xffffULL)); - rz_cons_printf("f %s %d 0x%" PFMT64x "\n", name, (int)cnk->size, (ut64)prev_chunk); - free(name); - break; - case 'g': - node_title = rz_str_newf(" Malloc chunk @ 0x%" PFMT64x " ", (ut64)prev_chunk); - node_data = rz_str_newf("[corrupted] size: 0x%" PFMT64x "\n fd: 0x%" PFMT64x ", bk: 0x%" PFMT64x - "\nHeap graph could not be recovered\n", - (ut64)cnk->size, (ut64)cnk->fd, (ut64)cnk->bk); - rz_agraph_add_node(g, node_title, node_data); - if (first_node) { - first_node = false; - } - break; + while (heap_info->prev != 0x0 && heap_info->prev != GHT_MAX) { + if (!GH(rz_heap_update_main_arena)(core, malloc_state, ms)) { + free(ms); + free(heap_info); + return; + } + if ((ms->GH(top) >> 16) << 16 != h_info) { + h_info = (ms->GH(top) >> 16) << 16; + rz_io_read_at(core->io, h_info, (ut8 *)heap_info, sizeof(GH(RzHeapInfo))); + GH(print_inst_minfo) + (heap_info, h_info); + } + } + free(heap_info); + free(ms); + } else { + PRINT_RA("This address is not part of the arenas\n"); + } +} + +/** + * \brief Get list of chunks of bin from NBINS array of an arena. + * \param core RzCore pointer + * \param main_arena MallocState struct of arena + * \param bin_num bin number of bin whose chunk list you want + * \return RzList of RzHeapChunkListItem for the bin + */ +RZ_API RzList *GH(rz_heap_bin_content_list)(RzCore *core, MallocState *main_arena, int bin_num) { + int idx = 2 * bin_num; + ut64 fw = main_arena->GH(bins)[idx]; + ut64 bk = main_arena->GH(bins)[idx + 1]; + RzList *chunks = rz_list_newf(free); + GH(RzHeapChunk) *head = RZ_NEW0(GH(RzHeapChunk)); + if (!head) { + return chunks; + } + + (void)rz_io_read_at(core->io, bk, (ut8 *)head, sizeof(GH(RzHeapChunk))); + + if (head->fd == fw) { + return chunks; + } + GH(RzHeapChunk) *cnk = RZ_NEW0(GH(RzHeapChunk)); + if (!cnk) { + return chunks; + } + GHT brk_start = GHT_MAX, brk_end = GHT_MAX, initial_brk = GHT_MAX; + GH(get_brks) + (core, &brk_start, &brk_end); + if (brk_start == GHT_MAX || brk_end == GHT_MAX) { + eprintf("No Heap section\n"); + return chunks; + } + const int tcache = rz_config_get_i(core->config, "dbg.glibc.tcache"); + if (tcache) { + const int fc_offset = rz_config_get_i(core->config, "dbg.glibc.fc_offset"); + initial_brk = ((brk_start >> 12) << 12) + fc_offset; + } else { + initial_brk = (brk_start >> 12) << 12; + } + while (fw != head->fd) { + if (fw > main_arena->GH(top) || fw < initial_brk) { + break; + } + rz_io_read_at(core->io, fw, (ut8 *)cnk, sizeof(GH(RzHeapChunk))); + RzHeapChunkListItem *chunk = malloc(sizeof(RzHeapChunkListItem)); + chunk->addr = fw; + rz_list_append(chunks, chunk); + fw = cnk->fd; + } + free(cnk); + free(head); + return chunks; +} +/** + * \brief Prints the heap chunks in a bin with double linked list (small|large|unsorted) + * \param core RzCore pointer + * \param main_arena MallocState struct for the arena in which bins are + * \param bin_num The bin number for the bin from which chunks have to printed + * \return number of chunks found in the bin + */ +static int GH(print_bin_content)(RzCore *core, MallocState *main_arena, int bin_num, PJ *pj) { + int idx = 2 * bin_num; + ut64 fw = main_arena->GH(bins)[idx]; + ut64 bk = main_arena->GH(bins)[idx + 1]; + RzListIter *iter; + RzHeapChunkListItem *pos; + RzList *chunks = GH(rz_heap_bin_content_list)(core, main_arena, bin_num); + if (rz_list_length(chunks) == 0) { + rz_list_free(chunks); + return 0; + } + int chunks_cnt = 0; + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + if (!pj) { + if (bin_num == 0) { + rz_cons_printf("Unsorted"); + } else if (bin_num >= 1 && bin_num <= NSMALLBINS - 1) { + rz_cons_printf("Small"); + } else if (bin_num >= NSMALLBINS && bin_num <= NBINS - 2) { + rz_cons_printf("Large"); + } + rz_cons_printf("_bin["); + PRINTF_BA("%d", bin_num); + rz_cons_printf("]: fd="); + PRINTF_YA("0x%" PFMT64x, fw); + rz_cons_printf(", bk="); + PRINTF_YA("0x%" PFMT64x, bk); + rz_cons_newline(); + } else { + pj_kn(pj, "fd", fw); + pj_kn(pj, "bk", bk); + pj_ka(pj, "chunks"); + } + + rz_list_foreach (chunks, iter, pos) { + if (!pj) { + rz_cons_printf(" -> "); + } + GH(print_heap_chunk_simple) + (core, pos->addr, NULL, pj); + if (!pj) { + rz_cons_newline(); + } + chunks_cnt += 1; + } + rz_list_free(chunks); + if (pj) { + pj_end(pj); + } + return chunks_cnt; +} + +/** + * \brief Prints unsorted bin description for an arena (used for `dmhd` command) + * \param core RzCore pointer + * \param m_arena Offset of the arena in memory + * \param main_arena MallocState struct for the arena in which bin are + */ +static void GH(print_unsortedbin_description)(RzCore *core, GHT m_arena, MallocState *main_arena, PJ *pj) { + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + if (!pj) { + rz_cons_printf("Unsorted bin in Arena @ "); + PRINTF_YA("0x%" PFMT64x "\n", (ut64)m_arena); + } + if (pj) { + pj_o(pj); + pj_kn(pj, "bin_num", 0); + pj_ks(pj, "bin_type", "unsorted"); + } + int chunk_cnt = GH(print_bin_content)(core, main_arena, 0, pj); + if (!pj) { + rz_cons_printf("Found %d chunks in unsorted bin\n", chunk_cnt); + } else { + pj_end(pj); + } +} + +/** + * \brief Prints small bins description for an arena (used for `dmhd` command) + * \param core RzCore pointer + * \param m_arena Offset of the arena in memory + * \param main_arena Pointer to MallocState struct for the arena in which bins are + */ +static void GH(print_smallbin_description)(RzCore *core, GHT m_arena, MallocState *main_arena, PJ *pj) { + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + if (!pj) { + rz_cons_printf("Small bins in Arena @ "); + PRINTF_YA("0x%" PFMT64x "\n", (ut64)m_arena); + } + int chunk_cnt = 0; + int non_empty_cnt = 0; + for (int bin_num = 1; bin_num < NSMALLBINS; bin_num++) { + if (pj) { + pj_o(pj); + pj_kn(pj, "bin_num", bin_num); + pj_ks(pj, "bin_type", "small"); + } + int chunk_found = GH(print_bin_content)(core, main_arena, bin_num, pj); + if (pj) { + pj_end(pj); + } + if (chunk_found > 0) { + non_empty_cnt += 1; + } + chunk_cnt += chunk_found; + } + if (!pj) { + rz_cons_printf("Found %d chunks in %d small bins\n", chunk_cnt, non_empty_cnt); + } +} + +/** + * \brief Prints large bins description for an arena (used for `dmhd` command) + * \param core RzCore pointer + * \param m_arena Offset of the arena in memory + * \param main_arena Pointer to MallocState struct for the arena in which bins are + */ +static void GH(print_largebin_description)(RzCore *core, GHT m_arena, MallocState *main_arena, PJ *pj) { + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + if (!pj) { + rz_cons_printf("Large bins in Arena @ "); + PRINTF_YA("0x%" PFMT64x "\n", (ut64)m_arena); + } + int chunk_cnt = 0; + int non_empty_cnt = 0; + for (int bin_num = NSMALLBINS; bin_num < NBINS - 2; bin_num++) { + if (pj) { + pj_o(pj); + pj_kn(pj, "bin_num", bin_num); + pj_ks(pj, "bin_type", "large"); + } + int chunk_found = GH(print_bin_content)(core, main_arena, bin_num, pj); + if (pj) { + pj_end(pj); + } + if (chunk_found > 0) { + non_empty_cnt += 1; + } + chunk_cnt += chunk_found; + } + if (!pj) { + rz_cons_printf("Found %d chunks in %d large bins\n", chunk_cnt, non_empty_cnt); + } +} + +/** + * \brief Prints description of bins for main arena for `dmhd` command + * \param core RzCore pointer + * \param m_arena Offset of main arena in memory + * \param main_arena Pointer to Malloc state struct for main arena + * \param global_max_fast The largest fast bin size + * \param format Enum to determine which type of bins to print. + */ +static void GH(print_main_arena_bins)(RzCore *core, GHT m_arena, MallocState *main_arena, GHT global_max_fast, RzHeapBinType format, bool json) { + rz_return_if_fail(core && core->dbg && core->dbg->maps); + PJ *pj = NULL; + if (json) { + pj = pj_new(); + pj_o(pj); + pj_ka(pj, "bins"); + } + if (format == RZ_HEAP_BIN_ANY || format == RZ_HEAP_BIN_TCACHE) { + bool main_thread_only = true; + GH(print_tcache_instance) + (core, m_arena, main_arena, main_thread_only, pj); + rz_cons_newline(); + } + if (format == RZ_HEAP_BIN_ANY || format == RZ_HEAP_BIN_FAST) { + char *input = malloc(sizeof(char) * 1); + input[0] = '\0'; + bool main_arena_only = true; + GH(print_heap_fastbin) + (core, m_arena, main_arena, global_max_fast, input, main_arena_only, pj); + free(input); + rz_cons_newline(); + } + if (format == RZ_HEAP_BIN_ANY || format == RZ_HEAP_BIN_UNSORTED) { + GH(print_unsortedbin_description) + (core, m_arena, main_arena, pj); + rz_cons_newline(); + } + if (format == RZ_HEAP_BIN_ANY || format == RZ_HEAP_BIN_SMALL) { + GH(print_smallbin_description) + (core, m_arena, main_arena, pj); + rz_cons_newline(); + } + if (format == RZ_HEAP_BIN_ANY || format == RZ_HEAP_BIN_LARGE) { + GH(print_largebin_description) + (core, m_arena, main_arena, pj); + rz_cons_newline(); + } + if (json) { + pj_end(pj); + pj_end(pj); + rz_cons_println(pj_string(pj)); + pj_free(pj); + } +} + +/** + * \brief Get a list of MallocState structs for all the arenas + * \param core RzCore pointer + * \param m_arena Base address of MallocState struct of main arena + * \param main_arena MallocState struct of main arena + * \return RzList pointer for list of MallocState structs of all the arenas + */ +RZ_API RzList *GH(rz_heap_arenas_list)(RzCore *core, GHT m_arena, MallocState *main_arena) { + RzList *arena_list = rz_list_newf(free); + MallocState *ta = RZ_NEW0(MallocState); + if (!ta) { + return arena_list; + } + // main arena + GH(rz_heap_update_main_arena) + (core, m_arena, ta); + rz_list_append(arena_list, ta); + if (main_arena->GH(next) != m_arena) { + ta->GH(next) = main_arena->GH(next); + while (GH(is_arena)(core, m_arena, ta->GH(next)) && ta->GH(next) != m_arena) { + ut64 ta_addr = ta->GH(next); + ta = RZ_NEW0(MallocState); + if (!GH(rz_heap_update_main_arena)(core, ta_addr, ta)) { + return arena_list; + } + // thread arenas + rz_list_append(arena_list, ta); + } + } + return arena_list; +} + +/** + * \brief Get a list of all the heap chunks in an arena. The chunks are in form of a struct RzHeapChunkListItem + * \param core RzCore pointer + * \param main_arena MallocState struct of main arena + * \param m_arena Base address of malloc state of main arena + * \param m_state Base address of malloc state of the arena whose chunks are required + * \param global_max_fast Max size of fastbin + * \return RzList pointer for list of all chunks in a given arena + */ +RZ_API RzList *GH(rz_heap_chunks_list)(RzCore *core, MallocState *main_arena, + GHT m_arena, GHT m_state) { + RzList *chunks = rz_list_newf(free); + if (!core || !core->dbg || !core->dbg->maps) { + return chunks; + } + GHT global_max_fast = (64 * SZ / 4); + GHT brk_start = GHT_MAX, brk_end = GHT_MAX, size_tmp, min_size = SZ * 4; + GHT tcache_fd = GHT_MAX, tcache_tmp = GHT_MAX; + GHT initial_brk = GHT_MAX, tcache_initial_brk = GHT_MAX; + + const int tcache = rz_config_get_i(core->config, "dbg.glibc.tcache"); + const int offset = rz_config_get_i(core->config, "dbg.glibc.fc_offset"); + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + int glibc_version = core->dbg->glibc_version; + + if (m_arena == m_state) { + GH(get_brks) + (core, &brk_start, &brk_end); + if (tcache) { + initial_brk = ((brk_start >> 12) << 12) + GH(HDR_SZ); + if (rz_config_get_b(core->config, "cfg.debug")) { + tcache_initial_brk = initial_brk; } + initial_brk += (glibc_version < 230) + ? sizeof(GH(RzHeapTcachePre230)) + : sizeof(GH(RzHeapTcache)); + } else { + initial_brk = (brk_start >> 12) << 12; + } + } else { + brk_start = ((m_state >> 16) << 16); + brk_end = brk_start + main_arena->GH(system_mem); + if (tcache) { + tcache_initial_brk = brk_start + sizeof(GH(RzHeapInfo)) + sizeof(GH(RzHeap_MallocState_tcache)) + GH(MMAP_ALIGN); + initial_brk = tcache_initial_brk + offset; + } else { + initial_brk = brk_start + sizeof(GH(RzHeapInfo)) + sizeof(GH(RzHeap_MallocState)) + MMAP_OFFSET; + } + } + + if (brk_start == GHT_MAX || brk_end == GHT_MAX || initial_brk == GHT_MAX) { + eprintf("No Heap section\n"); + return chunks; + } + + GHT next_chunk = initial_brk, prev_chunk = next_chunk; + GH(RzHeapChunk) *cnk = RZ_NEW0(GH(RzHeapChunk)); + if (!cnk) { + return chunks; + } + GH(RzHeapChunk) *cnk_next = RZ_NEW0(GH(RzHeapChunk)); + if (!cnk_next) { + free(cnk); + return chunks; + } + + RzConfigHold *hc = rz_config_hold_new(core->config); + if (!hc) { + free(cnk); + free(cnk_next); + return chunks; + } + + (void)rz_io_read_at(core->io, next_chunk, (ut8 *)cnk, sizeof(GH(RzHeapChunk))); + size_tmp = (cnk->size >> 3) << 3; + ut64 prev_chunk_addr; + ut64 prev_chunk_size; + while (next_chunk && next_chunk >= brk_start && next_chunk < main_arena->GH(top)) { + if (size_tmp < min_size || next_chunk + size_tmp > main_arena->GH(top)) { + RzHeapChunkListItem *block = malloc(sizeof(RzHeapChunkListItem)); + block->addr = next_chunk; + block->status = "corrupted"; + rz_list_append(chunks, block); break; } prev_chunk_addr = (ut64)prev_chunk; prev_chunk_size = (((ut64)cnk->size) >> 3) << 3; - bool fastbin = size_tmp >= SZ * 4 && size_tmp <= global_max_fast; bool is_free = false, double_free = false; @@ -1343,13 +1775,11 @@ static void GH(print_heap_segment)(RzCore *core, MallocState *main_arena, if (tcache) { GH(RTcache) *tcache_heap = GH(tcache_new)(core); if (!tcache_heap) { - rz_cons_canvas_free(can); rz_config_hold_restore(hc); rz_config_hold_free(hc); - free(g); free(cnk); free(cnk_next); - return; + return chunks; } GH(tcache_read) (core, tcache_initial_brk, tcache_heap); @@ -1391,7 +1821,7 @@ static void GH(print_heap_segment)(RzCore *core, MallocState *main_arena, rz_io_read_at(core->io, next_chunk, (ut8 *)cnk, sizeof(GH(RzHeapChunk))); size_tmp = (cnk->size >> 3) << 3; - const char *status = "allocated"; + char *status = "allocated"; if (fastbin) { if (is_free) { status = "free"; @@ -1406,45 +1836,185 @@ static void GH(print_heap_segment)(RzCore *core, MallocState *main_arena, } } - switch (format_out) { - case 'c': - GH(print_heap_chunk_simple) - (core, prev_chunk_addr, status); - rz_cons_newline(); - break; - case 'v': + RzHeapChunkListItem *block = malloc(sizeof(RzHeapChunkListItem)); + block->addr = prev_chunk_addr; + block->status = status; + block->size = prev_chunk_size; + rz_list_append(chunks, block); + } + free(cnk); + free(cnk_next); + return chunks; +} + +RZ_IPI RzCmdStatus GH(rz_cmd_arena_print_handler)(RzCore *core, int argc, const char **argv) { + GHT m_arena = GHT_MAX; + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + MallocState *main_arena = RZ_NEW0(MallocState); + if (!main_arena) { + return RZ_CMD_STATUS_ERROR; + } + if (!GH(rz_heap_resolve_main_arena)(core, &m_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + if (!GH(rz_heap_update_main_arena)(core, m_arena, main_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + RzList *arenas_list = GH(rz_heap_arenas_list)(core, m_arena, main_arena); + RzListIter *iter, *iter2; + MallocState *pos; + ut64 addr; + rz_list_foreach (arenas_list, iter2, pos) { + addr = pos->GH(next); + } + bool flag = 0; + rz_list_foreach (arenas_list, iter, pos) { + if (!flag) { + flag = true; + rz_cons_printf("Main arena (addr="); + + } else { + rz_cons_printf("Thread arena(addr="); + } + PRINTF_YA("0x%" PFMT64x, (ut64)addr); + rz_cons_printf(", lastRemainder="); + PRINTF_YA("0x%" PFMT64x, (ut64)pos->GH(last_remainder)); + rz_cons_printf(", top="); + PRINTF_YA("0x%" PFMT64x, (ut64)pos->GH(top)); + rz_cons_printf(", next="); + PRINTF_YA("0x%" PFMT64x, (ut64)pos->GH(next)); + if (pos->attached_threads) { + rz_cons_printf(")\n"); + } else { + rz_cons_printf(", free)\n"); + } + addr = (ut64)pos->GH(next); + } + rz_list_free(arenas_list); + free(main_arena); + return RZ_CMD_STATUS_OK; +} + +RZ_IPI RzCmdStatus GH(rz_cmd_heap_chunks_print_handler)(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) { + GHT m_arena = GHT_MAX, m_state = GHT_MAX; + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + MallocState *main_arena = RZ_NEW0(MallocState); + RzOutputMode mode = state->mode; + if (!main_arena) { + return RZ_CMD_STATUS_ERROR; + } + if (!GH(rz_heap_resolve_main_arena)(core, &m_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + if (argc == 1) { + m_state = m_arena; + } else if (argc == 2) { + m_state = rz_num_get(NULL, argv[1]); + } + if (!GH(is_arena)(core, m_arena, m_state)) { + free(main_arena); + PRINT_RA("This address is not a valid arena\n"); + return RZ_CMD_STATUS_ERROR; + } + if (!GH(rz_heap_update_main_arena)(core, m_state, main_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + GHT brk_start, brk_end; + if (m_arena == m_state) { + GH(get_brks) + (core, &brk_start, &brk_end); + + } else { + brk_start = ((m_state >> 16) << 16); + brk_end = brk_start + main_arena->GH(system_mem); + } + RzList *chunks = GH(rz_heap_chunks_list)(core, main_arena, m_arena, m_state); + RzListIter *iter; + RzHeapChunkListItem *pos; + PJ *pj = state->d.pj; + int w, h; + RzConfigHold *hc = rz_config_hold_new(core->config); + if (!hc) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + w = rz_cons_get_size(&h); + RzConsCanvas *can = rz_cons_canvas_new(w, h); + if (!can) { + free(main_arena); + rz_config_hold_free(hc); + return RZ_CMD_STATUS_ERROR; + } + + RzAGraph *g = rz_agraph_new(can); + if (!g) { + free(main_arena); + rz_cons_canvas_free(can); + rz_config_hold_restore(hc); + rz_config_hold_free(hc); + return RZ_CMD_STATUS_ERROR; + } + RzANode *top = RZ_EMPTY, *chunk_node = RZ_EMPTY, *prev_node = RZ_EMPTY; + char *top_title, *top_data, *node_title, *node_data; + bool first_node = true; + top_data = rz_str_new(""); + top_title = rz_str_new(""); + if (mode == RZ_OUTPUT_MODE_JSON) { + if (!pj) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + pj_o(pj); + pj_ka(pj, "chunks"); + } else if (mode == RZ_OUTPUT_MODE_STANDARD || mode == RZ_OUTPUT_MODE_LONG) { + rz_cons_printf("Arena @ "); + PRINTF_YA("0x%" PFMT64x, (ut64)m_state); + rz_cons_newline(); + } else if (mode == RZ_OUTPUT_MODE_LONG_JSON) { + can->linemode = rz_config_get_i(core->config, "graph.linemode"); + can->color = rz_config_get_i(core->config, "scr.color"); + core->cons->use_utf8 = rz_config_get_i(core->config, "scr.utf8"); + g->layout = rz_config_get_i(core->config, "graph.layout"); + rz_agraph_set_title(g, "Heap Layout"); + top_title = rz_str_newf("Top chunk @ 0x%" PFMT64x "\n", (ut64)main_arena->GH(top)); + } + rz_list_foreach (chunks, iter, pos) { + if (mode == RZ_OUTPUT_MODE_STANDARD || mode == RZ_OUTPUT_MODE_LONG) { GH(print_heap_chunk_simple) - (core, prev_chunk_addr, status); + (core, pos->addr, pos->status, NULL); rz_cons_newline(); - int size = 0x10; - char *data = calloc(1, size); - if (data) { - rz_io_nread_at(core->io, (ut64)(prev_chunk_addr + SZ * 2), (ut8 *)data, size); - core->print->flags &= ~RZ_PRINT_FLAGS_HEADER; - core->print->pairs = false; - rz_cons_printf(" "); - rz_print_hexdump(core->print, (ut64)(prev_chunk_addr + SZ * 2), (ut8 *)data, size, SZ * 2, 1, 1); - core->print->flags |= RZ_PRINT_FLAGS_HEADER; - core->print->pairs = true; - free(data); + if (mode == RZ_OUTPUT_MODE_LONG) { + int size = 0x10; + char *data = calloc(1, size); + if (data) { + rz_io_nread_at(core->io, (ut64)(pos->addr + SZ * 2), (ut8 *)data, size); + core->print->flags &= ~RZ_PRINT_FLAGS_HEADER; + core->print->pairs = false; + rz_cons_printf(" "); + rz_print_hexdump(core->print, (ut64)(pos->addr + SZ * 2), (ut8 *)data, size, SZ * 2, 1, 1); + core->print->flags |= RZ_PRINT_FLAGS_HEADER; + core->print->pairs = true; + free(data); + } } - break; - case 'j': + } else if (mode == RZ_OUTPUT_MODE_JSON) { pj_o(pj); - pj_kn(pj, "addr", prev_chunk_addr); - pj_kn(pj, "size", prev_chunk_size); - pj_ks(pj, "status", status); + pj_kn(pj, "addr", pos->addr); + pj_kn(pj, "size", pos->size); + pj_ks(pj, "status", pos->status); pj_end(pj); - break; - case '*': - rz_cons_printf("fs heap.%s\n", status); - char *name = rz_str_newf("chunk.%06" PFMT64x, ((prev_chunk_addr >> 4) & 0xffffULL)); - rz_cons_printf("f %s %d 0x%" PFMT64x "\n", name, (int)prev_chunk_size, (ut64)prev_chunk_addr); + } else if (mode == RZ_OUTPUT_MODE_RIZIN) { + rz_cons_printf("fs heap.%s\n", pos->status); + char *name = rz_str_newf("chunk.%06" PFMT64x, ((pos->addr >> 4) & 0xffffULL)); + rz_cons_printf("f %s %d 0x%" PFMT64x "\n", name, (int)pos->size, (ut64)pos->addr); free(name); - break; - case 'g': - node_title = rz_str_newf(" Malloc chunk @ 0x%" PFMT64x " ", (ut64)prev_chunk_addr); - node_data = rz_str_newf("size: 0x%" PFMT64x " status: %s\n", (ut64)prev_chunk_size, status); + } else if (mode == RZ_OUTPUT_MODE_LONG_JSON) { // graph + node_title = rz_str_newf(" Malloc chunk @ 0x%" PFMT64x " ", (ut64)pos->addr); + node_data = rz_str_newf("size: 0x%" PFMT64x " status: %s\n", (ut64)pos->size, pos->status); chunk_node = rz_agraph_add_node(g, node_title, node_data); if (first_node) { first_node = false; @@ -1452,586 +2022,285 @@ static void GH(print_heap_segment)(RzCore *core, MallocState *main_arena, rz_agraph_add_edge(g, prev_node, chunk_node); } prev_node = chunk_node; - break; } } - - switch (format_out) { - case 'v': - case 'c': + if (mode == RZ_OUTPUT_MODE_STANDARD || mode == RZ_OUTPUT_MODE_LONG) { GH(print_heap_chunk_simple) - (core, main_arena->GH(top), "free"); + (core, main_arena->GH(top), "free", NULL); PRINT_RA("[top]"); rz_cons_printf("[brk_start: "); PRINTF_YA("0x%" PFMT64x, (ut64)brk_start); rz_cons_printf(", brk_end: "); PRINTF_YA("0x%" PFMT64x, (ut64)brk_end); - rz_cons_printf("]\n"); - break; - case 'j': + rz_cons_printf("]"); + } else if (mode == RZ_OUTPUT_MODE_JSON) { pj_end(pj); pj_kn(pj, "top", main_arena->GH(top)); pj_kn(pj, "brk", brk_start); pj_kn(pj, "end", brk_end); pj_end(pj); - rz_cons_print(pj_string(pj)); - pj_free(pj); - break; - case '*': + } else if (mode == RZ_OUTPUT_MODE_RIZIN) { rz_cons_printf("fs-\n"); rz_cons_printf("f heap.top = 0x%08" PFMT64x "\n", (ut64)main_arena->GH(top)); rz_cons_printf("f heap.brk = 0x%08" PFMT64x "\n", (ut64)brk_start); rz_cons_printf("f heap.end = 0x%08" PFMT64x "\n", (ut64)brk_end); - break; - case 'g': + } else if (mode == RZ_OUTPUT_MODE_LONG_JSON) { top = rz_agraph_add_node(g, top_title, top_data); if (!first_node) { rz_agraph_add_edge(g, prev_node, top); free(node_data); free(node_title); } - rz_agraph_print(g); - rz_cons_canvas_free(can); - rz_config_hold_restore(hc); - rz_config_hold_free(hc); - break; - } - - rz_cons_printf("\n"); - free(g); - free(top_data); - free(top_title); - free(cnk); - free(cnk_next); -} - -void GH(print_malloc_states)(RzCore *core, GHT m_arena, MallocState *main_arena) { - MallocState *ta = RZ_NEW0(MallocState); - RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - - if (!ta) { - return; - } - PRINT_YA("main_arena @ "); - PRINTF_BA("0x%" PFMT64x "\n", (ut64)m_arena); - if (main_arena->GH(next) != m_arena) { - ta->GH(next) = main_arena->GH(next); - while (GH(is_arena)(core, m_arena, ta->GH(next)) && ta->GH(next) != m_arena) { - PRINT_YA("thread arena @ "); - PRINTF_BA("0x%" PFMT64x, (ut64)ta->GH(next)); - if (!GH(update_main_arena)(core, ta->GH(next), ta)) { - free(ta); - return; - } - if (ta->attached_threads) { - PRINT_BA("\n"); - } else { - PRINT_GA(" free\n"); - } - } - } - free(ta); -} - -void GH(print_inst_minfo)(GH(RzHeapInfo) * heap_info, GHT hinfo) { - RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - - PRINT_YA("malloc_info @ "); - PRINTF_BA("0x%" PFMT64x, (ut64)hinfo); - PRINT_YA(" {\n ar_ptr = "); - PRINTF_BA("0x%" PFMT64x "\n", (ut64)heap_info->ar_ptr); - PRINT_YA(" prev = "); - PRINTF_BA("0x%" PFMT64x "\n", (ut64)heap_info->prev); - PRINT_YA(" size = "); - PRINTF_BA("0x%" PFMT64x "\n", (ut64)heap_info->size); - PRINT_YA(" mprotect_size = "); - PRINTF_BA("0x%" PFMT64x "\n", (ut64)heap_info->mprotect_size); - PRINT_YA("}\n\n"); -} - -void GH(print_malloc_info)(RzCore *core, GHT m_state, GHT malloc_state) { - GHT h_info; - RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - - if (malloc_state == m_state) { - PRINT_RA("main_arena does not have an instance of malloc_info\n"); - } else if (GH(is_arena)(core, malloc_state, m_state)) { - - h_info = (malloc_state >> 16) << 16; - GH(RzHeapInfo) *heap_info = RZ_NEW0(GH(RzHeapInfo)); - if (!heap_info) { - return; - } - rz_io_read_at(core->io, h_info, (ut8 *)heap_info, sizeof(GH(RzHeapInfo))); - GH(print_inst_minfo) - (heap_info, h_info); - MallocState *ms = RZ_NEW0(MallocState); - if (!ms) { - free(heap_info); - return; - } - - while (heap_info->prev != 0x0 && heap_info->prev != GHT_MAX) { - if (!GH(update_main_arena)(core, malloc_state, ms)) { - free(ms); - free(heap_info); - return; - } - if ((ms->GH(top) >> 16) << 16 != h_info) { - h_info = (ms->GH(top) >> 16) << 16; - rz_io_read_at(core->io, h_info, (ut8 *)heap_info, sizeof(GH(RzHeapInfo))); - GH(print_inst_minfo) - (heap_info, h_info); - } - } - free(heap_info); - free(ms); - } else { - PRINT_RA("This address is not part of the arenas\n"); + rz_agraph_print(g); + rz_cons_canvas_free(can); + rz_config_hold_restore(hc); + rz_config_hold_free(hc); } + rz_cons_newline(); + free(g); + free(top_data); + free(top_title); + rz_list_free(chunks); + free(main_arena); + return RZ_CMD_STATUS_OK; } -/** - * \brief Prints the heap chunks in a bin with double linked list (small|large|unsorted) - * \param core RzCore pointer - * \param main_arena MallocState struct for the arena in which bins are - * \param bin_num The bin number for the bin from which chunks have to printed - * \return number of chunks found in the bin - */ -static int GH(print_bin_content)(RzCore *core, MallocState *main_arena, int bin_num) { - int idx = 2 * bin_num; - ut64 fw = main_arena->GH(bins)[idx]; - ut64 bk = main_arena->GH(bins)[idx + 1]; - - GH(RzHeapChunk) *head = RZ_NEW0(GH(RzHeapChunk)); - if (!head) { - return 0; - } +RZ_IPI RzCmdStatus GH(rz_cmd_main_arena_print_handler)(RzCore *core, int argc, const char **argv, RzOutputMode mode) { + GHT m_arena = GHT_MAX, m_state = GHT_MAX; RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - (void)rz_io_read_at(core->io, bk, (ut8 *)head, sizeof(GH(RzHeapChunk))); - - size_t chunks_cnt = 0; - if (head->fd == fw) { - return chunks_cnt; - } - if (bin_num == 0) { - rz_cons_printf("Unsorted"); - } else if (bin_num >= 1 && bin_num <= NSMALLBINS - 1) { - rz_cons_printf("Small"); - } else if (bin_num >= NSMALLBINS && bin_num <= NBINS - 2) { - rz_cons_printf("Large"); - } - rz_cons_printf("_bin["); - PRINTF_BA("%d", bin_num); - rz_cons_printf("]: fd="); - PRINTF_YA("0x%" PFMT64x, fw); - rz_cons_printf(", bk="); - PRINTF_YA("0x%" PFMT64x, bk); - rz_cons_newline(); - GH(RzHeapChunk) *cnk = RZ_NEW0(GH(RzHeapChunk)); - - if (!cnk) { - return 0; + GHT global_max_fast = (64 * SZ / 4); + MallocState *main_arena = RZ_NEW0(MallocState); + if (!main_arena) { + return RZ_CMD_STATUS_ERROR; } - - while (fw != head->fd) { - rz_io_read_at(core->io, fw, (ut8 *)cnk, sizeof(GH(RzHeapChunk))); - rz_cons_printf(" -> "); - GH(print_heap_chunk_simple) - (core, fw, NULL); - rz_cons_newline(); - fw = cnk->fd; - chunks_cnt += 1; + if (!GH(rz_heap_resolve_main_arena)(core, &m_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; } - free(cnk); - free(head); - - return chunks_cnt; -} - -/** - * \brief Prints unsorted bin description for an arena (used for `dmhd` command) - * \param core RzCore pointer - * \param m_arena Offset of the arena in memory - * \param main_arena MallocState struct for the arena in which bin are - */ -static void GH(print_unsortedbin_description)(RzCore *core, GHT m_arena, MallocState *main_arena) { - RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - rz_cons_printf("Unsorted bin in Arena @ "); - PRINTF_YA("0x%" PFMT64x "\n", (ut64)m_arena); - int chunk_cnt = GH(print_bin_content)(core, main_arena, 0); - rz_cons_printf("Found %d chunks in unsorted bin\n", chunk_cnt); -} - -/** - * \brief Prints small bins description for an arena (used for `dmhd` command) - * \param core RzCore pointer - * \param m_arena Offset of the arena in memory - * \param main_arena Pointer to MallocState struct for the arena in which bins are - */ -static void GH(print_smallbin_description)(RzCore *core, GHT m_arena, MallocState *main_arena) { - RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - rz_cons_printf("Small bins in Arena @ "); - PRINTF_YA("0x%" PFMT64x "\n", (ut64)m_arena); - int chunk_cnt = 0; - int non_empty_cnt = 0; - for (int bin_num = 1; bin_num < NSMALLBINS; bin_num++) { - int chunk_found = GH(print_bin_content)(core, main_arena, bin_num); - if (chunk_found > 0) { - non_empty_cnt += 1; - } - chunk_cnt += chunk_found; + if (argc == 1) { + m_state = m_arena; + } else if (argc == 2) { + m_state = rz_num_get(NULL, argv[1]); + } + if (!GH(is_arena)(core, m_arena, m_state)) { + PRINT_RA("This address is not a valid arena\n"); + free(main_arena); + return RZ_CMD_STATUS_ERROR; } - rz_cons_printf("Found %d chunks in %d small bins\n", chunk_cnt, non_empty_cnt); + if (!GH(rz_heap_update_main_arena)(core, m_state, main_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + GH(print_arena_stats) + (core, m_state, main_arena, global_max_fast, mode); + free(main_arena); + return RZ_CMD_STATUS_OK; } -/** - * \brief Prints large bins description for an arena (used for `dmhd` command) - * \param core RzCore pointer - * \param m_arena Offset of the arena in memory - * \param main_arena Pointer to MallocState struct for the arena in which bins are - */ -static void GH(print_largebin_description)(RzCore *core, GHT m_arena, MallocState *main_arena) { - RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - rz_cons_printf("Large bins in Arena @ "); - PRINTF_YA("0x%" PFMT64x "\n", (ut64)m_arena); - int chunk_cnt = 0; - int non_empty_cnt = 0; - for (int bin_num = NSMALLBINS; bin_num < NBINS - 2; bin_num++) { - int chunk_found = GH(print_bin_content)(core, main_arena, bin_num); - if (chunk_found > 0) { - non_empty_cnt += 1; - } - chunk_cnt += chunk_found; +RZ_IPI RzCmdStatus GH(rz_cmd_heap_chunk_print_handler)(RzCore *core, int argc, const char **argv) { + GHT m_arena = GHT_MAX; + MallocState *main_arena = RZ_NEW0(MallocState); + if (!main_arena) { + return RZ_CMD_STATUS_ERROR; } - rz_cons_printf("Found %d chunks in %d large bins\n", chunk_cnt, non_empty_cnt); + if (!GH(rz_heap_resolve_main_arena)(core, &m_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + ut64 addr = core->offset; + GH(print_heap_chunk) + (core, addr); + free(main_arena); + return RZ_CMD_STATUS_OK; } -/** - * \brief Prints description of bins for main arena for `dmhd` command - * \param core RzCore pointer - * \param m_arena Offset of main arena in memory - * \param main_arena Pointer to Malloc state struct for main arena - * \param global_max_fast The largest fast bin size (used for formatting) - * \param format Enum to determine which type of bins to print. - */ -static void GH(print_main_arena_bins)(RzCore *core, GHT m_arena, MallocState *main_arena, GHT global_max_fast, RzHeapBinType format) { - rz_return_if_fail(core && core->dbg && core->dbg->maps); - if (format == RZ_HEAP_BIN_ANY || format == RZ_HEAP_BIN_TCACHE) { - bool main_thread_only = true; - GH(print_tcache_instance) - (core, m_arena, main_arena, main_thread_only); - rz_cons_newline(); +RZ_IPI RzCmdStatus GH(rz_cmd_heap_info_print_handler)(RzCore *core, int argc, const char **argv) { + GHT m_arena = GHT_MAX, m_state = GHT_MAX; + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + MallocState *main_arena = RZ_NEW0(MallocState); + if (!main_arena) { + return RZ_CMD_STATUS_ERROR; } - if (format == RZ_HEAP_BIN_ANY || format == RZ_HEAP_BIN_FAST) { - char *input = malloc(sizeof(char) * 1); - input[0] = '\0'; - bool main_arena_only = true; - GH(print_heap_fastbin) - (core, m_arena, main_arena, global_max_fast, input, main_arena_only); - free(input); - rz_cons_newline(); + if (!GH(rz_heap_resolve_main_arena)(core, &m_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; } - if (format == RZ_HEAP_BIN_ANY || format == RZ_HEAP_BIN_UNSORTED) { - GH(print_unsortedbin_description) - (core, m_arena, main_arena); - rz_cons_newline(); + if (argc == 1) { + m_state = m_arena; + } else if (argc == 2) { + m_state = rz_num_get(NULL, argv[1]); } - if (format == RZ_HEAP_BIN_ANY || format == RZ_HEAP_BIN_SMALL) { - GH(print_smallbin_description) - (core, m_arena, main_arena); - rz_cons_newline(); + if (!GH(is_arena)(core, m_arena, m_state)) { + PRINT_RA("This address is not a valid arena\n"); + free(main_arena); + return RZ_CMD_STATUS_ERROR; } - if (format == RZ_HEAP_BIN_ANY || format == RZ_HEAP_BIN_LARGE) { - GH(print_largebin_description) - (core, m_arena, main_arena); - rz_cons_newline(); + if (!GH(rz_heap_update_main_arena)(core, m_state, main_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; } + GH(print_malloc_info) + (core, m_arena, m_state); + free(main_arena); + return RZ_CMD_STATUS_OK; } -static const char *GH(help_msg)[] = { - "Usage:", " dmh", " # Memory map heap", - "dmh", "", "List the chunks inside the heap segment", - "dmh", " @[malloc_state]", "List heap chunks of a particular arena", - "dmha", "", "List all malloc_state instances in application", - "dmhb", " @[malloc_state]", "Display all parsed Double linked list of main_arena's or a particular arena bins instance", - "dmhb", " [bin_num|bin_num:malloc_state]", "Display parsed double linked list of bins instance from a particular arena", - "dmhbg", " [bin_num]", "Display double linked list graph of main_arena's bin [Under development]", - "dmhc", " @[chunk_addr]", "Display malloc_chunk struct for a given malloc chunk", - "dmhd", " [tcache|unsorted|fast|small|large]", "Display description of bins in the main_arena", - "dmhf", " @[malloc_state]", "Display all parsed fastbins of main_arena's or a particular arena fastbinY instance", - "dmhf", " [fastbin_num|fastbin_num:malloc_state]", "Display parsed single linked list in fastbinY instance from a particular arena", - "dmhg", "", "Display heap graph of heap segment", - "dmhg", " [malloc_state]", "Display heap graph of a particular arena", - "dmhi", " @[malloc_state]", "Display heap_info structure/structures for a given arena", - "dmhj", "", "List the chunks inside the heap segment in JSON format", - "dmhm", "", "List all elements of struct malloc_state of main thread (main_arena)", - "dmhm", " @[malloc_state]", "List all malloc_state instance of a particular arena", - "dmht", "", "Display all parsed thread cache bins of all arena's tcache instance", - "dmhv", " @[malloc_state]", "List heap chunks of a particular arena along with hexdump of first 0x10 bytes", - "dmh?", "", "Show map heap help", - NULL -}; - -static int GH(cmd_dbg_map_heap_glibc)(RzCore *core, const char *input) { - static GHT m_arena = GHT_MAX, m_state = GHT_MAX; - RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - - GHT global_max_fast = (64 * SZ / 4); - +RZ_IPI RzCmdStatus GH(rz_cmd_heap_tcache_print_handler)(RzCore *core, int argc, const char **argv) { + GHT m_arena = GHT_MAX; MallocState *main_arena = RZ_NEW0(MallocState); if (!main_arena) { - return false; + return RZ_CMD_STATUS_ERROR; } + if (!GH(rz_heap_resolve_main_arena)(core, &m_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + if (!GH(rz_heap_update_main_arena)(core, m_arena, main_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + bool main_thread_only = false; + GH(print_tcache_instance) + (core, m_arena, main_arena, main_thread_only, NULL); + free(main_arena); + return RZ_CMD_STATUS_OK; +} - rz_config_set_i(core->config, "dbg.glibc.tcache", GH(is_tcache)(core)); - - int format = 'c'; - bool get_state = false; - - switch (input[0]) { - case ' ': // dmh [malloc_state] - m_state = rz_num_get(NULL, input); - get_state = true; - case '\0': // dmh - if (GH(rz_resolve_main_arena)(core, &m_arena)) { - - if (core->offset != core->prompt_offset) { - m_state = core->offset; - } else { - if (!get_state) { - m_state = m_arena; - } - } - if (GH(is_arena)(core, m_arena, m_state)) { - if (!GH(update_main_arena)(core, m_state, main_arena)) { - break; - } - GH(print_heap_segment) - (core, main_arena, m_arena, m_state, global_max_fast, format); - break; - } else { - PRINT_RA("This address is not part of the arenas\n"); - break; - } - } - break; - case 'a': // dmha - if (GH(rz_resolve_main_arena)(core, &m_arena)) { - if (!GH(update_main_arena)(core, m_arena, main_arena)) { - break; - } - GH(print_malloc_states) - (core, m_arena, main_arena); - } - break; - case 'i': // dmhi - if (GH(rz_resolve_main_arena)(core, &m_arena)) { - if (!GH(update_main_arena)(core, m_arena, main_arena)) { - break; - } - input += 1; - if (!strcmp(input, "\0")) { - if (core->offset != core->prompt_offset) { - m_state = core->offset; - } - } else { - m_state = rz_num_get(NULL, input); - } - GH(print_malloc_info) - (core, m_arena, m_state); +RZ_IPI int GH(rz_cmd_heap_bins_list_print)(RzCore *core, const char *input) { + GHT m_arena = GHT_MAX, m_state = GHT_MAX; + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + MallocState *main_arena = RZ_NEW0(MallocState); + if (!GH(rz_heap_resolve_main_arena)(core, &m_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + char *m_state_str, *dup = strdup(input); + if (*dup) { + strtok(dup, ":"); + m_state_str = strtok(NULL, ":"); + m_state = rz_num_get(NULL, m_state_str); + if (!m_state) { + m_state = m_arena; } - break; - case 'm': // "dmhm" - if (GH(rz_resolve_main_arena)(core, &m_arena)) { - - switch (input[1]) { - case '*': - format = '*'; - input += 1; - break; - case 'j': - format = 'j'; - input += 1; - break; - } - input += 1; - if (!strcmp(input, "\0")) { - if (core->offset != core->prompt_offset) { - m_arena = core->offset; - if (!GH(update_main_arena)(core, m_arena, main_arena)) { - break; - } - } else { - if (!GH(update_main_arena)(core, m_arena, main_arena)) { - break; - } - } - } else { - m_arena = rz_num_get(NULL, input); - if (!GH(update_main_arena)(core, m_arena, main_arena)) { - break; - } - } - GH(print_arena_stats) - (core, m_arena, main_arena, global_max_fast, format); + } else { + if (core->offset != core->prompt_offset) { + m_state = core->offset; + } else { + m_state = m_arena; } - break; - case 'b': // "dmhb" - if (GH(rz_resolve_main_arena)(core, &m_arena)) { - char *m_state_str, *dup = strdup(input + 1); - if (*dup) { - strtok(dup, ":"); - m_state_str = strtok(NULL, ":"); - m_state = rz_num_get(NULL, m_state_str); - if (!m_state) { - m_state = m_arena; - } - } else { - if (core->offset != core->prompt_offset) { - m_state = core->offset; - } else { - m_state = m_arena; - } - } - if (GH(is_arena)(core, m_arena, m_state)) { - if (!GH(update_main_arena)(core, m_state, main_arena)) { - free(dup); - break; - } - GH(print_heap_bin) - (core, m_state, main_arena, dup); - } else { - PRINT_RA("This address is not part of the arenas\n"); - free(dup); - break; - } + } + if (GH(is_arena)(core, m_arena, m_state)) { + if (!GH(rz_heap_update_main_arena)(core, m_state, main_arena)) { + free(main_arena); free(dup); + return RZ_CMD_STATUS_ERROR; } - break; - case 'c': // "dmhc" - if (GH(rz_resolve_main_arena)(core, &m_arena)) { - GH(print_heap_chunk) - (core); - } - break; - case 'd': // "dmhd" - if (!GH(rz_resolve_main_arena)(core, &m_arena)) { - break; - } - if (!GH(update_main_arena)(core, m_arena, main_arena)) { - break; - } - input += 1; - RzHeapBinType bin_format = RZ_HEAP_BIN_ANY; - if (input[0] == ' ') { - input += 1; - if (!strcmp(input, "tcache")) { - bin_format = RZ_HEAP_BIN_TCACHE; - } else if (!strcmp(input, "fast")) { - bin_format = RZ_HEAP_BIN_FAST; - } else if (!strcmp(input, "unsorted")) { - bin_format = RZ_HEAP_BIN_UNSORTED; - } else if (!strcmp(input, "small")) { - bin_format = RZ_HEAP_BIN_SMALL; - } else if (!strcmp(input, "large")) { - bin_format = RZ_HEAP_BIN_LARGE; - } else { - break; - } - } + GH(print_heap_bin) + (core, m_state, main_arena, dup); + } else { + PRINT_RA("This address is not part of the arenas\n"); + free(main_arena); + free(dup); + return RZ_CMD_STATUS_ERROR; + } + free(dup); + free(main_arena); + return RZ_CMD_STATUS_OK; +} - GH(print_main_arena_bins) - (core, m_arena, main_arena, global_max_fast, bin_format); - break; - case 'f': // "dmhf" - if (GH(rz_resolve_main_arena)(core, &m_arena)) { - bool main_arena_only = false; - char *m_state_str, *dup = strdup(input + 1); - if (*dup) { - strtok(dup, ":"); - m_state_str = strtok(NULL, ":"); - m_state = rz_num_get(NULL, m_state_str); - if (!m_state) { - m_state = m_arena; - } - } else { - if (core->offset != core->prompt_offset) { - m_state = core->offset; - } else { - m_state = m_arena; - } - } - if (GH(is_arena)(core, m_arena, m_state)) { - if (!GH(update_main_arena)(core, m_state, main_arena)) { - free(dup); - break; - } - GH(print_heap_fastbin) - (core, m_state, main_arena, global_max_fast, dup, main_arena_only); - } else { - PRINT_RA("This address is not part of the arenas\n"); - free(dup); - break; - } - free(dup); +RZ_IPI int GH(rz_cmd_heap_fastbins_print)(void *data, const char *input) { + RzCore *core = (RzCore *)data; + GHT m_arena = GHT_MAX, m_state = GHT_MAX; + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + MallocState *main_arena = RZ_NEW0(MallocState); + GHT global_max_fast = (64 * SZ / 4); + if (!GH(rz_heap_resolve_main_arena)(core, &m_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + bool main_arena_only = false; + char *m_state_str, *dup = strdup(input); + if (*dup) { + strtok(dup, ":"); + m_state_str = strtok(NULL, ":"); + m_state = rz_num_get(NULL, m_state_str); + if (!m_state) { + m_state = m_arena; } - break; - case 'v': - if (input[0] == 'v') { - format = 'v'; - } - case 'g': //dmhg - if (input[0] == 'g') { - format = 'g'; - } - case '*': //dmh* - if (input[0] == '*') { - format = '*'; - } - case 'j': // "dmhj" - if (input[0] == 'j') { - format = 'j'; - } - if (GH(rz_resolve_main_arena)(core, &m_arena)) { - input += 1; - if (!strcmp(input, "\0")) { - if (core->offset != core->prompt_offset) { - m_state = core->offset; - get_state = true; - } - } else { - m_state = rz_num_get(NULL, input); - get_state = true; - } - if (!get_state) { - m_state = m_arena; - } - if (GH(is_arena)(core, m_arena, m_state)) { - if (!GH(update_main_arena)(core, m_state, main_arena)) { - break; - } - GH(print_heap_segment) - (core, main_arena, m_arena, m_state, global_max_fast, format); - } else { - PRINT_RA("This address is not part of the arenas\n"); - } + } else { + if (core->offset != core->prompt_offset) { + m_state = core->offset; + } else { + m_state = m_arena; } - break; - case 't': - if (GH(rz_resolve_main_arena)(core, &m_arena)) { - if (!GH(update_main_arena)(core, m_arena, main_arena)) { - break; - } - bool main_thread_only = false; - GH(print_tcache_instance) - (core, m_arena, main_arena, main_thread_only); + } + if (GH(is_arena)(core, m_arena, m_state)) { + if (!GH(rz_heap_update_main_arena)(core, m_state, main_arena)) { + free(dup); + free(main_arena); + return RZ_CMD_STATUS_ERROR; } - break; - case '?': - rz_core_cmd_help(core, GH(help_msg)); - break; + GH(print_heap_fastbin) + (core, m_state, main_arena, global_max_fast, dup, main_arena_only, NULL); + } else { + PRINT_RA("This address is not part of the arenas\n"); + free(dup); + free(main_arena); + return RZ_CMD_STATUS_ERROR; } + free(dup); free(main_arena); - return true; + return RZ_CMD_STATUS_OK; } + +RZ_IPI RzCmdStatus GH(rz_cmd_heap_arena_bins_print_handler)(RzCore *core, int argc, const char **argv, RzOutputMode mode) { + GHT m_arena = GHT_MAX, m_state = GHT_MAX; + RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; + GHT global_max_fast = (64 * SZ / 4); + MallocState *main_arena = RZ_NEW0(MallocState); + if (!main_arena) { + return RZ_CMD_STATUS_ERROR; + } + if (!GH(rz_heap_resolve_main_arena)(core, &m_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + if (core->offset != core->prompt_offset) { + m_state = core->offset; + } else { + m_state = m_arena; + } + if (!GH(is_arena)(core, m_arena, m_state)) { + PRINT_RA("This address is not part of the arenas\n"); + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + if (!GH(rz_heap_update_main_arena)(core, m_state, main_arena)) { + free(main_arena); + return RZ_CMD_STATUS_ERROR; + } + + bool json = false; + if (mode == RZ_OUTPUT_MODE_JSON) { // dmhdj + json = true; + } + RzHeapBinType bin_format = RZ_HEAP_BIN_ANY; + if (argc == 2) { + const char *input = argv[1]; + if (!strcmp(input, "tcache")) { + bin_format = RZ_HEAP_BIN_TCACHE; + } else if (!strcmp(input, "fast")) { + bin_format = RZ_HEAP_BIN_FAST; + } else if (!strcmp(input, "unsorted")) { + bin_format = RZ_HEAP_BIN_UNSORTED; + } else if (!strcmp(input, "small")) { + bin_format = RZ_HEAP_BIN_SMALL; + } else if (!strcmp(input, "large")) { + bin_format = RZ_HEAP_BIN_LARGE; + } + } + GH(print_main_arena_bins) + (core, m_state, main_arena, global_max_fast, bin_format, json); + free(main_arena); + return RZ_CMD_STATUS_OK; +} \ No newline at end of file diff --git a/librz/core/linux_heap_jemalloc.c b/librz/core/linux_heap_jemalloc.c index 890b78531a5..e24c869eb64 100644 --- a/librz/core/linux_heap_jemalloc.c +++ b/librz/core/linux_heap_jemalloc.c @@ -492,12 +492,12 @@ static void GH(jemalloc_get_runs)(RzCore *core, const char *input) { static int GH(cmd_dbg_map_jemalloc)(RzCore *core, const char *input) { const char *help_msg[] = { - "Usage:", "dmh", " # Memory map heap", - "dmha", "[arena_t]", "show all arenas created, or print arena_t structure for given arena", - "dmhb", "[arena_t]", "show all bins created for given arena", - "dmhc", "*|[arena_t]", "show all chunks created in all arenas, or show all chunks created for a given arena_t instance", + "Usage:", "dmx", " # Jemalloc heap parsing commands", + "dmxa", "[arena_t]", "show all arenas created, or print arena_t structure for given arena", + "dmxb", "[arena_t]", "show all bins created for given arena", + "dmxc", "*|[arena_t]", "show all chunks created in all arenas, or show all chunks created for a given arena_t instance", // "dmhr", "[arena_chunk_t]", "print all runs created for a given arena_chunk_t instance", - "dmh?", "", "Show map heap help", NULL + "dmx?", "", "Show map heap help", NULL }; switch (input[0]) { diff --git a/librz/core/windows_heap.c b/librz/core/windows_heap.c index a509900a208..708838923b8 100644 --- a/librz/core/windows_heap.c +++ b/librz/core/windows_heap.c @@ -1315,17 +1315,17 @@ static void w32_list_heaps_blocks(RzCore *core, const char format) { } static const char *help_msg[] = { - "Usage:", " dmh[?|b][f|j]", " # Memory map heap", - "dmh[j]", "", "List process heaps", - "dmhb[?] [addr]", "", "List process heap blocks", + "Usage:", " dmh[?|b][f|j]", " # Windows heap parsing commands", + "dmw[j]", "", "List process heaps", + "dmwb[?] [addr]", "", "List process heap blocks", NULL }; static const char *help_msg_block[] = { - "Usage:", " dmhb[f|j]", " # Memory map heap", - "dmhb [addr]", "", "List allocated heap blocks", - "dmhbf", "", "Create flags for each allocated block", - "dmhbj [addr]", "", "Print output in JSON format", + "Usage:", " dmhb[f|j]", " # Windows heap parsing commands", + "dmwb [addr]", "", "List allocated heap blocks", + "dmwbf", "", "Create flags for each allocated block", + "dmwbj [addr]", "", "Print output in JSON format", NULL }; diff --git a/librz/debug/dmap.c b/librz/debug/dmap.c index c5f02e78c65..72a3e3c8251 100644 --- a/librz/debug/dmap.c +++ b/librz/debug/dmap.c @@ -4,278 +4,6 @@ #include #include -/* Print out the JSON body for memory maps in the passed map region */ -static void print_debug_map_json(RzDebugMap *map, PJ *pj) { - pj_o(pj); - if (map->name && *map->name) { - pj_ks(pj, "name", map->name); - } - if (map->file && *map->file) { - pj_ks(pj, "file", map->file); - } - pj_kn(pj, "addr", map->addr); - pj_kn(pj, "addr_end", map->addr_end); - pj_ks(pj, "type", map->user ? "u" : "s"); - pj_ks(pj, "perm", rz_str_rwx_i(map->perm)); - pj_end(pj); -} - -/* Write the memory map header describing the line columns */ -static void print_debug_map_line_header(RzDebug *dbg, const char *input) { - // TODO: Write header to console based on which command is being ran -} - -/* Write a single memory map line to the console */ -static void print_debug_map_line(RzDebug *dbg, RzDebugMap *map, ut64 addr, const char *input) { - char humansz[8]; - if (input[0] == 'q') { // "dmq" - char *name = (map->name && *map->name) - ? rz_str_newf("%s.%s", map->name, rz_str_rwx_i(map->perm)) - : rz_str_newf("%08" PFMT64x ".%s", map->addr, rz_str_rwx_i(map->perm)); - rz_name_filter(name, 0, true); - rz_num_units(humansz, sizeof(humansz), map->addr_end - map->addr); - dbg->cb_printf("0x%016" PFMT64x " - 0x%016" PFMT64x " %6s %5s %s\n", - map->addr, - map->addr_end, - humansz, - rz_str_rwx_i(map->perm), - name); - free(name); - } else { - const char *fmtstr = dbg->bits & RZ_SYS_BITS_64 - ? "0x%016" PFMT64x " - 0x%016" PFMT64x " %c %s %6s %c %s %s %s%s%s\n" - : "0x%08" PFMT64x " - 0x%08" PFMT64x " %c %s %6s %c %s %s %s%s%s\n"; - const char *type = map->shared ? "sys" : "usr"; - const char *flagname = dbg->corebind.getName - ? dbg->corebind.getName(dbg->corebind.core, map->addr) - : NULL; - if (!flagname) { - flagname = ""; - } else if (map->name) { - char *filtered_name = strdup(map->name); - rz_name_filter(filtered_name, 0, true); - if (!strncmp(flagname, "map.", 4) && - !strcmp(flagname + 4, filtered_name)) { - flagname = ""; - } - free(filtered_name); - } - rz_num_units(humansz, sizeof(humansz), map->size); - dbg->cb_printf(fmtstr, - map->addr, - map->addr_end, - (addr >= map->addr && addr < map->addr_end) ? '*' : '-', - type, - humansz, - map->user ? 'u' : 's', - rz_str_rwx_i(map->perm), - map->name ? map->name : "?", - map->file ? map->file : "?", - *flagname ? " ; " : "", - flagname); - } -} - -RZ_API void rz_debug_map_list(RzDebug *dbg, ut64 addr, const char *input) { - int i; - RzListIter *iter; - RzDebugMap *map; - PJ *pj = NULL; - if (!dbg) { - return; - } - - switch (input[0]) { - case 'j': // "dmj" add JSON opening array brace - pj = pj_new(); - if (!pj) { - return; - } - pj_a(pj); - break; - case '*': // "dm*" don't print a header for r2 commands output - break; - default: - // TODO: Find a way to only print headers if output isn't being grepped - print_debug_map_line_header(dbg, input); - } - - for (i = 0; i < 2; i++) { // Iterate over dbg::maps and dbg::maps_user - RzList *maps = (i == 0) ? dbg->maps : dbg->maps_user; - rz_list_foreach (maps, iter, map) { - switch (input[0]) { - case 'j': // "dmj" - print_debug_map_json(map, pj); - break; - case '*': // "dm*" - { - char *name = (map->name && *map->name) - ? rz_str_newf("%s.%s", map->name, rz_str_rwx_i(map->perm)) - : rz_str_newf("%08" PFMT64x ".%s", map->addr, rz_str_rwx_i(map->perm)); - rz_name_filter(name, 0, true); - dbg->cb_printf("f map.%s 0x%08" PFMT64x " 0x%08" PFMT64x "\n", - name, map->addr_end - map->addr + 1, map->addr); - free(name); - } break; - case 'q': // "dmq" - if (input[1] == '.') { // "dmq." - if (addr >= map->addr && addr < map->addr_end) { - print_debug_map_line(dbg, map, addr, input); - } - break; - } - print_debug_map_line(dbg, map, addr, input); - break; - case '.': - if (addr >= map->addr && addr < map->addr_end) { - print_debug_map_line(dbg, map, addr, input); - } - break; - default: - print_debug_map_line(dbg, map, addr, input); - break; - } - } - } - - if (pj) { // "dmj" add JSON closing array brace - pj_end(pj); - dbg->cb_printf("%s\n", pj_string(pj)); - pj_free(pj); - } -} - -static int cmp(const void *a, const void *b) { - RzDebugMap *ma = (RzDebugMap *)a; - RzDebugMap *mb = (RzDebugMap *)b; - return ma->addr - mb->addr; -} - -/** - * \brief Find the min and max addresses in an RzList of maps. - * \param maps RzList of maps that will be searched through - * \param min Pointer to a ut64 that the min will be stored in - * \param max Pointer to a ut64 that the max will be stored in - * \param skip How many maps to skip at the start of iteration - * \param width Divisor for the return value - * \return (max-min)/width - * - * Used to determine the min & max addresses of maps and - * scale the ascii bar to the width of the terminal - */ -static int findMinMax(RzList *maps, ut64 *min, ut64 *max, int skip, int width) { - RzDebugMap *map; - RzListIter *iter; - *min = UT64_MAX; - *max = 0; - rz_list_foreach (maps, iter, map) { - if (skip > 0) { - skip--; - continue; - } - if (map->addr < *min) { - *min = map->addr; - } - if (map->addr_end > *max) { - *max = map->addr_end; - } - } - return (*max - *min) / width; -} - -static void print_debug_maps_ascii_art(RzDebug *dbg, RzList *maps, ut64 addr, int colors) { - ut64 mul; // The amount of address space a single console column will represent in bar graph - ut64 min = -1, max = 0; - int width = rz_cons_get_size(NULL) - 90; - RzListIter *iter; - RzDebugMap *map; - RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal; - if (width < 1) { - width = 30; - } - rz_list_sort(maps, cmp); - mul = findMinMax(maps, &min, &max, 0, width); - ut64 last = min; - if (min != -1 && mul != 0) { - const char *color_prefix = ""; // Color escape code prefixed to string (address coloring) - const char *color_suffix = ""; // Color escape code appended to end of string - const char *fmtstr; - char humansz[8]; // Holds the human formatted size string [124K] - int skip = 0; // Number of maps to skip when re-calculating the minmax - rz_list_foreach (maps, iter, map) { - rz_num_units(humansz, sizeof(humansz), map->size); // Convert map size to human readable string - if (colors) { - color_suffix = Color_RESET; - if ((map->perm & 2) && (map->perm & 1)) { // Writable & Executable - color_prefix = pal->widget_sel; - } else if (map->perm & 2) { // Writable - color_prefix = pal->graph_false; - } else if (map->perm & 1) { // Executable - color_prefix = pal->graph_true; - } else { - color_prefix = ""; - color_suffix = ""; - } - } else { - color_prefix = ""; - color_suffix = ""; - } - if ((map->addr - last) > UT32_MAX) { // TODO: Comment what this is for - mul = findMinMax(maps, &min, &max, skip, width); // Recalculate minmax - } - skip++; - fmtstr = dbg->bits & RZ_SYS_BITS_64 // Prefix formatting string (before bar) - ? "map %4.8s %c %s0x%016" PFMT64x "%s |" - : "map %4.8s %c %s0x%08" PFMT64x "%s |"; - dbg->cb_printf(fmtstr, humansz, - (addr >= map->addr && - addr < map->addr_end) - ? '*' - : '-', - color_prefix, map->addr, color_suffix); // * indicates map is within our current sought offset - int col; - for (col = 0; col < width; col++) { // Iterate over the available width/columns for bar graph - ut64 pos = min + (col * mul); // Current address space to check - ut64 npos = min + ((col + 1) * mul); // Next address space to check - if (map->addr < npos && map->addr_end > pos) { - dbg->cb_printf("#"); // TODO: Comment what a # represents - } else { - dbg->cb_printf("-"); - } - } - fmtstr = dbg->bits & RZ_SYS_BITS_64 ? // Suffix formatting string (after bar) - "| %s0x%016" PFMT64x "%s %s %s\n" - : "| %s0x%08" PFMT64x "%s %s %s\n"; - dbg->cb_printf(fmtstr, color_prefix, map->addr_end, color_suffix, - rz_str_rwx_i(map->perm), map->name); - last = map->addr; - } - } -} - -RZ_API void rz_debug_map_list_visual(RzDebug *dbg, ut64 addr, const char *input, int colors) { - if (dbg) { - int i; - for (i = 0; i < 2; i++) { // Iterate over dbg::maps and dbg::maps_user - RzList *maps = (i == 0) ? dbg->maps : dbg->maps_user; - if (maps) { - RzListIter *iter; - RzDebugMap *map; - if (input[1] == '.') { // "dm=." Only show map overlapping current offset - dbg->cb_printf("TODO:\n"); - rz_list_foreach (maps, iter, map) { - if (addr >= map->addr && addr < map->addr_end) { - // print_debug_map_ascii_art (dbg, map); - } - } - } else { // "dm=" Show all maps with a graph - print_debug_maps_ascii_art(dbg, maps, addr, colors); - } - } - } - } -} - RZ_API RzDebugMap *rz_debug_map_new(char *name, ut64 addr, ut64 addr_end, int perm, int user) { RzDebugMap *map; /* range could be 0k on OpenBSD, it's a honeypot */ @@ -360,3 +88,13 @@ RZ_API RzList *rz_debug_map_list_new(void) { list->free = (RzListFree)rz_debug_map_free; return list; } + +/** + * \brief Get RzList* of memory maps for the process currently being debugged + * \param dbg RzDebug pointer + * \param user_map Boolean value, if true return memory maps belonging to user space else return memory maps belonging to kernel space + * \return + */ +RZ_API RzList *rz_debug_map_list(RzDebug *dbg, bool user_map) { + return user_map ? dbg->maps_user : dbg->maps; +} diff --git a/librz/include/rz_core.h b/librz/include/rz_core.h index 38a7eead35f..12a2f3be1ae 100644 --- a/librz/include/rz_core.h +++ b/librz/include/rz_core.h @@ -31,6 +31,7 @@ #include #include #include +#include "rz_heap_glibc.h" #ifdef __cplusplus extern "C" { @@ -772,6 +773,22 @@ RZ_API char *rz_core_sysenv_begin(RzCore *core, const char *cmd); RZ_API void rz_core_sysenv_end(RzCore *core, const char *cmd); RZ_API void rz_core_recover_vars(RzCore *core, RzAnalysisFunction *fcn, bool argonly); + +/* linux_heap_glibc.c */ +RZ_API RzHeapChunk_64 *rz_heap_get_chunk_at_addr_64(RzCore *core, ut64 addr); +RZ_API RzList *rz_heap_bin_content_list_64(RzCore *core, MallocState *main_arena, int bin_num); +RZ_API RzList *rz_heap_arenas_list_64(RzCore *core, ut64 m_arena, MallocState *main_arena); +RZ_API RzList *rz_heap_chunks_list_64(RzCore *core, MallocState *main_arena, ut64 m_arena, ut64 m_state); +RZ_API bool rz_heap_resolve_main_arena_64(RzCore *core, ut64 *m_arena); +RZ_API bool rz_heap_update_main_arena_64(RzCore *core, ut64 m_arena, MallocState *main_arena); +RZ_API RzList *rz_heap_tcache_list_64(RzCore *core, ut64 m_arena, MallocState *main_arena, bool main_thread_only); +RZ_API RzHeapChunk_32 *rz_heap_get_chunk_at_addr_32(RzCore *core, ut32 addr); +RZ_API RzList *rz_heap_bin_content_list_32(RzCore *core, MallocState *main_arena, int bin_num); +RZ_API RzList *rz_heap_arenas_list_32(RzCore *core, ut32 m_arena, MallocState *main_arena); +RZ_API RzList *rz_heap_heap_chunks_list_32(RzCore *core, MallocState *main_arena, ut32 m_arena, ut32 m_state); +RZ_API bool rz_heap_resolve_main_arena_32(RzCore *core, ut32 *m_arena); +RZ_API bool rz_heap_update_main_arena_32(RzCore *core, ut32 m_arena, MallocState *main_arena); +RZ_API RzList *rz_heap_tcache_list_32(RzCore *core, ut32 m_arena, MallocState *main_arena, bool main_thread_only); // XXX dupe from rz_bin.h /* bin.c */ #define RZ_CORE_BIN_ACC_STRINGS 0x001 diff --git a/librz/include/rz_debug.h b/librz/include/rz_debug.h index f7f56c2a990..a636b015d21 100644 --- a/librz/include/rz_debug.h +++ b/librz/include/rz_debug.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "rz_bind.h" @@ -50,6 +51,13 @@ RZ_LIB_VERSION_HEADER(rz_debug); #define PTRACE_SYSCALL PT_STEP #endif +#define CMD_CHECK_DEBUG_DEAD(core) \ + do { \ + if (rz_debug_is_dead(core->dbg)) { \ + rz_cons_println("Debugging is not enabled. Run ood?"); \ + return RZ_CMD_STATUS_ERROR; \ + } \ + } while (0) #define SNAP_PAGE_SIZE 4096 #define CHECK_POINT_LIMIT 0x100000 //TODO: take the benchmark /* @@ -494,8 +502,9 @@ RZ_API RzList *rz_debug_map_list_new(void); RZ_API RzDebugMap *rz_debug_map_get(RzDebug *dbg, ut64 addr); RZ_API RzDebugMap *rz_debug_map_new(char *name, ut64 addr, ut64 addr_end, int perm, int user); RZ_API void rz_debug_map_free(RzDebugMap *map); -RZ_API void rz_debug_map_list(RzDebug *dbg, ut64 addr, const char *input); +RZ_API void rz_debug_map_print(RzDebug *dbg, ut64 addr, RzCmdStateOutput *state); RZ_API void rz_debug_map_list_visual(RzDebug *dbg, ut64 addr, const char *input, int colors); +RZ_API RzList *rz_debug_map_list(RzDebug *dbg, bool user_map); /* descriptors */ RZ_API RzDebugDesc *rz_debug_desc_new(int fd, char *path, int perm, int type, int off); diff --git a/librz/include/rz_heap_glibc.h b/librz/include/rz_heap_glibc.h index 75228bf2465..486a1726b68 100644 --- a/librz/include/rz_heap_glibc.h +++ b/librz/include/rz_heap_glibc.h @@ -48,8 +48,6 @@ RZ_LIB_VERSION_HEADER(rz_heap_glibc); #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) \ @@ -278,6 +276,12 @@ typedef enum rz_heap_bin_type { RZ_HEAP_BIN_LARGE } RzHeapBinType; +typedef struct rz_heap_chunk_list_item { + ut64 addr; /* Base addr of the chunk */ + ut64 size; /* Size of the chunk */ + char *status; /* Status of the chunk, allocated/free/corrupted */ +} RzHeapChunkListItem; + #ifdef __cplusplus } #endif diff --git a/test/db/archos/linux-x64/dbg_aslr b/test/db/archos/linux-x64/dbg_aslr index 83acb607cbd..f261d97b8ec 100644 --- a/test/db/archos/linux-x64/dbg_aslr +++ b/test/db/archos/linux-x64/dbg_aslr @@ -15,7 +15,7 @@ ARGS=-d CMDS=<