/**
* 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 :)
* Props to neonfreon for motivating me to install FreeBSD for porting.
*
* skape
* mmiller@hick.org
*
* Supported OS's:
*
* Linux
* FreeBSD
* Solaris (sparc)
*
* To compile as an executable:
*
* gcc -Wall -O3 memgrep.c -o memgrep
*
* To compile as a library:
*
* gcc -Wall -O3 -fPIC -shared -DLIBRARY memgrep.c -o memgrep.so
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include "memgrep.h"
#if defined(FREEBSD)
#include <kvm.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/ptrace.h>
#include <machine/reg.h>
#include <sys/user.h>
#define PTRACE_ATTACH PT_ATTACH
#define PTRACE_DETACH PT_DETACH
#define PTRACE_PEEKDATA PT_READ_D
#define PTRACE_POKEDATA PT_WRITE_D
#define PTRACE_SETREGS PT_SETREGS
#define PTRACE_GETREGS PT_GETREGS
#define user_regs_struct reg
#define PTRACE_ADDR_CAST void *
#elif defined(SUNOS)
#include <link.h>
struct my_wrapper_regs {
unsigned long esp;
};
#define user_regs_struct my_wrapper_regs
#define PTRACE_ATTACH 1
#define PTRACE_DETACH 2
#define PTRACE_PEEKDATA 3
#define PTRACE_POKEDATA 4
#define PTRACE_SETREGS 5
#define PTRACE_GETREGS 6
#ifndef REG_SP
#define REG_SP REG_O6
#endif
#define PTRACE_ADDR_CAST unsigned long
#define ptrace(a,b,c,d) myPtrace(ctx, a, b, c, d)
int myPtrace(MEM_CTX *ctx, int command, int pid, unsigned long addr, unsigned long data);
#else
#include <linux/user.h>
#define PTRACE_ADDR_CAST void *
extern long int ptrace (unsigned long int cmd, unsigned long int pid, void *param, unsigned long int data);
#endif
const char memgrepVersion[] = "v0.8.0 12/21/2003";
void _memgrep_addResultRow(MEMGREP_RESULT *result, MEMGREP_RESULT_ROW *row);
void _memgrep_getPidFile(MEM_CTX *ctx, char *path, unsigned long pathSize);
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);
unsigned long pid_listSegments(MEM_CTX *ctx);
unsigned long pid_heapEnumerate(MEM_CTX *ctx, unsigned long current, unsigned long *addr, unsigned long *size);
// 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 core_listSegments(MEM_CTX *ctx);
unsigned long core_heapEnumerate(MEM_CTX *ctx, unsigned long current, unsigned long *addr, unsigned long *size);
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;
#ifndef LIBRARY
#define MEMGREP_CMD_BUILDREFTREE 0x10000000
void buildReferentialTree(MEM_CTX *ctx, unsigned long *addrs, unsigned long numAddrs, int index);
int main(int argc, char **argv)
{
char *addrs = NULL, *from = NULL, *to = NULL, *core = NULL;
enum MemoryDumpFormat fmt = MEMORY_DUMP_FORMAT_HEXINT;
enum MemoryMedium medium = MEMORY_MEDIUM_UNKNOWN;
unsigned long flags = MEMGREP_FLAG_VERBOSE;
unsigned long cmd = 0, minSize = 0;
extern char *optarg;
int c, pid = 0;
MEM_CTX ctx;
memset(&ctx, 0, sizeof(ctx));
while ((c = getopt(argc, argv, "p:o:Tdesra:l:f:t:b:m:LcF:vh")) != EOF)
{
switch (c)
{
case 'p':
medium = MEMORY_MEDIUM_PID;
pid = atoi(optarg) & 0xFFFF;
break;
case 'o':
medium = MEMORY_MEDIUM_CORE;
core = optarg;
break;
case 'T':
cmd = MEMGREP_CMD_BUILDREFTREE;
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 'e':
cmd = MEMGREP_CMD_HEAPENUMERATE;
break;
case 'a':
addrs = optarg;
break;
case 'l':
memgrep(&ctx, MEMGREP_CMD_SET, NULL, 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, NULL, MEMGREP_PARAM_PADDING, strtoul(optarg, NULL, 10));
break;
case 'm':
minSize = strtoul(optarg, NULL, 10);
break;
case 'L':
cmd = MEMGREP_CMD_LISTSEGMENTS;
break;
case 'F':
{
if (!strcasecmp(optarg, "hexint"))
fmt = MEMORY_DUMP_FORMAT_HEXINT;
else if (!strcasecmp(optarg, "hexshort"))
fmt = MEMORY_DUMP_FORMAT_HEXSHORT;
else if (!strcasecmp(optarg, "hexbyte"))
fmt = MEMORY_DUMP_FORMAT_HEXBYTE;
else if (!strcasecmp(optarg, "decint"))
fmt = MEMORY_DUMP_FORMAT_DECINT;
else if (!strcasecmp(optarg, "decshort"))
fmt = MEMORY_DUMP_FORMAT_DECSHORT;
else if (!strcasecmp(optarg, "decbyte"))
fmt = MEMORY_DUMP_FORMAT_DECBYTE;
else if (!strcasecmp(optarg, "printable"))
fmt = MEMORY_DUMP_FORMAT_PRINTABLE;
}
break;
case 'c': // Backwards compatible flag
fmt = MEMORY_DUMP_FORMAT_PRINTABLE;
break;
case 'v':
_displayVersion();
break;
case 'h':
_displayHelp();
break;
}
}
if (cmd == 0 || (medium == MEMORY_MEDIUM_UNKNOWN))
_displayHelp();
memgrep(&ctx, MEMGREP_CMD_SET, NULL, MEMGREP_PARAM_FLAGS, flags);
if (!memgrep(&ctx, MEMGREP_CMD_INITIALIZE, NULL, (unsigned long)medium, (medium == MEMORY_MEDIUM_PID)?pid:(unsigned long)core))
return 0;
// Populate addresses
if (addrs)
memgrep(&ctx, MEMGREP_CMD_POPULATE, NULL, (unsigned long)addrs, 0);
memgrep(&ctx, MEMGREP_CMD_SET, NULL, MEMGREP_PARAM_DUMPFORMAT, fmt);
switch (cmd)
{
case MEMGREP_CMD_SEARCH:
memgrep(&ctx, cmd, NULL, (unsigned long)from, 0);
break;
case MEMGREP_CMD_REPLACE:
memgrep(&ctx, cmd, NULL, (unsigned long)to, 0);
break;
case MEMGREP_CMD_SEARCHREPLACE:
memgrep(&ctx, cmd, NULL, (unsigned long)from, (unsigned long)to);
break;
case MEMGREP_CMD_DUMP:
memgrep(&ctx, cmd, NULL, 0, 0);
break;
case MEMGREP_CMD_HEAPENUMERATE:
memgrep(&ctx, cmd, NULL, minSize, 0);
break;
case MEMGREP_CMD_BUILDREFTREE:
buildReferentialTree(&ctx, ctx.addrs, ctx.numAddrs, 0);
break;
default:
memgrep(&ctx, cmd, NULL, 0, 0);
break;
}
memgrep(&ctx, MEMGREP_CMD_DEINITIALIZE, NULL, 0, 0);
return 0;
}
void buildReferentialTree(MEM_CTX *ctx, unsigned long *addrs, unsigned long numAddrs, int index)
{
unsigned long x;
unsigned long *save;
unsigned long saveLength;
ctx->flags &= ~(MEMGREP_FLAG_VERBOSE);
save = ctx->addrs;
saveLength = ctx->numAddrs;
ctx->addrs = NULL;
ctx->numAddrs = 0;
for (x = 0;
x < numAddrs;
x++)
{
unsigned long current;
MEMGREP_RESULT res;
char space[256];
char from[64];
memgrep(ctx, MEMGREP_CMD_POPULATE, NULL, (unsigned long)"rodata,data,bss", 0);
memset(&res, 0, sizeof(res));
memset(space, 0, sizeof(space));
if ((index > 0) && (index < sizeof(space) - 1))
memset(space, ' ', index);
snprintf(from, sizeof(from) - 1, "i,%lu", addrs[x]);
if (!index)
fprintf(stdout, "0x%.8x ", (unsigned int)addrs[x]);
if ((!memgrep(ctx, MEMGREP_CMD_SEARCH, &res, (unsigned long)from, 0)) ||
(!res.numRows))
{
fprintf(stdout, "is not referenced.\n");
continue;
}
fprintf(stdout, "is referenced at...\n");
for (current = 0;
current < res.numRows;
current++)
{
MEMGREP_RESULT_ROW_SEARCH *search = (MEMGREP_RESULT_ROW_SEARCH *)res.rows[current];
unsigned long val = search->addr;
fprintf(stdout, "%s 0x%.8x ", space, (unsigned int)val);
buildReferentialTree(ctx, &val, 1, index + 1);
}
memgrep(ctx, MEMGREP_CMD_DESTROYRESULT, NULL, (unsigned long)&res, 0);
}
ctx->addrs = save;
ctx->numAddrs = saveLength;
fflush(stdout);
}
#endif
unsigned long memgrep(MEM_CTX *ctx, unsigned long cmd, MEMGREP_RESULT *result, 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:
if (ctx->addrs)
{
free(ctx->addrs);
ctx->addrs = NULL;
ctx->numAddrs = 0;
}
if (!data)
ret = memgrep_populate_string(ctx, (const char *)param);
else
ret = memgrep_populate_array(ctx, (unsigned long *)param, data);
break;
case MEMGREP_CMD_SEARCH:
ret = memgrep_search(ctx, result, (const char *)param);
break;
case MEMGREP_CMD_REPLACE:
ret = memgrep_replace(ctx, result, (const char *)param);
break;
case MEMGREP_CMD_SEARCHREPLACE:
ret = memgrep_searchreplace(ctx, result, (const char *)param, (const char *)data);
break;
case MEMGREP_CMD_DUMP:
ret = memgrep_dump(ctx, result);
break;
case MEMGREP_CMD_LISTSEGMENTS:
ret = memgrep_listSegments(ctx);
break;
case MEMGREP_CMD_DESTROYRESULT:
ret = memgrep_destroy(ctx, (MEMGREP_RESULT *)param);
break;
case MEMGREP_CMD_HEAPENUMERATE:
ret = memgrep_heapenumerate(ctx, result, param);
break;
default:
break;
}
return ret;
}
unsigned long memgrep_initialize(MEM_CTX *ctx, enum MemoryMedium medium, void *data)
{
unsigned long ret = 0;
ctx->addrs = NULL;
ctx->numAddrs = 0;
ctx->medium = medium;
ctx->dumpFormat = MEMORY_DUMP_FORMAT_HEXINT;
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;
ctx->functions.listSegments = pid_listSegments;
ctx->functions.heapEnumerate = pid_heapEnumerate;
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;
ctx->functions.listSegments = core_listSegments;
ctx->functions.heapEnumerate = core_heapEnumerate;
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;
}
if (!(ret = ctx->functions.getSections(ctx)))
{
if (ctx->flags & MEMGREP_FLAG_VERBOSE)
fprintf(stderr, "memgrep_initialize(): Couldn't determine binary sections.\n");
// Deinitialize accordingly.
memgrep_deinitialize(ctx);
}
return ret;
}
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;
case MEMGREP_PARAM_DUMPFORMAT:
ctx->dumpFormat = (enum MemoryDumpFormat)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;
case MEMGREP_PARAM_DUMPFORMAT:
ret = ctx->dumpFormat;
break;
default:
break;
}
return ret;
}
unsigned long memgrep_populate_string(MEM_CTX *ctx, const char *addresses)
{
const char *comma = NULL, *curr = NULL;
unsigned char done = 0;
char *copy = strdup(addresses);
curr = copy;
if (!copy)
return 0;
if (ctx->addrs)
free(ctx->addrs);
while (curr && !done)
{
comma = strchr(curr, ',');
if (comma)
*(char *)comma = 0;
else
done = 1;
// Invalid format
if (strlen(curr) < 2)
break;
if (!ctx->functions.populateKeyword(ctx, curr))
{
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;
}
if (copy)
free(copy);
return ctx->numAddrs;
}
unsigned long memgrep_populate_array(MEM_CTX *ctx, unsigned long *array, unsigned long elements)
{
unsigned long x = 0;
if (ctx->addrs)
free(ctx->addrs);
for (; x < elements; x++)
{
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] = array[x];
}
return x;
}
unsigned long memgrep_replace(MEM_CTX *ctx, MEMGREP_RESULT *result, const char *replacePhrase)
{
unsigned long ret = 0, x, replaceLength = 0;
unsigned char *replaceBuf = NULL;
MEMGREP_RESULT_ROW_REPLACE replace;
if (result)
memset(result, 0, sizeof(MEMGREP_RESULT));
replace.base.length = sizeof(MEMGREP_RESULT_ROW_REPLACE);
replace.base.type = MEMGREP_RESULT_TYPE_REPLACE;
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))
{
replace.addr = ctx->addrs[x];
_memgrep_addResultRow(result, (MEMGREP_RESULT_ROW *)&replace);
ret++;
}
}
free(replaceBuf);
return ret;
}
unsigned long memgrep_search(MEM_CTX *ctx, MEMGREP_RESULT *result, const char *searchPhrase)
{
return memgrep_searchreplace(ctx, result, searchPhrase, NULL);
}
unsigned long memgrep_searchreplace(MEM_CTX *ctx, MEMGREP_RESULT *result, const char *searchPhrase, const char *replacePhrase)
{
unsigned long ret = 0;
unsigned char *searchBuf = NULL, *replaceBuf = NULL;
unsigned long searchLength = 0, replaceLength = 0, x;
if (result)
memset(result, 0, sizeof(MEMGREP_RESULT));
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 = 0;
unsigned long left = 0, y, getLength, slope = 0;
unsigned char *buf = NULL, *potential;
// Use section lengths if necessary.
if (addr == ctx->sections.rodata)
end = ctx->addrs[x] + ((ctx->length &&
ctx->length < ctx->sections.rodataLength) ? ctx->length : ctx->sections.rodataLength);
else if (addr == ctx->sections.data)
end = ctx->addrs[x] + ((ctx->length &&
ctx->length < ctx->sections.dataLength) ? ctx->length : ctx->sections.dataLength);
else if (addr == ctx->sections.text)
end = ctx->addrs[x] + ((ctx->length &&
ctx->length < ctx->sections.textLength) ? ctx->length : ctx->sections.textLength);
else
end = (ctx->length) ? ctx->addrs[x] + ctx->length : 0xffffffff;
// Initialize the left count
left = end - addr;
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)
{
MEMGREP_RESULT_ROW_REPLACE replace;
replace.base.length = sizeof(MEMGREP_RESULT_ROW_REPLACE);
replace.base.type = MEMGREP_RESULT_TYPE_REPLACE;
replace.addr = addr + y;
_memgrep_addResultRow(result, (MEMGREP_RESULT_ROW *)&replace);
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
{
MEMGREP_RESULT_ROW_SEARCH search;
search.base.length = sizeof(MEMGREP_RESULT_ROW_SEARCH);
search.base.type = MEMGREP_RESULT_TYPE_SEARCH;
search.addr = addr + y;
_memgrep_addResultRow(result, (MEMGREP_RESULT_ROW *)&search);
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, MEMGREP_RESULT *result)
{
unsigned long ret = 0, x, y, base, len, width = 0;
MEMGREP_RESULT_ROW_DUMP dump;
const char *fmtString = NULL;
if (result)
memset(result, 0, sizeof(MEMGREP_RESULT));
dump.base.length = sizeof(MEMGREP_RESULT_ROW_DUMP);
dump.base.type = MEMGREP_RESULT_TYPE_DUMP;
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 count = 0, inc = sizeof(unsigned long);
ret++;
if (result)
{
dump.buf = (unsigned char *)malloc(len);
dump.bufLength = len;
if (dump.buf)
{
memcpy(dump.buf, buf, len);
_memgrep_addResultRow(result, (MEMGREP_RESULT_ROW *)&dump);
}
}
if (!(ctx->flags & MEMGREP_FLAG_VERBOSE))
{
free(buf);
continue;
}
switch (ctx->dumpFormat)
{
case MEMORY_DUMP_FORMAT_PRINTABLE: fmtString = "printable"; width = 1; break;
case MEMORY_DUMP_FORMAT_HEXBYTE: fmtString = "hexbyte"; width = 2; break;
case MEMORY_DUMP_FORMAT_DECBYTE: fmtString = "decbyte"; width = 3; break;
case MEMORY_DUMP_FORMAT_HEXSHORT: fmtString = "hexshort"; width = 4; break;
case MEMORY_DUMP_FORMAT_DECSHORT: fmtString = "decshort"; width = 5; break;
case MEMORY_DUMP_FORMAT_DECINT: fmtString = "decint"; width = 10; break;
case MEMORY_DUMP_FORMAT_HEXINT: default: fmtString = "hexint"; width = 8; break;
}
fprintf(stdout, "%lu bytes starting at %.8x (+/- %lu) as %s...\n", ctx->length, (unsigned int)ctx->addrs[x], ctx->padding, fmtString);
fprintf(stdout, "%.8x: ", (unsigned int)base);
if (!buf)
continue;
switch (ctx->dumpFormat)
{
case MEMORY_DUMP_FORMAT_PRINTABLE:
case MEMORY_DUMP_FORMAT_HEXBYTE:
case MEMORY_DUMP_FORMAT_DECBYTE:
inc = 1;
break;
case MEMORY_DUMP_FORMAT_HEXSHORT:
case MEMORY_DUMP_FORMAT_DECSHORT:
inc = 2;
break;
case MEMORY_DUMP_FORMAT_HEXINT:
case MEMORY_DUMP_FORMAT_DECINT:
default:
inc = 4;
break;
}
for (y = 0, count = 0; y < len; y += inc)
{
if ((count * inc) == 16)
{
if (ctx->dumpFormat != MEMORY_DUMP_FORMAT_PRINTABLE)
{
char printable[17];
int z;
printable[sizeof(printable) - 1] = 0;
memcpy(printable, buf + y - 16, 16);
for (z = 0; z < 16; z++)
{
if (!isprint((int)printable[z]))
printable[z] = '.';
}
fprintf(stdout, "%s", printable);
}
fprintf(stdout, "\n%.8x: ", (unsigned int)(base + y));
count = 1;
}
else
count++;
switch (ctx->dumpFormat)
{
case MEMORY_DUMP_FORMAT_PRINTABLE:
{
char let = *(buf + y);
if (!isprint((int)let))
let = '.';
fprintf(stdout, "%c", let);
}
break;
case MEMORY_DUMP_FORMAT_DECBYTE:
fprintf(stdout, "%3.3d ", (unsigned int)*(unsigned char *)(buf + y) & 0xff);
break;
case MEMORY_DUMP_FORMAT_HEXBYTE:
fprintf(stdout, "%.2x ", (unsigned int)*(unsigned char *)(buf + y));
break;
case MEMORY_DUMP_FORMAT_DECSHORT:
fprintf(stdout, "%5.5d ", (unsigned int)*(unsigned short *)(buf + y) & 0xffff);
break;
case MEMORY_DUMP_FORMAT_HEXSHORT:
fprintf(stdout, "%.4x ", (unsigned int)*(unsigned short *)(buf + y));
break;
case MEMORY_DUMP_FORMAT_DECINT:
fprintf(stdout, "%10.10lu ", *(unsigned long *)(buf + y));
break;
case MEMORY_DUMP_FORMAT_HEXINT:
default:
fprintf(stdout, "%.8x ", (unsigned int)*(unsigned long *)(buf + y));
break;
}
}
// Print the printable characters for the last line.
if (ctx->dumpFormat != MEMORY_DUMP_FORMAT_PRINTABLE)
{
int offset = (count * inc);
int total = ((16 - offset) * (width + 1)) / inc;
char pad[256], printable[17];
int z;
if (total > (sizeof(pad) - 1))
total = sizeof(pad) - 1;
memset(printable, 0, sizeof(printable));
memset(pad, 0, sizeof(pad));
memset(pad, ' ', total);
memcpy(printable, buf + len - offset, offset);
for (z = 0; z < offset; z++)
{
if (!isprint((int)printable[z]))
printable[z] = '.';
}
fprintf(stdout, "%s%s", pad, printable);
}
fprintf(stdout, "\n");
free(buf);
}
return ret;
}
unsigned long memgrep_listSegments(MEM_CTX *ctx)
{
return ctx->functions.listSegments(ctx);
}
unsigned long memgrep_destroy(MEM_CTX *ctx, MEMGREP_RESULT *result)
{
unsigned long curr = 0;
if (!result)
return 0;
if (result->rows)
{
for (curr = 0;
curr < result->numRows;
curr++)
{
switch (result->rows[curr]->type)
{
case MEMGREP_RESULT_TYPE_DUMP:
{
MEMGREP_RESULT_ROW_DUMP *dump = (MEMGREP_RESULT_ROW_DUMP *)result->rows[curr];
if (dump->buf)
free(dump->buf);
}
break;
default:
break;
}
free(result->rows[curr]);
}
free(result->rows);
}
result->rows = NULL;
result->numRows = 0;
return 1;
}
unsigned long memgrep_heapenumerate(MEM_CTX *ctx, MEMGREP_RESULT *result, unsigned long minSize)
{
MEMGREP_RESULT_ROW_HEAP heapRow;
unsigned long current = 0, addr;
unsigned long size = 0;
unsigned long entries = 0;
int finished = 0;
if (result)
memset(result, 0, sizeof(MEMGREP_RESULT));
heapRow.base.length = sizeof(MEMGREP_RESULT_ROW_HEAP);
heapRow.base.type = MEMGREP_RESULT_TYPE_HEAP;
while (!finished)
{
heapRow.addr = addr;
heapRow.size = size;
if (!(current = ctx->functions.heapEnumerate(ctx, current, &addr, &size)))
finished = 1;
// If we have a match address, see if it's in the range of this allocation
if (ctx->addrs)
{
int x;
for (x = 0; x < ctx->numAddrs; x++)
{
if ((addr <= ctx->addrs[x]) &&
(addr + size > ctx->addrs[x]))
{
if (ctx->flags & MEMGREP_FLAG_VERBOSE)
fprintf(stdout, "Found allocation unit for %.8x: base is %.8x, length is %lu.\n",
(unsigned int)ctx->addrs[x], (unsigned int)addr, size);
_memgrep_addResultRow(result, (MEMGREP_RESULT_ROW *)&heapRow);
entries++;
break;
}
}
}
else
{
if ((!minSize) ||
(size >= minSize))
{
if (ctx->flags & MEMGREP_FLAG_VERBOSE)
fprintf(stdout, "Heap entry: %.8x (size %lu)\n", (unsigned int)addr, size);
_memgrep_addResultRow(result, (MEMGREP_RESULT_ROW *)&heapRow);
entries++;
}
}
}
if (ctx->flags & MEMGREP_FLAG_VERBOSE)
fprintf(stdout, "Total matched heap entries: %lu\n", entries);
return ((result) && (result->numRows)) ? 1 : 0;
}
void _memgrep_addResultRow(MEMGREP_RESULT *result, MEMGREP_RESULT_ROW *row)
{
MEMGREP_RESULT_ROW *newRow;
if (!result)
return;
newRow = (MEMGREP_RESULT_ROW *)malloc(row->length);
if (!newRow)
return;
memcpy(newRow, row, row->length);
if (!result->rows)
result->rows = (MEMGREP_RESULT_ROW **)malloc(sizeof(MEMGREP_RESULT_ROW *));
else
result->rows = (MEMGREP_RESULT_ROW **)realloc(result->rows, (sizeof(MEMGREP_RESULT_ROW *) * result->numRows) + 1);
result->rows[result->numRows++] = newRow;
}
unsigned long _translateToHex(const char *fullString, unsigned char **buf, unsigned long *bufLength)
{
const char *comma = NULL;
unsigned long ret = 0;
char *copy = NULL;
// If the input string is invalid, don't even try.
if (!fullString)
return 0;
if (!(copy = strdup(fullString)))
return 0;
comma = strchr(copy, ',');
if (!comma)
{
free(copy);
return 0;
}
*(char *)comma = 0;
ret = _translateFormatToHex(copy, comma + 1, buf, bufLength);
*(char *)comma = ',';
free(copy);
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, 0, 0) < 0)
{
if (ctx->flags & MEMGREP_FLAG_VERBOSE)
perror("ptrace(ATTACH)");
return 0;
}
wait(NULL);
// For signal handling
_pid = ctx->pid;
signal(SIGINT, _safeCleanup);
return 1;
}
unsigned long pid_close(MEM_CTX *ctx)
{
#if defined(FREEBSD)
// XXX probably needs to be before
kill(ctx->pid, SIGCONT);
if (ptrace(PTRACE_DETACH, ctx->pid, (PTRACE_ADDR_CAST)1, 0) < 0)
perror("ptrace(DETACH)");
#else
if (ptrace(PTRACE_DETACH, ctx->pid, 0, 0) < 0)
perror("ptrace(DETACH)");
#endif
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;
_memgrep_getPidFile(ctx, path, sizeof(path) - 1);
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;
ctx->sections.rodataLength = sectionHeaders[index].sh_size;
}
else if (!strcmp(name, ".data"))
{
ctx->sections.data = sectionHeaders[index].sh_addr;
ctx->sections.dataLength = sectionHeaders[index].sh_size;
}
else if (!strcmp(name, ".text"))
{
ctx->sections.text = sectionHeaders[index].sh_addr;
ctx->sections.textLength = sectionHeaders[index].sh_size;
}
}
} while (0);
if (stringTable)
free(stringTable);
if (sectionHeaders)
free(sectionHeaders);
close(fd);
// Get current stack address
memset(®s, 0, sizeof(regs));
#if defined(FREEBSD)
if (ptrace(PTRACE_GETREGS, ctx->pid, (void *)®s, 0) != -1)
ctx->sections.stack = regs.r_esp;
#else
if (ptrace(PTRACE_GETREGS, ctx->pid, 0, (unsigned long int)®s) != -1)
ctx->sections.stack = regs.esp;
#endif
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, (PTRACE_ADDR_CAST)x, 0)) == -1))
{
#if defined(FREEBSD)
if (errno == 14)
#elif defined(SUNOS)
if (errno == EFAULT)
#else
if (errno == 5)
#endif
{
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, (PTRACE_ADDR_CAST)x, 0);
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, (PTRACE_ADDR_CAST)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, "text"))
addr = ctx->sections.text;
else if (!strcmp(keyword, "all"))
{
pid_populateKeyword(ctx, "rodata");
pid_populateKeyword(ctx, "data");
pid_populateKeyword(ctx, "bss");
pid_populateKeyword(ctx, "stack");
pid_populateKeyword(ctx, "text");
}
else if (!strtoul(keyword, NULL, 16) && (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;
}
unsigned long pid_listSegments(MEM_CTX *ctx)
{
fprintf(stdout, ".bss => %.8x\n",
(unsigned int)ctx->sections.bss);
fprintf(stdout, ".data => %.8x (%lu bytes, %lu Kbytes)\n",
(unsigned int)ctx->sections.data, ctx->sections.dataLength, ctx->sections.dataLength / 1024);
fprintf(stdout, ".rodata => %.8x (%lu bytes, %lu Kbytes)\n",
(unsigned int)ctx->sections.rodata, ctx->sections.rodataLength, ctx->sections.rodataLength / 1024);
fprintf(stdout, ".text => %.8x (%lu bytes, %lu Kbytes)\n",
(unsigned int)ctx->sections.text, ctx->sections.textLength, ctx->sections.textLength / 1024);
fprintf(stdout, "stack => %.8x\n",
(unsigned int)ctx->sections.stack);
return 1;
}
unsigned long pid_heapEnumerate(MEM_CTX *ctx, unsigned long current, unsigned long *addr, unsigned long *size)
{
struct _pointer_entry {
void *ptr;
size_t size;
struct _pointer_entry *next;
};
do
{
if (!current)
{
int verbose = ctx->flags & MEMGREP_FLAG_VERBOSE;
MEMGREP_RESULT_ROW_SEARCH *row;
MEMGREP_RESULT search;
unsigned long *save;
unsigned long *ptr;
if (verbose)
ctx->flags &= ~MEMGREP_FLAG_VERBOSE;
// Save the current address list so it's not overwritten.
save = ctx->addrs;
ctx->addrs = NULL;
if (!memgrep_populate_string(ctx, "bss"))
break;
if (!memgrep_search(ctx, &search, "s,HEAPLISTSIG"))
{
if (verbose)
fprintf(stdout, "The heap signature could not be found, did you LD_PRELOAD heaplist.so?\n");
break;
}
// Restore the saved addresses
if (ctx->addrs)
free(ctx->addrs);
ctx->addrs = save;
if (verbose)
ctx->flags |= MEMGREP_FLAG_VERBOSE;
if (search.numRows < 1)
break;
row = (MEMGREP_RESULT_ROW_SEARCH *)search.rows[0];
ptr = (unsigned long *)pid_get(ctx, row->addr + 16, sizeof(current));
if (ptr)
current = *ptr;
free(ptr);
memgrep_destroy(ctx, &search);
}
if (current)
{
struct _pointer_entry *entry = (struct _pointer_entry *)pid_get(ctx, current, sizeof(struct _pointer_entry));
if (entry)
{
if (addr)
*addr = (unsigned long)entry->ptr;
if (size)
*size = (unsigned long)entry->size;
current = (unsigned long)entry->next;
free(entry);
}
else
current = 0;
}
} while (0);
return current;
}
//
// 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;
for (index = 0; index < ctx->coreData.elfHeader.e_phnum; index++)
{
unsigned long rma = 0;
// We'll mmap everything but non-writeable segments and as long as it's a loadable segment.
if (!(ctx->coreData.programHeaders[index].p_flags & PF_W))
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;
ctx->coreData.numSections++;
}
} while (0);
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 (!strtoul(keyword, NULL, 16) && (ctx->flags & MEMGREP_FLAG_VERBOSE))
fprintf(stderr, "core_populateKeyword(): warning: unknown keyword '%s'\n", keyword);
return 0;
}
unsigned long core_listSegments(MEM_CTX *ctx)
{
unsigned long x = 0;
fprintf(stdout, "core file VMA segments:\n");
for (; x < ctx->coreData.numSections; x++)
{
fprintf(stdout, " %.8x -> %.8x (%lu bytes)\n",
(unsigned int)ctx->coreData.sections[x].vma,
(unsigned int)(ctx->coreData.sections[x].vma + ctx->coreData.sections[x].length),
ctx->coreData.sections[x].length);
}
return 1;
}
unsigned long core_heapEnumerate(MEM_CTX *ctx, unsigned long current, unsigned long *addr, unsigned long *size)
{
return 0;
}
void _displayHelp()
{
fprintf(stderr, "memgrep -- Run-time/core-time memory searching, dumping and modifying utility.\n"
"Usage: ./memgrep [-p pid] [-o core] [-T] [-d] [-r] [-s] [-e] [-a addr1,addr2,bss,addr3] [-l length]\n"
" [-f fmt,search data] [-t fmt,replace data] [-b pad] [-m minimum size]\n"
" [-F fmt] [-L] [-v] [-h]\n"
"\n"
" -p [pid] The process id to operate on.\n"
" -o [core] The core file to operate on.\n"
" -T Build a referential tree for the given address(es).\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"
" -e Enumerate the heap.\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 (uninit global vars, heap data).\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"
" text -> Uses the VMA associated with the .text section (text, ie, executable code).\n"
" stack -> Dynamically determines the current stack pointer.\n"
" all -> Uses bss, stack, rodata, data, text. This is the only keyword that can be used\n"
" when operating on 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"
" -m [minsz] The minimum size of a heap allocation for use when enumerating.\n"
" -b [pad] Number of bytes of padding to use around dump addresses (default is 0).\n"
" -F [fmt] The format to use when dumping memory, can be one of the following:\n"
" hexint -> Four byte hexi-decimal integers.\n"
" hexshort -> Two byte hexi-decimal shorts.\n"
" hexbyte -> One byte hexi-decimal characters.\n"
" decint -> Four byte decimal integers.\n"
" decshort -> Two byte decimal shorts.\n"
" decbyte -> One byte decimal characters.\n"
" printable -> Printable characters.\n"
" -L List memory segments of a process or core file.\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);
}
void _memgrep_getPidFile(MEM_CTX *ctx, char *path, unsigned long pathSize)
{
// Initialize the path
#if defined(FREEBSD)
struct stat statbuf;
snprintf(path, pathSize, "/proc/%d/file", ctx->pid);
#elif defined(SUNOS)
snprintf(path, pathSize, "/proc/%d/object/a.out", ctx->pid);
#else
snprintf(path, pathSize, "/proc/%d/exe", ctx->pid);
#endif
/*
FreeBSD RANT:
There is no way from a user land program to obtain the
full path to an executable associated with a process id
in FreeBSD. That sucks. So, we hack it.
We look search the stack for the PWD environment variable
and then combine the path in PWD with argv[0].
This will work in all cases that we can actually find
the PWD env var.
We only do this if they don't have procfs.
*/
#if defined(FREEBSD)
if (stat(path, &statbuf) != 0)
{
unsigned long stackLength = 65536, x, found = 0;
struct user_regs_struct regs;
unsigned char *stack = NULL;
struct kinfo_proc *proc = NULL;
kvm_t *kd = NULL;
char **argv;
int cnt = 0;
memset(®s, 0, sizeof(regs));
do
{
if (ptrace(PTRACE_GETREGS, ctx->pid, (void *)®s, 0) == -1)
break;
if (!(stack = pid_get(ctx, regs.r_esp, stackLength)))
break;
// Now we search the stack for 'PWD='
for (x = 0; !found && x < stackLength; x++)
{
if (stack[x] == 'P' && x + 4 < stackLength)
{
if (memcmp(stack + x, "PWD=", 4) == 0)
{
x += 3; // Point right at the path.
found = 1;
}
}
}
if (!found)
break;
// Werd, we made it this far, now we extract argv[0]
if (!(kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)))
break;
if (!(proc = kvm_getprocs(kd, KERN_PROC_PID, ctx->pid, &cnt)))
break;
if (!(argv = kvm_getargv(kd, proc, 0)))
break;
if (argv[0][0] == '/')
strncpy(path, argv[0], pathSize);
else
snprintf(path, pathSize, "%s/%s", stack + x, argv[0]);
} while (0);
if (stack)
free(stack);
if (kd)
kvm_close(kd);
}
#endif
}
#if defined(SUNOS)
#define _STRUCTURED_PROC 1 // Use preferred API
#include <sys/procfs.h>
void extractPidStatus(MEM_CTX *ctx, pstatus_t *status)
{
char path[512];
int fd = 0;
snprintf(path, sizeof(path) - 1, "/proc/%d/status", ctx->pid);
if ((fd = open(path, O_RDONLY)) <= 0)
return;
read(fd, status, sizeof(pstatus_t));
close(fd);
}
int myPtrace(MEM_CTX *ctx, int command, int pid, unsigned long addr, unsigned long data)
{
int ret = -1;
if (command != PTRACE_ATTACH && !ctx->procCtlFd)
{
errno = ESRCH;
return ret;
}
switch (command)
{
case PTRACE_ATTACH:
{
char path[512];
snprintf(path, sizeof(path) - 1, "/proc/%d/ctl", ctx->pid);
ctx->procCtlFd = open(path, O_WRONLY);
snprintf(path, sizeof(path) - 1, "/proc/%d/as", ctx->pid);
ctx->procAsFd = open(path, O_RDWR);
if (ctx->procCtlFd && ctx->procAsFd)
{
long ctl = PCSTOP;
write(ctx->procCtlFd, &ctl, sizeof(long));
ret = errno = 0;
}
else
{
if (ctx->procCtlFd)
close(ctx->procCtlFd);
if (ctx->procAsFd)
close(ctx->procAsFd);
ctx->procCtlFd = 0;
ctx->procAsFd = 0;
errno = ESRCH;
}
}
break;
case PTRACE_POKEDATA:
{
if (pwrite(ctx->procAsFd, &data, sizeof(data), addr) <= 0)
errno = EFAULT;
else
errno = ret = 0;
}
break;
case PTRACE_PEEKDATA:
{
if (pread(ctx->procAsFd, &ret, sizeof(ret), addr) <= 0)
{
errno = EFAULT;
ret = -1;
}
else
errno = 0;
}
break;
case PTRACE_SETREGS: // Not used yet.
{
ret = errno = 0;
}
break;
case PTRACE_GETREGS:
{
pstatus_t status;
extractPidStatus(ctx, &status);
#if defined(SPARC)
((struct my_wrapper_regs *)data)->esp = status.pr_lwp.pr_reg[REG_SP];
#else
((struct my_wrapper_regs *)data)->esp = status.pr_lwp.pr_reg[REG_ESP];
#endif
ret = errno = 0;
}
break;
case PTRACE_DETACH:
{
long run[2];
run[0] = PCRUN;
run[1] = PRCFAULT | PRCSIG;
// Re-run the process
write(ctx->procCtlFd, run, sizeof(run));
close(ctx->procCtlFd);
close(ctx->procAsFd);
ret = errno = 0;
ctx->procCtlFd = 0;
ctx->procAsFd = 0;
}
break;
default:
break;
}
return ret;
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1