diff options
author | the lemons <citrons@mondecitronne.com> | 2023-02-05 19:06:16 -0600 |
---|---|---|
committer | the lemons <citrons@mondecitronne.com> | 2023-02-05 19:06:16 -0600 |
commit | 3af50b956b8887b64c3c29b30a0008be866b24c4 (patch) | |
tree | af2a6f736505585192f852342ae988a297024133 |
initial commit
-rw-r--r-- | Makefile | 18 | ||||
-rw-r--r-- | core.c | 150 | ||||
-rw-r--r-- | memview.c | 124 | ||||
-rw-r--r-- | memview.h | 28 | ||||
-rw-r--r-- | procfs.c | 28 | ||||
-rw-r--r-- | procfs.h | 15 |
6 files changed, 363 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..50a35f4 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +SDL_HEADERS=/usr/include/SDL2 + +CFLAGS=-g -Wall -I$(SDL_HEADERS) +LFLAGS=-lSDL2 + +core: core.o procfs.o memview.o + $(CC) -o $@ $^ $(LFLAGS) + +core.c: procfs.h memview.h +memview.c: procfs.h memview.h +procfs.c: procfs.h + +.SUFFIXES: .c .o +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +clean: + rm -f *.o core @@ -0,0 +1,150 @@ +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <unistd.h> +#include <assert.h> +#include <SDL.h> + +#include "procfs.h" +#include "memview.h" + +static int screen_width = 640; +static int screen_height = 480; + +static double scale = 5; +static double pos_x = 1024; +static double pos_y = 0; + +static int mouse_x = 0; +static int mouse_y = 0; + +SDL_Window *window = NULL; +SDL_Renderer *renderer = NULL; + +int mem_fd; +struct viewer *viewer; + +static void add_scale(double amount) { + pos_x += mouse_x / scale; + pos_y += mouse_y / scale; + scale += amount; + if (scale > 50) scale = 50; + else if (scale < 0.5) scale = 0.5; + pos_x -= mouse_x / scale; + pos_y -= mouse_y / scale; +} + +static void goto_addr(uintptr_t addr) { + int x, y; + to_pos(addr, &x, &y); + pos_x = x; pos_y = y; + pos_x -= (double) screen_width / 2 * scale; + pos_y -= (double) screen_height / 2 * scale; +} + +static void report_error() { + fprintf(stderr, "SDL Error: %s\n", SDL_GetError()); + exit(-1); +} + +static void init_sdl(const char *title) { + if (SDL_Init(SDL_INIT_VIDEO) < 0) report_error(); + window = SDL_CreateWindow(title, + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + screen_width, screen_height, + SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); + if (window == NULL) report_error(); + renderer = SDL_CreateRenderer(window, -1, 0); + if (renderer == NULL) report_error(); +} + +static void deinit_sdl() { + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} + +static void handle_events() { + bool refresh = false; + SDL_Event e; + while (SDL_PollEvent(&e)) { + switch (e.type) { + case SDL_QUIT: + exit(0); break; + case SDL_WINDOWEVENT: + switch (e.window.event) { + case SDL_WINDOWEVENT_RESIZED: + screen_width = e.window.data1; + screen_height = e.window.data2; + SDL_SetWindowSize(window, screen_width, screen_height); + refresh = true; + break; + case SDL_WINDOWEVENT_EXPOSED: + refresh = true; + break; + default: + break; + } + break; + case SDL_MOUSEMOTION: + if (e.motion.state & SDL_BUTTON_RMASK) { + pos_x -= (double) e.motion.xrel / scale; + pos_y -= (double) e.motion.yrel / scale; + refresh = true; + } + mouse_x = e.motion.x; + mouse_y = e.motion.y; + break; + case SDL_MOUSEWHEEL: + if (e.wheel.y == 0) break; + add_scale((double) e.wheel.y / 2); + refresh = true; + break; + default: + break; + } + } + render_pages(viewer, pos_x, pos_y, + screen_width, screen_height, scale, refresh); + SDL_RenderPresent(renderer); +} + +void cleanup() { + destroy_viewer(viewer); + deinit_sdl(); +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + fprintf(stderr, "todo: show usage\n"); + return -1; + } + int pid = atoi(argv[1]); + if (pid == 0) { + fprintf(stderr, "todo: show usage\n"); + return -1; + } + mem_fd = procfs_open(pid); + if (mem_fd == -1) perror("error attaching to process"); + + char title[1024]; + snprintf(title, 1024, "%s [%d]", argv[0], pid); + init_sdl(argv[0]); + viewer = create_viewer(mem_fd, renderer); + if (!viewer) abort(); + atexit(cleanup); + + goto_addr(0x7fea87727000); + + int last_time = 0; + int current_time = 0; + while (1) { + handle_events(); + last_time = current_time; + current_time = SDL_GetTicks(); + int elapsed = current_time - last_time; + if (elapsed < 1000 / 30) + SDL_Delay(1000 / 30 - elapsed); + } +} diff --git a/memview.c b/memview.c new file mode 100644 index 0000000..c2af3ac --- /dev/null +++ b/memview.c @@ -0,0 +1,124 @@ +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <assert.h> +#include <limits.h> +#include <string.h> +#include <SDL.h> + +#include "memview.h" +#include "procfs.h" + +struct viewer { + int fd; + SDL_Renderer *renderer; + SDL_Texture *page_texture; + uint8_t page_buffer[PAGE_SIZE]; +}; + +enum color { + BLACK = 0x00000000, + WHITE = 0xFFFFFFFF, + GREY = 0xFF303030, +}; + +#define BIT_OF(X, I) (((X) >> (I)) & 1ULL) + +static uintptr_t zorder(int x, int y) { + uintptr_t z = 0; + unsigned int ux, uy; + memcpy(&ux, &x, sizeof(unsigned int)); + memcpy(&uy, &y, sizeof(unsigned int)); + // interleaving bits results in a point on the z-order curve + for (int i = 0; i < sizeof(x) * CHAR_BIT; i++) + z |= (BIT_OF(ux, i) << i*2) | (BIT_OF(uy, i) << (i*2 + 1)); + return z; +} + +static void unzorder(uintptr_t z, int *x, int *y) { + unsigned int ux, uy = 0; + for (int i = 0; i < sizeof(z) * CHAR_BIT / 2; i++) { + ux |= BIT_OF(z, i*2) << i; + uy |= BIT_OF(z, i*2 + 1) << i; + } + memcpy(x, &ux, sizeof(int)); + memcpy(y, &uy, sizeof(int)); +} + +uintptr_t to_addr(int x, int y) { + uintptr_t page = zorder(x / PAGE_WIDTH, y / PAGE_HEIGHT); + int offset = x % PAGE_WIDTH + y % PAGE_HEIGHT * PAGE_WIDTH; + return page * PAGE_SIZE + offset; +} + +void to_pos(uintptr_t addr, int *x, int *y) { + int px, py; + unzorder(addr / PAGE_SIZE, &px, &py); + int offset = addr % PAGE_SIZE; + *x = px * PAGE_WIDTH + offset % PAGE_WIDTH; + *y = py * PAGE_HEIGHT + offset / PAGE_WIDTH; +} + +struct viewer *create_viewer(int fd, SDL_Renderer *renderer) { + struct viewer *v = calloc(1, sizeof(struct viewer)); + if (!v) return NULL; + v->fd = fd; v->renderer = renderer; + v->page_texture = SDL_CreateTexture(v->renderer, + SDL_PIXELFORMAT_RGBA32, + SDL_TEXTUREACCESS_STREAMING, + PAGE_WIDTH, PAGE_HEIGHT); + if (!v->page_texture) {free(v); return NULL;} + return v; +} + +void destroy_viewer(struct viewer *v) { + SDL_DestroyTexture(v->page_texture); + free(v); +} + +static void render_page(SDL_Texture *tex, uint8_t *data) { + uint32_t *pixels; int pitch; + assert(SDL_LockTexture(tex, NULL, (void **) &pixels, &pitch) != -1); + if (data) { + for (int i = 0; i < PAGE_SIZE; i++) { + for (int j = 0; j < CHAR_BIT; j++) { + uint8_t bit = (data[i] >> j) & 1; + pixels[i * CHAR_BIT + j] = bit ? WHITE : BLACK; + } + } + } else { + for (int i = 0; i < PAGE_WIDTH * PAGE_HEIGHT; i++) + pixels[i] = GREY; + } + SDL_UnlockTexture(tex); +} + +void render_pages(struct viewer *v, + int x, int y, int width, int height, double scale, bool refresh) { + int page_x = x / PAGE_WIDTH; + int page_y = y / PAGE_HEIGHT; + + int offs_x = x % PAGE_WIDTH * scale; + int offs_y = y % PAGE_HEIGHT * scale; + + int page_width = PAGE_WIDTH * scale; + int page_height = PAGE_HEIGHT * scale; + + int view_width = width / page_width; + int view_height = height / page_height; + + for (int draw_y = -1; draw_y <= view_height + 1; draw_y++) { + for (int draw_x = -1; draw_x <= view_width + 1; draw_x++) { + SDL_Rect src = {0, 0, PAGE_WIDTH, PAGE_HEIGHT}; + SDL_Rect dest = { + draw_x * page_width - offs_x, draw_y * page_height - offs_y, + page_width, page_height + }; + uintptr_t page = zorder(page_x + draw_x, page_y + draw_y); + bool success = read_page(v->fd, page, v->page_buffer) != -1; + // TODO: unless refresh is true, do not redraw if page unaltered + render_page(v->page_texture, success ? v->page_buffer : NULL); + SDL_RenderCopy(v->renderer, v->page_texture, &src, &dest); + } + } +} diff --git a/memview.h b/memview.h new file mode 100644 index 0000000..186bfa8 --- /dev/null +++ b/memview.h @@ -0,0 +1,28 @@ +#ifndef CORE_MEMVIEW_H +#define CORE_MEMVIEW_H + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <SDL.h> + +#include "procfs.h" + +#ifndef PAGE_WIDTH +#define PAGE_WIDTH (256) +#endif + +#define PAGE_HEIGHT ((PAGE_SIZE * CHAR_BIT) / PAGE_WIDTH) + +uintptr_t to_addr(int x, int y); +void to_pos(uintptr_t addr, int *x, int *y); + +struct viewer; +struct viewer *create_viewer(int fd, SDL_Renderer *renderer); +void destroy_viewer(struct viewer *v); + +void render_pages(struct viewer *v, + int x, int y, int width, int height, double scale, bool refresh); +void pencil(struct viewer *v, int x1, int y1, int x2, int y2, bool bit); + +#endif diff --git a/procfs.c b/procfs.c new file mode 100644 index 0000000..63aef3a --- /dev/null +++ b/procfs.c @@ -0,0 +1,28 @@ +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include "procfs.h" + +int procfs_open(pid_t pid) { + char path[2048]; + sprintf(path, "/proc/%d/mem", pid); + return open(path, O_RDWR); +} + +int read_page(int fd, uintptr_t index, uint8_t *data) { + if (lseek(fd, index * PAGE_SIZE, SEEK_SET) == -1) + return -1; + size_t n = 0; + while (n < PAGE_SIZE) { + ssize_t result = read(fd, data + n, PAGE_SIZE - n); + if (result == -1) return -1; + n += result; + } + return 0; +} diff --git a/procfs.h b/procfs.h new file mode 100644 index 0000000..7b81a13 --- /dev/null +++ b/procfs.h @@ -0,0 +1,15 @@ +#ifndef CORE_PROCFS_H +#define CORE_PROCFS_H + +#include <stdlib.h> +#include <stddef.h> +#include <sys/types.h> + +#ifndef PAGE_SIZE +#define PAGE_SIZE (4096) +#endif + +int procfs_open(pid_t pid); +int read_page(int fd, uintptr_t index, uint8_t *data); + +#endif |