/**
 * 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 = &sectionHeaders[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(&regs, 0, sizeof(regs));

#if defined(FREEBSD)
	if (ptrace(PTRACE_GETREGS, ctx->pid, (void *)&regs, 0) != -1)
		ctx->sections.stack = regs.r_esp;		
#else
	if (ptrace(PTRACE_GETREGS, ctx->pid, 0, (unsigned long int)&regs) != -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(&regs, 0, sizeof(regs));
	
		do
		{
			if (ptrace(PTRACE_GETREGS, ctx->pid, (void *)&regs, 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