/** * uninformed research * http://www.uninformed.org * * Search/replace/dump for memory in a running process. * Search/dump for memory in core files. * * Props to thief for the idea :) * * This only works on Linux I'd assume. * * skape * mmiller@hick.org * * gcc -Wall -O3 memgrep.c -o memgrep */ #include #include #include #include #include #include #include #include #include #include #include #include #include const char memgrepVersion[] = "v0.5.0 01/01/2003"; #define MEMGREP_CMD_INITIALIZE 0x00000001 // Initialize from a given medium. #define MEMGREP_CMD_DEINITIALIZE 0x00000002 // Deinitialize from a given medium. #define MEMGREP_CMD_SET 0x00000003 // Used to set parameters. #define MEMGREP_CMD_GET 0x00000004 // Used to get parameters. #define MEMGREP_CMD_POPULATE 0x00000005 // Populate a given set of addresses from a string. #define MEMGREP_CMD_SEARCH 0x00000006 // Search memory #define MEMGREP_CMD_REPLACE 0x00000007 // Replace memory #define MEMGREP_CMD_SEARCHREPLACE 0x00000008 // Search and replace matches. #define MEMGREP_CMD_DUMP 0x00000009 // Dump memory #define MEMGREP_PARAM_FLAGS 0x00000001 // Identifier used for setting 'flags' with MEMGREP_CMD_SET #define MEMGREP_PARAM_LENGTH 0x00000002 // Length for search/replace/dump. Default is 'max' #define MEMGREP_PARAM_PADDING 0x00000003 // Padding space +/- for dump. #define MEMGREP_FLAG_VERBOSE (1 << 0) // Be verbose #define MEMGREP_FLAG_PROMPT (1 << 1) // Prompt before replacing #define MEMGREP_FLAG_DUMPCLEAN (1 << 2) // Dump data in a readable text, not hex. enum MemoryMedium { MEMORY_MEDIUM_UNKNOWN = 0, MEMORY_MEDIUM_PID = 1, MEMORY_MEDIUM_CORE = 2 }; typedef struct _process_section_addrs { unsigned long rodata; // read-only data, static unsigned long data; // data, static unsigned long bss; // bss, static unsigned long stack; // stack, dynamic } PROCESS_SECTION_ADDRS; typedef struct _core_memory_sections { unsigned long vma; // Virtual memory address unsigned long length; // Memory length unsigned long rma; // Real memory address (mmap) } CORE_MEMORY_SECTIONS; typedef struct _mem_ctx_core_data { int fd; Elf32_Ehdr elfHeader; // core file elf header Elf32_Phdr *programHeaders; // Program header array for the core file CORE_MEMORY_SECTIONS *sections; // Array of sections unsigned long numSections; // Number of sections } MEM_CTX_CORE_DATA; struct _mem_ctx; typedef struct _memgrep_functions { unsigned long (*open)(struct _mem_ctx *ctx); unsigned long (*close)(struct _mem_ctx *ctx); unsigned long (*getSections)(struct _mem_ctx *ctx); unsigned char *(*get)(struct _mem_ctx *ctx, unsigned long addr, unsigned long length); unsigned long (*put)(struct _mem_ctx *ctx, unsigned long addr, unsigned char *buf, unsigned long bufLength); unsigned long (*populateKeyword)(struct _mem_ctx *ctx, const char *keyword); } MEMGREP_FUNCTIONS; typedef struct _mem_ctx { unsigned long flags; // Flags enum MemoryMedium medium; int pid; // Process id char *core; // Core file MEMGREP_FUNCTIONS functions; // Dynamic functions PROCESS_SECTION_ADDRS sections; // Section addresses (bss, data, rodata, stack) unsigned long *addrs; // Start address(es) unsigned long numAddrs; // Number of start address(es) unsigned long length; // The length to operate on unsigned long padding; // The padding used when dumping // Core file extended data MEM_CTX_CORE_DATA coreData; } MEM_CTX; unsigned long memgrep(MEM_CTX *ctx, unsigned long cmd, unsigned long param, unsigned long data); unsigned long memgrep_initialize(MEM_CTX *ctx, enum MemoryMedium medium, void *data); // 1 for success, 0 for failure unsigned long memgrep_deinitialize(MEM_CTX *ctx); // 1 for success, 0 for failure unsigned long memgrep_set(MEM_CTX *ctx, unsigned long param, unsigned long data); // 1 for success, 0 for failure unsigned long memgrep_get(MEM_CTX *ctx, unsigned long param); // the value associated w/ the param unsigned long memgrep_populate(MEM_CTX *ctx, const char *addresses); // number of addresses populated unsigned long memgrep_search(MEM_CTX *ctx, const char *searchPhrase); // number of addresses found unsigned long memgrep_replace(MEM_CTX *ctx, const char *replacePhrase); // number of addresses replaced unsigned long memgrep_searchreplace(MEM_CTX *ctx, const char *searchPhrase, const char *replacePhrase); // number of addresses search/replaced unsigned long memgrep_dump(MEM_CTX *ctx); // 1 for success, 0 for failure void safeCleanup(int signal); // Pid operators unsigned long pid_open(MEM_CTX *ctx); unsigned long pid_close(MEM_CTX *ctx); unsigned long pid_getSections(MEM_CTX *ctx); unsigned char *pid_get(MEM_CTX *ctx, unsigned long addr, unsigned long length); unsigned long pid_put(MEM_CTX *ctx, unsigned long addr, unsigned char *buf, unsigned long bufLength); unsigned long pid_populateKeyword(MEM_CTX *ctx, const char *keyword); // Core operators unsigned long core_open(MEM_CTX *ctx); unsigned long core_close(MEM_CTX *ctx); unsigned long core_getSections(MEM_CTX *ctx); unsigned char *core_get(MEM_CTX *ctx, unsigned long addr, unsigned long length); unsigned long core_put(MEM_CTX *ctx, unsigned long addr, unsigned char *buf, unsigned long bufLength); unsigned long core_populateKeyword(MEM_CTX *ctx, const char *keyword); unsigned long translateToHex(const char *fullString, unsigned char **buf, unsigned long *bufLength); unsigned long translateFormatToHex(const char *format, const char *string, unsigned char **buf, unsigned long *bufLength); void displayVersion(); void displayHelp(); unsigned long _pid = 0; int main(int argc, char **argv) { char *addrs = NULL, *from = NULL, *to = NULL, *core = NULL; enum MemoryMedium medium = MEMORY_MEDIUM_UNKNOWN; unsigned long flags = MEMGREP_FLAG_VERBOSE; unsigned long cmd = 0; extern char *optarg; int c, pid = 0; MEM_CTX ctx; memset(&ctx, 0, sizeof(ctx)); while ((c = getopt(argc, argv, "p:o:dsra:l:f:t:b:cvh")) != EOF) { switch (c) { case 'p': medium = MEMORY_MEDIUM_PID; pid = atoi(optarg) & 0xFFFF; break; case 'o': medium = MEMORY_MEDIUM_CORE; core = optarg; break; case 'd': cmd = MEMGREP_CMD_DUMP; break; case 's': if (cmd == MEMGREP_CMD_REPLACE) cmd = MEMGREP_CMD_SEARCHREPLACE; else cmd = MEMGREP_CMD_SEARCH; break; case 'r': if (cmd == MEMGREP_CMD_SEARCH) cmd = MEMGREP_CMD_SEARCHREPLACE; else cmd = MEMGREP_CMD_REPLACE; break; case 'a': addrs = optarg; break; case 'l': memgrep(&ctx, MEMGREP_CMD_SET, MEMGREP_PARAM_LENGTH, strtoul(optarg, NULL, 10)); break; case 'f': from = optarg; break; case 't': to = optarg; break; case 'b': memgrep(&ctx, MEMGREP_CMD_SET, MEMGREP_PARAM_PADDING, strtoul(optarg, NULL, 10)); break; case 'c': flags |= MEMGREP_FLAG_DUMPCLEAN; break; case 'v': displayVersion(); break; case 'h': displayHelp(); break; } } if (cmd == 0 || (medium == MEMORY_MEDIUM_UNKNOWN)) displayHelp(); memgrep(&ctx, MEMGREP_CMD_SET, MEMGREP_PARAM_FLAGS, flags); if (!memgrep(&ctx, MEMGREP_CMD_INITIALIZE, (unsigned long)medium, (medium == MEMORY_MEDIUM_PID)?pid:(unsigned long)core)) return 0; // Populate addresses if (!memgrep(&ctx, MEMGREP_CMD_POPULATE, (unsigned long)addrs, 0)) { fprintf(stderr, "memgrep: No addresses specified.\n"); return 0; } switch (cmd) { case MEMGREP_CMD_SEARCH: memgrep(&ctx, cmd, (unsigned long)from, 0); break; case MEMGREP_CMD_REPLACE: memgrep(&ctx, cmd, (unsigned long)to, 0); break; case MEMGREP_CMD_SEARCHREPLACE: memgrep(&ctx, cmd, (unsigned long)from, (unsigned long)to); break; case MEMGREP_CMD_DUMP: memgrep(&ctx, cmd, 0, 0); break; default: break; } memgrep(&ctx, MEMGREP_CMD_DEINITIALIZE, 0, 0); return 0; } unsigned long memgrep(MEM_CTX *ctx, unsigned long cmd, unsigned long param, unsigned long data) { unsigned long ret = 0; switch (cmd) { case MEMGREP_CMD_INITIALIZE: ret = memgrep_initialize(ctx, (enum MemoryMedium)param, (void *)data); break; case MEMGREP_CMD_DEINITIALIZE: ret = memgrep_deinitialize(ctx); break; case MEMGREP_CMD_SET: ret = memgrep_set(ctx, param, data); break; case MEMGREP_CMD_GET: ret = memgrep_get(ctx, param); break; case MEMGREP_CMD_POPULATE: ret = memgrep_populate(ctx, (const char *)param); break; case MEMGREP_CMD_SEARCH: ret = memgrep_search(ctx, (const char *)param); break; case MEMGREP_CMD_REPLACE: ret = memgrep_replace(ctx, (const char *)param); break; case MEMGREP_CMD_SEARCHREPLACE: ret = memgrep_searchreplace(ctx, (const char *)param, (const char *)data); break; case MEMGREP_CMD_DUMP: ret = memgrep_dump(ctx); break; default: break; } return ret; } unsigned long memgrep_initialize(MEM_CTX *ctx, enum MemoryMedium medium, void *data) { unsigned long ret = 0; ctx->medium = medium; switch (medium) { case MEMORY_MEDIUM_PID: ctx->pid = (unsigned long)data; ctx->functions.open = pid_open; ctx->functions.close = pid_close; ctx->functions.getSections = pid_getSections; ctx->functions.get = pid_get; ctx->functions.put = pid_put; ctx->functions.populateKeyword = pid_populateKeyword; break; case MEMORY_MEDIUM_CORE: ctx->core = strdup((data)?(char*)data:""); if (!ctx->core) return 0; ctx->functions.open = core_open; ctx->functions.close = core_close; ctx->functions.getSections = core_getSections; ctx->functions.get = core_get; ctx->functions.put = core_put; ctx->functions.populateKeyword = core_populateKeyword; break; default: if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stderr, "memgrep_initialize(): Invalid medium specified.\n"); return 0; } if (!(ret = ctx->functions.open(ctx))) { if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stderr, "memgrep_initialize(): Couldn't open medium device.\n"); return 0; } return ctx->functions.getSections(ctx); } unsigned long memgrep_deinitialize(MEM_CTX *ctx) { unsigned long ret = ctx->functions.close(ctx); memset(&ctx->functions, 0, sizeof(MEMGREP_FUNCTIONS)); switch (ctx->medium) { case MEMORY_MEDIUM_CORE: if (ctx->core) free(ctx->core); break; default: break; } ctx->medium = MEMORY_MEDIUM_UNKNOWN; if (ctx->addrs) free(ctx->addrs); return ret; } unsigned long memgrep_set(MEM_CTX *ctx, unsigned long param, unsigned long data) { unsigned long ret = 0; switch (param) { case MEMGREP_PARAM_FLAGS: ctx->flags = data; break; case MEMGREP_PARAM_LENGTH: ctx->length = data; break; case MEMGREP_PARAM_PADDING: ctx->padding = data; break; default: break; } return ret; } unsigned long memgrep_get(MEM_CTX *ctx, unsigned long param) { unsigned long ret = 0; switch (param) { case MEMGREP_PARAM_FLAGS: ret = ctx->flags; break; case MEMGREP_PARAM_LENGTH: ret = ctx->length; break; case MEMGREP_PARAM_PADDING: ret = ctx->padding; break; default: break; } return ret; } unsigned long memgrep_populate(MEM_CTX *ctx, const char *addresses) { const char *comma = NULL, *curr = addresses; unsigned char done = 0; while (curr && !done) { comma = strchr(curr, ','); if (comma) *(char *)comma = 0; else done = 1; // Invalid format if (strlen(curr) < 2) break; if (!isdigit(*curr)) ctx->functions.populateKeyword(ctx, curr); else { if (!ctx->addrs) ctx->addrs = (unsigned long *)malloc((++ctx->numAddrs) * sizeof(unsigned long)); else ctx->addrs = (unsigned long *)realloc(ctx->addrs, (++ctx->numAddrs) * sizeof(unsigned long)); if (curr[1] == 'x') ctx->addrs[ctx->numAddrs-1] = strtoul(curr+2, NULL, 16); else ctx->addrs[ctx->numAddrs-1] = strtoul(curr, NULL, 16); } if (comma) *(char *)comma = ','; curr = comma + 1; } return ctx->numAddrs; } unsigned long memgrep_replace(MEM_CTX *ctx, const char *replacePhrase) { unsigned long ret = 0, x, replaceLength = 0; unsigned char *replaceBuf = NULL; if (!translateToHex(replacePhrase, &replaceBuf, &replaceLength)) { if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stderr, "memgrep_replace(): Invalid replace phrase specified.\n"); return 0; } for (x = 0; x < ctx->numAddrs; x++) { if (ctx->flags & MEMGREP_FLAG_PROMPT) { char rep[32]; fprintf(stdout, "Replace %lu bytes of memory at %.8x [Y/n]? ", replaceLength, (unsigned int)ctx->addrs[x]); fflush(stdout); fgets(rep, sizeof(rep) - 1, stdin); if (rep[0] == 'n' || rep[1] == 'N') continue; } if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stdout, "Replacing memory %.8x with %lu bytes of data...\n", (unsigned int)ctx->addrs[x], replaceLength); if (ctx->functions.put(ctx, ctx->addrs[x], replaceBuf, replaceLength)) ret++; } free(replaceBuf); return ret; } unsigned long memgrep_search(MEM_CTX *ctx, const char *searchPhrase) { return memgrep_searchreplace(ctx, searchPhrase, NULL); } unsigned long memgrep_searchreplace(MEM_CTX *ctx, const char *searchPhrase, const char *replacePhrase) { unsigned long ret = 0; unsigned char *searchBuf = NULL, *replaceBuf = NULL; unsigned long searchLength = 0, replaceLength = 0, x; if ((!searchPhrase) || (!translateToHex(searchPhrase, &searchBuf, &searchLength))) { if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stderr, "memgrep_search(): You did not specify a valid search phrase.\n"); return 0; } if (replacePhrase) translateToHex(replacePhrase, &replaceBuf, &replaceLength); for (x = 0; x < ctx->numAddrs; x++) { unsigned long addr = ctx->addrs[x], end = (ctx->length)?ctx->addrs[x] + ctx->length:0xffffffff; unsigned long left = end - addr, y, getLength, slope = 0; unsigned char *buf = NULL, *potential; if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stdout, "Searching 0x%.8x => 0x%.8x...\n", (unsigned int)ctx->addrs[x], (unsigned int)end); while (left > 0) { slope = getLength = (left < 256)?left:256; if (!(buf = ctx->functions.get(ctx, addr, getLength))) break; for (y = 0; y < getLength; y++) { if (buf[y] != searchBuf[0]) continue; // Potential match? if (!(potential = ctx->functions.get(ctx, addr + y, searchLength))) continue; // So far so good. if (memcmp(potential, searchBuf, searchLength) == 0) { // If we're replacing if (replaceBuf) { ctx->functions.put(ctx, addr + y, replaceBuf, replaceLength); if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stdout, " replaced at 0x%.8x\n", (unsigned int)(addr + y)); slope = replaceLength + y; } else { if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stdout, " found at 0x%.8x\n", (unsigned int)(addr + y)); slope = searchLength + y; } ret++; break; } free(potential); } left -= slope; addr += slope; free(buf); } } if (searchBuf) free(searchBuf); if (replaceBuf) free(replaceBuf); return ret; } unsigned long memgrep_dump(MEM_CTX *ctx) { unsigned long ret = 0, x, y, base, len; if (!ctx->length) return 0; for (x = 0; x < ctx->numAddrs; x++) { unsigned char *buf = ctx->functions.get(ctx, (base = ctx->addrs[x] - ctx->padding), (len = ctx->length + (ctx->padding * 2))); unsigned long data, count = 0, inc = sizeof(unsigned long); fprintf(stdout, "%lu bytes starting at %.8x (+/- %lu)...\n%.8x: ", ctx->length, (unsigned int)ctx->addrs[x], ctx->padding, (unsigned int)base); if (!buf) continue; if (ctx->flags & MEMGREP_FLAG_DUMPCLEAN) inc = 1; for (y = 0; y < len; y += inc) { if (ctx->flags & MEMGREP_FLAG_DUMPCLEAN) { if (isdigit(buf[y]) || isalpha(buf[y]) || buf[y] == ' ') fprintf(stdout, "%c", buf[y]); else fprintf(stdout, "."); count++; if (count == 16) { fprintf(stdout, "\n"); if (y + 1 < len) fprintf(stdout, "%.8x: ", (unsigned int)(base + y + 1)); count = 0; } } else { memcpy(&data, buf + y, sizeof(unsigned long)); fprintf(stdout, "%.8x ", (unsigned int)data); count++; if (count == 4) { fprintf(stdout, "\n"); if (y + 4 < len) fprintf(stdout, "%.8x: ", (unsigned int)(base + y + 4)); count = 0; } } } fprintf(stdout, "\n"); free(buf); } return ret; } unsigned long translateToHex(const char *fullString, unsigned char **buf, unsigned long *bufLength) { const char *comma = strchr(fullString, ','); unsigned long ret = 0; if (!comma) return 0; *(char *)comma = 0; ret = translateFormatToHex(fullString, comma + 1, buf, bufLength); *(char *)comma = ','; return ret; } unsigned long translateFormatToHex(const char *format, const char *string, unsigned char **buf, unsigned long *bufLength) { *bufLength = 0; switch (format[0]) { case 's': // String { unsigned long x; *bufLength = strlen(string); *buf = (unsigned char *)malloc(*bufLength); if (!*buf) return 0 ; for (x = 0; x < *bufLength; x++) (*buf)[x] = string[x]; } break; case 'i': // Integer { *bufLength = sizeof(long); *buf = (unsigned char *)malloc(*bufLength); *(*((long **)buf)) = strtol(string, NULL, 10); } break; case 'x': // Hex { unsigned long x, stringLen, bufPos = 0; char hex[3] = { 0, 0, 0 }; *bufLength = (stringLen = strlen(string)) / 2; *buf = (unsigned char *)malloc(*bufLength); if (!*buf) return 0 ; for (x = 0; x < stringLen; x += 2) { hex[0] = string[x]; hex[1] = string[x+1]; (*buf)[bufPos++] = strtoul(hex, NULL, 16) & 0xFF; } } break; } return *bufLength; } // // Pid operators // unsigned long pid_open(MEM_CTX *ctx) { if (ptrace(PTRACE_ATTACH, ctx->pid, NULL, NULL) < 0) { if (ctx->flags & MEMGREP_FLAG_VERBOSE) perror("ptrace(ATTACH)"); return 0; } // waitpid doesn't work for me, anyone else had that problem? while (ptrace(PTRACE_PEEKDATA, ctx->pid, 0, NULL) == -1 && errno == 3) usleep(100); // For signal handling _pid = ctx->pid; signal(SIGINT, safeCleanup); return 1; } unsigned long pid_close(MEM_CTX *ctx) { if (ptrace(PTRACE_DETACH, ctx->pid, NULL, NULL) < 0) perror("ptrace(DETACH)"); signal(SIGINT, SIG_DFL); return 1; } void safeCleanup(int signal) { MEM_CTX t; t.pid = _pid; pid_close(&t); exit(0); } unsigned long pid_getSections(MEM_CTX *ctx) { Elf32_Ehdr elfHeader; Elf32_Shdr *sectionHeaders = NULL, *stringTableHeader = NULL; unsigned long index = 0; char path[1024], *stringTable = NULL; int fd = 0; struct user_regs_struct regs; path[sizeof(path) - 1] = path[0] = 0; snprintf(path, sizeof(path) - 1, "/proc/%d/exe", ctx->pid); if ((fd = open(path, O_RDONLY)) <= 0) return 0; do { if (lseek(fd, 0, SEEK_SET) < 0) break; if (read(fd, &elfHeader, sizeof(elfHeader)) < 0) break; if (!(sectionHeaders = (Elf32_Shdr *)malloc(elfHeader.e_shentsize * elfHeader.e_shnum))) break; if (lseek(fd, elfHeader.e_shoff, SEEK_SET) < 0) break; // Read the section headers if (read(fd, sectionHeaders, elfHeader.e_shentsize * elfHeader.e_shnum) < 0) break; // Read the string table if (!(stringTableHeader = §ionHeaders[elfHeader.e_shstrndx])) break; if (lseek(fd, stringTableHeader->sh_offset, SEEK_SET) < 0) break; if (!(stringTable = (char *)malloc(stringTableHeader->sh_size))) break; if (read(fd, stringTable, stringTableHeader->sh_size) < 0) break; for (index = 0; index < elfHeader.e_shnum; index++) { const char *name = &stringTable[sectionHeaders[index].sh_name]; if (!name) continue; if (!strcmp(name, ".bss")) ctx->sections.bss = sectionHeaders[index].sh_addr; else if (!strcmp(name, ".rodata")) ctx->sections.rodata = sectionHeaders[index].sh_addr; else if (!strcmp(name, ".data")) ctx->sections.data = sectionHeaders[index].sh_addr; } } while (0); if (stringTable) free(stringTable); if (sectionHeaders) free(sectionHeaders); close(fd); // Get current stack address memset(®s, 0, sizeof(regs)); if (ptrace(PTRACE_GETREGS, ctx->pid, NULL, ®s) != -1) ctx->sections.stack = regs.esp; else { if (ctx->flags & MEMGREP_FLAG_VERBOSE) perror("ptrace(GETREGS)"); } return 1; } unsigned char *pid_get(MEM_CTX *ctx, unsigned long addr, unsigned long length) { unsigned char *ret = (unsigned char *)malloc(length); unsigned long x = addr, end = addr + length, data, left = length, y, retPos = 0; if (!ret) return NULL; memset(ret, 0, length); for (; x < end; x += 4) { // If the first address is non-readable, break out and return null if (((data = ptrace(PTRACE_PEEKDATA, ctx->pid, x, NULL)) == -1) && (errno == 5)) { if (x == addr) { free(ret); ret = NULL; break; } } for (y = 0; y < sizeof(unsigned long) && left != 0; y++) { ret[retPos++] = ((unsigned char *)&data)[y]; left--; } } return ret; } unsigned long pid_put(MEM_CTX *ctx, unsigned long addr, unsigned char *buf, unsigned long length) { unsigned long x = addr, end = addr + length, data, pokedata, left = length, pos = 0; int y; for (; x < end; x += 4) { data = ptrace(PTRACE_PEEKDATA, ctx->pid, x, NULL); for (y = 0; y < sizeof(unsigned long); y++) { if (left == 0) ((unsigned char *)&pokedata)[y] = ((unsigned char *)&data)[y]; else { ((unsigned char *)&pokedata)[y] = buf[pos++]; left--; } } ptrace(PTRACE_POKEDATA, ctx->pid, x, pokedata); } return 1; } unsigned long pid_populateKeyword(MEM_CTX *ctx, const char *keyword) { unsigned long addr = 0; if (!strcmp(keyword, "bss")) addr = ctx->sections.bss; else if (!strcmp(keyword, "stack")) addr = ctx->sections.stack; else if (!strcmp(keyword, "rodata")) addr = ctx->sections.rodata; else if (!strcmp(keyword, "data")) addr = ctx->sections.data; else if (!strcmp(keyword, "all")) { pid_populateKeyword(ctx, "rodata"); pid_populateKeyword(ctx, "data"); pid_populateKeyword(ctx, "bss"); pid_populateKeyword(ctx, "stack"); } else if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stderr, "pid_populateKeyword(): warning: unknown keyword '%s'\n", keyword); if (addr) { if (!ctx->addrs) ctx->addrs = (unsigned long *)malloc((++ctx->numAddrs) * sizeof(unsigned long)); else ctx->addrs = (unsigned long *)realloc(ctx->addrs, (++ctx->numAddrs) * sizeof(unsigned long)); ctx->addrs[ctx->numAddrs - 1] = addr; return 1; } return 0; } // // Core operators // unsigned long core_open(MEM_CTX *ctx) { int fd = 0, index; // Open the file if ((fd = ctx->coreData.fd = open(ctx->core, O_RDONLY)) <= 0) { if (ctx->flags & MEMGREP_FLAG_VERBOSE) perror("core_open()"); return 0; } // Load the elf header and section headers. do { if (lseek(fd, 0, SEEK_SET) < 0) break; if (read(fd, &ctx->coreData.elfHeader, sizeof(ctx->coreData.elfHeader)) < 0) break; if (!(ctx->coreData.programHeaders = (Elf32_Phdr *)malloc(ctx->coreData.elfHeader.e_phentsize * ctx->coreData.elfHeader.e_phnum))) break; if (lseek(fd, ctx->coreData.elfHeader.e_phoff, SEEK_SET) < 0) break; // Read the programs headers if (read(fd, ctx->coreData.programHeaders, ctx->coreData.elfHeader.e_phentsize * ctx->coreData.elfHeader.e_phnum) < 0) break; if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stdout, "core file '%s' has the following VMA's:\n", ctx->core); for (index = 0; index < ctx->coreData.elfHeader.e_phnum; index++) { unsigned long rma = 0; // We'll mmap everything but executable code and as long as it's a loadable segment. if (ctx->coreData.programHeaders[index].p_flags & PF_X) continue; if (ctx->coreData.programHeaders[index].p_type != PT_LOAD) continue; // No length? too bad. if (!ctx->coreData.programHeaders[index].p_filesz) continue; // mmap this section rma = (unsigned long)mmap(NULL, ctx->coreData.programHeaders[index].p_filesz, PROT_READ, MAP_PRIVATE, fd, ctx->coreData.programHeaders[index].p_offset); if (!rma) continue; if (!ctx->coreData.sections) ctx->coreData.sections = (CORE_MEMORY_SECTIONS *)malloc(sizeof(CORE_MEMORY_SECTIONS)); else ctx->coreData.sections = (CORE_MEMORY_SECTIONS *)realloc(ctx->coreData.sections, sizeof(CORE_MEMORY_SECTIONS) * (ctx->coreData.numSections + 1)); ctx->coreData.sections[ctx->coreData.numSections].vma = ctx->coreData.programHeaders[index].p_vaddr; ctx->coreData.sections[ctx->coreData.numSections].rma = rma; ctx->coreData.sections[ctx->coreData.numSections].length = ctx->coreData.programHeaders[index].p_filesz; if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stdout, " %.8x -> %.8x (%lu bytes)\n", (unsigned int)ctx->coreData.sections[ctx->coreData.numSections].vma, (unsigned int)(ctx->coreData.sections[ctx->coreData.numSections].vma + ctx->coreData.sections[ctx->coreData.numSections].length), ctx->coreData.sections[ctx->coreData.numSections].length); ctx->coreData.numSections++; } } while (0); if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stdout, "\n"); return ctx->coreData.numSections; } unsigned long core_close(MEM_CTX *ctx) { if (ctx->coreData.fd) close(ctx->coreData.fd); if (ctx->coreData.programHeaders) free(ctx->coreData.programHeaders); if (ctx->coreData.sections) { int x = 0; for (; x < ctx->coreData.numSections; x++) { if (ctx->coreData.sections[x].rma) munmap((void *)ctx->coreData.sections[x].rma, ctx->coreData.sections[x].length); } free(ctx->coreData.sections); ctx->coreData.sections = NULL; ctx->coreData.numSections = 0; } return 0; } unsigned long core_getSections(MEM_CTX *ctx) { return 1; } unsigned char *core_get(MEM_CTX *ctx, unsigned long addr, unsigned long length) { unsigned long copyLength = length; unsigned char *ret = NULL; int index = 0; for (; index < ctx->coreData.numSections; index++) { unsigned long end = ctx->coreData.sections[index].vma + ctx->coreData.sections[index].length; void *real = NULL; // In between our range? if (!(addr >= ctx->coreData.sections[index].vma) || !(addr < end)) continue; if (addr + length > end) copyLength = end - addr; real = (void *)(ctx->coreData.sections[index].rma + (addr - ctx->coreData.sections[index].vma)); if (!(ret = (unsigned char *)malloc(length))) break; memset(ret, 0, length); memcpy(ret, real, copyLength); break; } return ret; } unsigned long core_put(MEM_CTX *ctx, unsigned long addr, unsigned char *buf, unsigned long bufLength) { if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stderr, "core_put(): warning: memory write access is not supported with core files.\n"); return 0; } unsigned long core_populateKeyword(MEM_CTX *ctx, const char *keyword) { if (!strcmp(keyword, "all")) { int x = 0; for (; x < ctx->coreData.numSections; x++) { if (ctx->coreData.sections[x].vma) { if (!ctx->addrs) ctx->addrs = (unsigned long *)malloc((++ctx->numAddrs) * sizeof(unsigned long)); else ctx->addrs = (unsigned long *)realloc(ctx->addrs, (++ctx->numAddrs) * sizeof(unsigned long)); ctx->addrs[ctx->numAddrs - 1] = ctx->coreData.sections[x].vma; } } return 1; } else if (ctx->flags & MEMGREP_FLAG_VERBOSE) fprintf(stderr, "core_populateKeyword(): warning: unknown keyword '%s'\n", keyword); return 0; } void displayHelp() { fprintf(stdout, "memgrep -- Run-time/core-time memory searching, dumping and modifying utility.\n" "Usage: ./memgrep [-p pid] [-o core] [-d] [-r] [-s] [-a addr1,addr2,bss,addr3] [-l length] [-f fmt,search data]\n" " [-t fmt,replace data] [-b pad] [-c] [-v] [-h]\n" "\n" " -p [pid] The process id to operate on.\n" " -o [core] The core file to operate on.\n" " -d Dump memory from the specified address(es) for the given length (-l).\n" " -r Replace memory at the specified address(es). If -s is also specified,\n" " only memory that matches the search criteria will be replaced\n" " -s Search memory at the specified address(es).\n" " -a [addr] The address(es) to operate on seperated by commas. Addresses can be\n" " in the following format:\n" " 0x821c4ac\n" " 821c4ac\n" " Also, the following keywords can be used:\n" " bss -> Uses the VMA associated with the .bss section (commonly heap).\n" " stack -> Dynamically determines the current stack pointer.\n" " rodata -> Uses the VMA associated with the .rodata section (read-only data, ie, static text).\n" " data -> Uses the VMA associated with the .data section (data, ie, global variables).\n" " all -> Uses bss, stack, rodata, data. Only valid keyword for core files.\n" " -l [len] The length to use when searching or dumping. A length of 0 means search\n" " till end-of-memory.\n" " -f [data] This specifies the search criteria. Multiple formats are accepted for ease\n" " of use. Below are accepted formats and their examples:\n" " s -> String format (Ex: 's,Testing')\n" " x -> Hex format (Ex: 'x,00414100AB')\n" " i -> Integer format (Ex: 'i,4724')\n" " -t [data] This specifies the replace data. The same formats used with the -f parameter\n" " are valid for the -t parameter.\n" " -b [pad] Number of bytes of padding to use around dump addresses (default is 0).\n" " -c Dump data in clear-text, not hex.\n" " -v Version information.\n" " -h Help.\n" "\n" " Example search (search for 'Jane' in .bss):\n\n" " ./memgrep -p 1335 -s -a bss -f s,Jane\n\n" " Example replace (replace memory at 0x8423143 and 0x8443147 with 0x00ff0041):\n\n" " ./memgrep -p 1335 -r -a 0x8423143,0x8443147 -t x,00ff0041\n\n" " Example search/replace (Replace 'Test' with 'Rest' in .bss and .rodata):\n\n" " ./memgrep -p 1335 -s -r -a bss,rodata -f s,Test -t s,Rest\n\n" " Example dump (Dump memory starting at 0x8422113 for 16 bytes):\n\n" " ./memgrep -p 1335 -d -a 0x8422113 -l 16\n\n" "\n" ); exit(0); } void displayVersion() { fprintf(stdout, "memgrep -- Your friendly neighborhood memory mangler. (%s)\n" " (c) 2002-2003 uninformed research\n\n" " skape mmiller@hick.org\n", memgrepVersion); exit(0); }