/* readmem.c * dumps the virtual memory of a linux process using ptrace+/dev/pid/mem + dev/pid/maps * (c) 2007 Y. Guillot * distributed under the terms of the WtfPLv2 */ #define _LARGEFILE64_SOURCE #ifndef PAGE_SIZE /* size of a memory page */ #define PAGE_SIZE 4096 #endif #ifndef WRITE_TIMEOUT /* abort dumping the process if we can't write a memory page to stdout in less than WRITE_TIMEOUT seconds */ #define WRITE_TIMEOUT 5 #endif #include #include #include #include #include #include #define FAIL(msg) do { fprintf(stderr, msg); exit(1); } while (0) /* read one hex unsigned long long from fd, then read until end of line * so when we're called repeatedly we return the number at the beginning of each line * return 0 on end of file */ int read_maps_addr(int fd, unsigned long long *out) { *out = 0; char c = 0; int want_bytes = 1; while (read(fd, &c, 1) == 1) { if (c == '\n') return 1; if (!want_bytes) continue; switch(c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': *out <<= 4; *out |= c - '0'; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': c -= 0x20; /* convert to uppercase */ /* fallthrough */ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': *out <<= 4; *out |= 10 + c - 'A'; break; default: want_bytes = 0; } } return 0; } static volatile int timed_out = 0; void handle_signal(int signal) { if (signal == SIGALRM) timed_out = 1; } int main(int argc, char **argv) { if (argc < 2) FAIL("usage: readmem \n"); int pid = atoi(argv[1]); static char path_mem[128]; snprintf(path_mem, sizeof(path_mem), "/proc/%d/mem", pid); int fd_mem = open(path_mem, O_RDONLY); if (fd_mem == -1) FAIL("open mem failed: %m\n"); static char path_maps[128]; snprintf(path_maps, sizeof(path_maps), "/proc/%d/maps", pid); int fd_maps = open(path_maps, O_RDONLY); if (fd_maps == -1) FAIL("open maps failed: %m\n"); int fd_out = 1; /* stdout */ long ptret = ptrace(PTRACE_ATTACH, pid, 0, 0); if (ptret == -1) FAIL("ptrace failed: %m\n"); /* setup timeout on write(), in case we have: $ readmem | gzip > mem.gz * without timeout, the write() would block waiting for gzip to read the data and we'd deadlock */ struct sigaction sa = { .sa_handler = handle_signal, .sa_flags = SA_RESETHAND }; sigaction(SIGALRM, &sa, NULL); static unsigned char page[PAGE_SIZE]; unsigned long long offset = 0; while (read_maps_addr(fd_maps, &offset) || offset) { while (!timed_out) { /* read each page, stop on 1st invalid read */ lseek64(fd_mem, offset, SEEK_SET); ssize_t ret; ret = read(fd_mem, page, sizeof(page)); if (ret < 0) break; #ifdef VERBOSE if (ret >= 0) fprintf(stderr, "got page at %llx\n", offset); #endif /* reset the timer for the timeout */ alarm(5); if (ret > 0) write(fd_out, page, sizeof(page)); /* dont print anything on timeout, this may lock the process again */ offset += sizeof(page); } } ptrace(PTRACE_DETACH, pid, 0, 0); close(fd_mem); return 0; }