summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2021-05-08 14:42:01 -0400
committerLeo Tenenbaum <pommicket@gmail.com>2021-05-08 14:42:01 -0400
commit47f0ede5e45a14fa2b94f5333f6d4534c71802d6 (patch)
treea1911299da0249099380e68cace8f15ffdab29bb
parente22ef4390b887f38d4acaa2b6b7ae6c28f62b676 (diff)
memfiles, configure protections
-rw-r--r--main.c125
-rw-r--r--memory.c190
-rw-r--r--ui.glade153
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 @@
</packing>
</child>
<child>
- <placeholder/>
+ <object class="GtkBox" id="protection-box">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkLabel" id="protection-label">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Memory protection: </property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="protection">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="tooltip-text" translatable="yes">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.</property>
+ <property name="max-length">4</property>
+ <property name="text" translatable="yes">rw-p</property>
+ <signal name="activate" handler="update_configuration" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
</child>
<child>
- <placeholder/>
+ <!-- n-columns=3 n-rows=5 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkButton" id="save-search-candidates">
+ <property name="label" translatable="yes">Save search candidates</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="no-show-all">True</property>
+ <property name="tooltip-text" translatable="yes">Save current values of all search candidates to a file, so they can be loaded back later.</property>
+ <signal name="clicked" handler="memfile_do_write_candidates" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="label" translatable="yes">Save all memory to file</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="tooltip-text" translatable="yes">This probably won't work the way you think it will. I would recommend against using it.</property>
+ <signal name="clicked" handler="memfile_do_write_all" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="memfile-path">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="text" translatable="yes">/tmp/save.mem</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="label" translatable="yes">Load memory from file</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="tooltip-text" translatable="yes">Load whatever's in the saved memory file.</property>
+ <signal name="clicked" handler="memfile_do_read" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Memory file:</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes">Memory files</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
</child>
</object>
<packing>