From 47f0ede5e45a14fa2b94f5333f6d4534c71802d6 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Sat, 8 May 2021 14:42:01 -0400 Subject: memfiles, configure protections --- main.c | 125 +++++++++++++++++++++++++++-------------- memory.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ui.glade | 153 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 424 insertions(+), 44 deletions(-) diff --git a/main.c b/main.c index bbeec12..628d43f 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,3 @@ -// @TODO: -// - configure which memory to look at (see "rw-p") #include "base.h" #include "unicode.h" #include "data.c" @@ -133,9 +131,68 @@ static char const *radio_group_get_selected(State *state, char const *group_name return NULL; } +// update the memory maps for the current process (state->maps) +// returns true on success +static bool update_maps(State *state) { + GtkBuilder *builder = state->builder; + char const *desired_protection = gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(builder, "protection"))); + free(state->maps); state->maps = NULL; + + char maps_name[64]; + sprintf(maps_name, "/proc/%lld/maps", (long long)state->pid); + FILE *maps_file = fopen(maps_name, "rb"); + if (maps_file) { + char line[256]; + size_t capacity = 0; + while (fgets(line, sizeof line, maps_file)) + ++capacity; + rewind(maps_file); + Map *maps = state->maps = calloc(capacity, sizeof *maps); + unsigned nmaps = 0; + state->total_memory = 0; + if (maps) { + while (fgets(line, sizeof line, maps_file)) { + Address addr_lo, addr_hi; + char protections[8]; + if (sscanf(line, "%" SCNxADDR "-%" SCNxADDR " %8s", &addr_lo, &addr_hi, protections) == 3 && nmaps < capacity) { + if (strcmp(protections, desired_protection) == 0) { + Map *map = &maps[nmaps++]; + map->lo = addr_lo; + map->size = addr_hi - addr_lo; + state->total_memory += map->size; + } + } + } + state->nmaps = nmaps; + return true; + } else { + display_error(state, "Not enough memory to hold map metadata (%zu items)", capacity); + } + } else { + display_error(state, "Couldn't open %s: %s", maps_name, strerror(errno)); + } + return false; +} + G_MODULE_EXPORT void update_configuration(GtkWidget *_widget, gpointer user_data) { State *state = user_data; GtkBuilder *builder = state->builder; + + static char prev_protection[5] = "rw-p"; + char const *protection = gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(builder, "protection"))); + if (strcmp(protection, prev_protection) != 0) { + strcpy(prev_protection, protection); + if (update_maps(state)) { + if (state->nmaps) { + GtkEntry *address_entry = GTK_ENTRY(gtk_builder_get_object(builder, "address")); + Address addr = state->maps[0].lo; + char addr_text[32]; + sprintf(addr_text, "%" PRIxADDR, addr); + gtk_entry_set_text(address_entry, addr_text); + } + } + } + state->stop_while_accessing_memory = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "stop-while-accessing-memory"))); char const *n_items_text = gtk_entry_get_text( @@ -203,6 +260,7 @@ G_MODULE_EXPORT void update_configuration(GtkWidget *_widget, gpointer user_data update_memory_view(state, true); } + } G_MODULE_EXPORT void set_all(GtkWidget *_widget, gpointer user_data) { @@ -235,46 +293,6 @@ G_MODULE_EXPORT void set_all(GtkWidget *_widget, gpointer user_data) { } } -// update the memory maps for the current process (state->maps) -// returns true on success -static bool update_maps(State *state) { - free(state->maps); state->maps = NULL; - - char maps_name[64]; - sprintf(maps_name, "/proc/%lld/maps", (long long)state->pid); - FILE *maps_file = fopen(maps_name, "rb"); - if (maps_file) { - char line[256]; - size_t capacity = 0; - while (fgets(line, sizeof line, maps_file)) - ++capacity; - rewind(maps_file); - Map *maps = state->maps = calloc(capacity, sizeof *maps); - unsigned nmaps = 0; - state->total_memory = 0; - if (maps) { - while (fgets(line, sizeof line, maps_file)) { - Address addr_lo, addr_hi; - char protections[8]; - if (sscanf(line, "%" SCNxADDR "-%" SCNxADDR " %8s", &addr_lo, &addr_hi, protections) == 3 && nmaps < capacity) { - if (strcmp(protections, "rw-p") == 0) { // @TODO(eventually): make this configurable - Map *map = &maps[nmaps++]; - map->lo = addr_lo; - map->size = addr_hi - addr_lo; - state->total_memory += map->size; - } - } - } - state->nmaps = nmaps; - return true; - } else { - display_error(state, "Not enough memory to hold map metadata (%zu items)", capacity); - } - } else { - display_error(state, "Couldn't open %s: %s", maps_name, strerror(errno)); - } - return false; -} // the user entered a PID. G_MODULE_EXPORT void select_pid(GtkButton *_button, gpointer user_data) { @@ -451,7 +469,9 @@ G_MODULE_EXPORT void search_start(GtkWidget *_widget, gpointer user_data) { if (candidates) { gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "pre-search"))); gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(builder, "data-type-box")), 0); + gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(builder, "protection")), 0); gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "search-common"))); + gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "save-search-candidates"))); gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(builder, "steps-completed")), "0"); gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(builder, "address")), ""); memset(candidates, 0xff, state->total_memory / (8 * item_size)); @@ -651,8 +671,10 @@ G_MODULE_EXPORT void search_stop(GtkWidget *_widget, gpointer user_data) { gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "search-common"))); gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "search-enter-value"))); gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "search-same-different"))); + gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "save-search-candidates"))); gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "pre-search"))); gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(builder, "data-type-box")), 1); + gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(builder, "protection")), 1); } // this function is run once per frame @@ -682,6 +704,25 @@ static gboolean frame_callback(gpointer user_data) { return 1; } + +G_MODULE_EXPORT void memfile_do_read(GtkWidget *_widget, gpointer user_data) { + State *state = user_data; + GtkBuilder *builder = state->builder; + memfile_load(state, gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(builder, "memfile-path")))); +} + +G_MODULE_EXPORT void memfile_do_write_all(GtkWidget *_widget, gpointer user_data) { + State *state = user_data; + GtkBuilder *builder = state->builder; + memfile_write_all(state, gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(builder, "memfile-path")))); +} + +G_MODULE_EXPORT void memfile_do_write_candidates(GtkWidget *_widget, gpointer user_data) { + State *state = user_data; + GtkBuilder *builder = state->builder; + memfile_write_candidates(state, gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(builder, "memfile-path")))); +} + static void on_activate(GtkApplication *app, gpointer user_data) { State *state = user_data; GError *error = NULL; diff --git a/memory.c b/memory.c index 309d1ae..8513f21 100644 --- a/memory.c +++ b/memory.c @@ -102,3 +102,193 @@ static Address memory_write_bytes(int writer, Address addr, uint8_t const *bytes } return idx; } + +typedef struct { + FILE *fp; + Address curr_addr; +} MemfileWriter; +static char const MEMFILE_IDENT[4] = {'\xff', 'M', 'E', 'M'}; + +static bool memfile_writer_open(State *state, MemfileWriter *writer, char const *filename) { + memset(writer, 0, sizeof *writer); + writer->fp = fopen(filename, "wb"); + if (writer->fp) { + fwrite(MEMFILE_IDENT, 1, sizeof MEMFILE_IDENT, writer->fp); + return true; + } else { + display_error(state, "Couldn't open %s: %s.", filename, strerror(errno)); + return false; + } +} + +static void memfile_writer_close(MemfileWriter *writer) { + if (writer->fp) fclose(writer->fp); + memset(writer, 0, sizeof *writer); +} + + +// write up to 64K of consecutive memory. +static void memfile_write_bytes(MemfileWriter *writer, Address addr, uint8_t const *data, size_t nbytes) { + Address addr_increment = addr - writer->curr_addr; + // set address + if (addr_increment < 64) { + putc((int)addr_increment, writer->fp); + } else if (addr_increment < 8192) { + putc(0x40 | (int)(addr_increment & 0x1f), writer->fp); + putc((int)(addr_increment >> 5), writer->fp); + } else { + putc(0x60, writer->fp); + fwrite(&addr, sizeof addr, 1, writer->fp); + } + + if (nbytes < 64) { + putc(0x80 | (int)nbytes, writer->fp); + } else if (nbytes < 65536) { + putc(0xC0, writer->fp); + fwrite(&nbytes, 2, 1, writer->fp); + } else { + putc(0xE0, writer->fp); + fwrite(&nbytes, sizeof nbytes, 1, writer->fp); + } + fwrite(data, 1, nbytes, writer->fp); + + writer->curr_addr = addr + nbytes; +} + +static void memfile_write_byte(MemfileWriter *writer, Address addr, uint8_t byte) { + memfile_write_bytes(writer, addr, &byte, 1); +} + +static void memfile_write_all(State *state, char const *filename) { + if (!state->pid) return; + MemfileWriter writer = {0}; + if (memfile_writer_open(state, &writer, filename)) { + int reader = memory_reader_open(state); + if (reader) { + for (unsigned m = 0; m < state->nmaps; ++m) { + Map *map = &state->maps[m]; + uint8_t chunk[4096] = {0}; // page size is probably a multiple of 4096. + for (Address offset = 0; offset < map->size; offset += sizeof chunk) { + Address addr = map->lo + offset; + memory_read_bytes(reader, addr, chunk, sizeof chunk); + memfile_write_bytes(&writer, addr, chunk, sizeof chunk); + } + } + memory_reader_close(state, reader); + } + memfile_writer_close(&writer); + } +} + +static void memfile_write_candidates(State *state, char const *filename) { + if (!state->pid) return; + size_t item_size = data_type_size(state->data_type); + MemfileWriter writer = {0}; + if (memfile_writer_open(state, &writer, filename)) { + int reader = memory_reader_open(state); + if (reader) { + Address bitset_index = 0; + uint64_t *search_candidates = state->search_candidates; + for (unsigned m = 0; m < state->nmaps; ++m) { + Map *map = &state->maps[m]; + Address n_items = map->size / item_size; + for (Address i = 0; i < n_items; ) { + if (bitset_index % 64 == 0 && search_candidates[bitset_index / 64] == 0) { + // no candidates here + i += 64; + bitset_index += 64; + } else { + if (search_candidates[bitset_index / 64] & MASK64(bitset_index % 64)) { + // a candidate! + Address addr = map->lo + i * item_size; + uint64_t value = 0; + memory_read_bytes(reader, addr, (uint8_t *)&value, item_size); + memfile_write_bytes(&writer, addr, (uint8_t const *)&value, item_size); + } + ++i; + ++bitset_index; + } + } + } + memory_reader_close(state, reader); + } + memfile_writer_close(&writer); + } +} + +static void memfile_load(State *state, char const *filename) { + if (!state->pid) return; + FILE *fp = fopen(filename, "rb"); + if (fp) { + char ident[sizeof MEMFILE_IDENT] = {0}; + fread(ident, sizeof ident, 1, fp); + if (memcmp(ident, MEMFILE_IDENT, sizeof MEMFILE_IDENT) != 0) { + display_error(state, "%s is not a memory file.", filename); + } else { + int writer = memory_writer_open(state); + if (writer) { + Address addr = 0; + + int first_byte; + while ((first_byte = getc(fp)) != EOF) { + uint64_t val = 0; + size_t nbytes = 0; + switch (first_byte & 0xE0) { + case 0x00: + case 0x20: + // 6 bits address increment + addr += first_byte & 0x3F; + break; + case 0x40: + // 5 bits + 1 byte address increment + addr += first_byte & 0x1F; + addr += (unsigned)getc(fp) << 5; + break; + case 0x60: + // constant 4/8-byte address + if (first_byte != 0x60) + goto invalid; + fread(&val, sizeof(Address), 1, fp); + addr = val; + break; + case 0x80: + case 0xA0: + // 6 bit length; data + nbytes = first_byte & 0x3F; + goto read_data; + case 0xC0: + if (first_byte != 0xC0) goto invalid; + // 2 bytes length; data + fread(&nbytes, 2, 1, fp); + goto read_data; + case 0xE0: + if (first_byte != 0xE0) goto invalid; + // 4/8 bytes length; data + fread(&nbytes, sizeof(size_t), 1, fp); + goto read_data; + read_data: { + uint8_t chunk[4096] = {0}; + size_t bytes_left = nbytes; + while (bytes_left > 0) { + size_t chunk_len = bytes_left; + if (chunk_len > sizeof chunk) chunk_len = sizeof chunk; + fread(chunk, 1, chunk_len, fp); + memory_write_bytes(writer, addr, chunk, chunk_len); + addr += chunk_len; + bytes_left -= chunk_len; + } + } break; + invalid: + display_error(state, "%s is an invalid memory file.", filename); + goto eof; + } + } + eof: + memory_writer_close(state, writer); + } + } + fclose(fp); + } else { + display_error(state, "Couldn't open %s: %s.", filename, strerror(errno)); + } +} diff --git a/ui.glade b/ui.glade index e842787..ee81f28 100644 --- a/ui.glade +++ b/ui.glade @@ -973,10 +973,159 @@ - + + True + False + + + True + False + Memory protection: + + + False + True + 0 + + + + + True + True + What protection the memory you're interested in has (the second column of /proc/PID/maps)--if you don't know what this does, just leave it set to the default. + 4 + rw-p + + + + False + True + 1 + + + + + False + True + 1 + - + + + True + False + + + Save search candidates + True + True + True + Save current values of all search candidates to a file, so they can be loaded back later. + + + + 0 + 4 + + + + + Save all memory to file + True + True + True + This probably won't work the way you think it will. I would recommend against using it. + + + + 0 + 3 + + + + + True + True + /tmp/save.mem + + + 1 + 1 + + + + + Load memory from file + True + True + True + Load whatever's in the saved memory file. + + + + 0 + 2 + + + + + True + False + Memory file: + + + 0 + 1 + + + + + True + False + Memory files + + + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + True + 2 + -- cgit v1.2.3