From c86ddbbc0eee0e8317d4ed180ee4505f10e8a52c Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 18 Oct 2018 13:25:53 +0300 Subject: Initial commit --- Makefile | 28 ++++++++ proc-region-rw.c | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 Makefile create mode 100644 proc-region-rw.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4086d15 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +PREFIX ?= /usr/local +bindir ?= /bin + +MAKEFLAGS += --no-builtin-rules + +WARNINGS := -Wall -Wextra -Wpedantic -Wformat=2 -Wstrict-aliasing=3 -Wstrict-overflow=5 -Wstack-usage=12500 \ + -Wfloat-equal -Wcast-align -Wpointer-arith -Wchar-subscripts -Warray-bounds=2 + +override CFLAGS ?= -g +override CFLAGS += -std=c99 -D_DEFAULT_SOURCE $(WARNINGS) + +bins = proc-region-rw +all: $(bins) + +$(bins): %: + $(LINK.c) $(filter %.c %.a,$^) $(LDLIBS) -o $@ + +proc-region-rw: proc-region-rw.c + +install-bin: $(bins) + install -Dm755 $^ -t "$(DESTDIR)$(PREFIX)$(bindir)" + +install: install-bin + +clean: + $(RM) $(bins) + +.PHONY: all clean install diff --git a/proc-region-rw.c b/proc-region-rw.c new file mode 100644 index 0000000..0c1ca4f --- /dev/null +++ b/proc-region-rw.c @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include +#include +#include + +struct context { + void *buf; + + struct { + size_t offset, len; + bool with_offset_and_len; + + struct { + FILE *src; + } write; + + enum { + MODE_WRITE, + MODE_READ + } mode; + } op; + + struct { + FILE *mem; + pid_t pid; + } proc; +}; + +static void +usage(const char *argv0) +{ + fprintf(stderr, "usage: %s pid write file [offset len] < regions\n" + " %s pid read [offset len] < regions\n\n" + " regions must be in /proc//maps format", argv0, argv0); + exit(EXIT_FAILURE); +} + +static void +context_init(struct context *ctx, int argc, const char *argv[]) +{ + if (argc < 3) + usage(argv[0]); + + *ctx = (struct context){0}; + ctx->proc.pid = strtoull(argv[1], NULL, 10); + + { + bool w, r; + const char *mode = argv[2]; + if ((w = strcmp(mode, "write")) && (r = strcmp(mode, "read"))) + err(EXIT_FAILURE, "mode must be write or read"); + + ctx->op.mode = (!w ? MODE_WRITE : MODE_READ); + } + + if (ctx->op.mode == MODE_WRITE && argc < 4) + usage(argv[0]); + + if (argc > (ctx->op.mode == MODE_WRITE) + 3) { + if (argc < (ctx->op.mode == MODE_WRITE) + 5) + usage(argv[0]); + + ctx->op.offset = strtoull(argv[(ctx->op.mode == MODE_WRITE) + 3], NULL, 10); + ctx->op.len = strtoull(argv[(ctx->op.mode == MODE_WRITE) + 4], NULL, 10); + ctx->op.with_offset_and_len = true; + } + + if (ctx->op.mode == MODE_WRITE && !(ctx->op.write.src = fopen(argv[3], "rb"))) + err(EXIT_FAILURE, "fopen(%s)", argv[3]); + + char path[128]; + snprintf(path, sizeof(path), "/proc/%u/mem", ctx->proc.pid); + if (!(ctx->proc.mem = fopen(path, "w+b"))) + err(EXIT_FAILURE, "fopen(%s)", path); +} + +static void +context_release(struct context *ctx) +{ + if (ctx->op.write.src) + fclose(ctx->op.write.src); + + fclose(ctx->proc.mem); + free(ctx->buf); + *ctx = (struct context){0}; +} + +static void +for_each_line_in_file(FILE *f, void (*cb)(const char *line, void *data), void *data) +{ + char *buffer = NULL; + size_t step = 1024, allocated = 0, written = 0, read = 0; + do { + if (written + read >= allocated && !(buffer = realloc(buffer, (allocated += step) + 1))) + err(EXIT_FAILURE, "realloc"); + buffer[(written += read)] = 0; + size_t ate = 0; + for (char *line = buffer, *nl; (nl = strchr(line, '\n')); line = nl + 1) { + *nl = 0; + cb(line, data); + ate += nl + 1 - line; + } + memmove(buffer, buffer + ate, (written = written - ate)); + } while ((read = fread(buffer + written, 1, allocated - written, f))); + free(buffer); +} + +static void +region_cb(const char *line, void *data) +{ + struct context *ctx = data; + unsigned long start, end, offset; + if (sscanf(line, "%lx-%lx %*s %lx", &start, &end, &offset) < 3) { + warnx("failed to parse mapping:\n%s", line); + return; + } + + warnx("%s", line); + + offset = (ctx->op.with_offset_and_len ? 0 : offset); + const size_t len = (ctx->op.with_offset_and_len ? ctx->op.len : end - start); + if (start + ctx->op.offset > end) { + warnx("write offset %lx is out of bounds", start + ctx->op.offset); + return; + } + + if (len > (end - start - ctx->op.offset)) { + warnx("%zu bytes doesn't fit in the region", len); + return; + } + + if (!len) + return; + + if (!(ctx->buf = realloc(ctx->buf, len))) + err(EXIT_FAILURE, "realloc"); + + clearerr(ctx->proc.mem); + if (ctx->op.mode == MODE_WRITE) { + if (fseek(ctx->op.write.src, offset, SEEK_SET) != 0) + err(EXIT_FAILURE, "fseek"); + + const size_t rd = fread(ctx->buf, 1, len, ctx->op.write.src); + + if (fseek(ctx->proc.mem, start + ctx->op.offset, SEEK_SET) != 0) + err(EXIT_FAILURE, "fseek"); + + const size_t wd = fwrite(ctx->buf, 1, rd, ctx->proc.mem); + + if (ferror(ctx->proc.mem)) { + warn("fread(/proc/%u/mem)", ctx->proc.pid); + } else { + warnx("wrote %lu bytes from offset 0x%lx to offset 0x%lx", wd, offset, start + ctx->op.offset); + } + } else { + if (fseek(ctx->proc.mem, start + ctx->op.offset, SEEK_SET) != 0) + err(EXIT_FAILURE, "fseek"); + + const size_t rd = fread(ctx->buf, 1, len, ctx->proc.mem); + + if (ferror(ctx->proc.mem)) + warn("fread(/proc/%u/mem)", ctx->proc.pid); + + if (fwrite(ctx->buf, 1, rd, stdout) != rd) + err(EXIT_FAILURE, "fwrite"); + } +} + +int +main(int argc, const char *argv[]) +{ + struct context ctx; + context_init(&ctx, argc, argv); + + if (ptrace(PTRACE_ATTACH, ctx.proc.pid, NULL, NULL) == -1L) + err(EXIT_FAILURE, "ptrace(PTRACE_ATTACH, %u, NULL, NULL)", ctx.proc.pid); + + { + int status; + if (waitpid(ctx.proc.pid, &status, 0) == -1 || !WIFSTOPPED(status)) + err(EXIT_FAILURE, "waitpid"); + } + + for_each_line_in_file(stdin, region_cb, &ctx); + ptrace(PTRACE_DETACH, ctx.proc.pid, 1, 0); + context_release(&ctx); + return EXIT_SUCCESS; +} -- cgit v1.2.3-70-g09d2