/**
 * uninformed research
 * http://www.uninformed.org
 *
 * Search/replace/dump for memory in a running process.
 * Search/dump for memory in core files.
 *
 * Props to thief for the idea :)
 *
 * This only works on Linux I'd assume.
 *
 * skape
 * mmiller@hick.org
 *
 * gcc -Wall -O3 memgrep.c -o memgrep
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <linux/user.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 <elf.h>

const char memgrepVersion[] = "v0.5.0 01/01/2003";

#define MEMGREP_CMD_INITIALIZE    0x00000001  // Initialize from a given medium.
#define MEMGREP_CMD_DEINITIALIZE  0x00000002  // Deinitialize from a given medium.
#define MEMGREP_CMD_SET           0x00000003  // Used to set parameters.
#define MEMGREP_CMD_GET           0x00000004  // Used to get parameters.
#define MEMGREP_CMD_POPULATE      0x00000005  // Populate a given set of addresses from a string.
#define MEMGREP_CMD_SEARCH        0x00000006  // Search memory
#define MEMGREP_CMD_REPLACE       0x00000007  // Replace memory
#define MEMGREP_CMD_SEARCHREPLACE 0x00000008  // Search and replace matches.
#define MEMGREP_CMD_DUMP          0x00000009  // Dump memory

#define MEMGREP_PARAM_FLAGS       0x00000001  // Identifier used for setting 'flags' with MEMGREP_CMD_SET
#define MEMGREP_PARAM_LENGTH      0x00000002  // Length for search/replace/dump.  Default is 'max'
#define MEMGREP_PARAM_PADDING     0x00000003  // Padding space +/- for dump.

#define MEMGREP_FLAG_VERBOSE      (1 << 0)  // Be verbose
#define MEMGREP_FLAG_PROMPT       (1 << 1)  // Prompt before replacing 
#define MEMGREP_FLAG_DUMPCLEAN    (1 << 2)  // Dump data in a readable text, not hex.

enum MemoryMedium {
	MEMORY_MEDIUM_UNKNOWN = 0,
	MEMORY_MEDIUM_PID     = 1,
	MEMORY_MEDIUM_CORE    = 2
};

typedef struct _process_section_addrs {

	unsigned long rodata;    // read-only data, static
	unsigned long data;      // data, static
	unsigned long bss;       // bss, static

	unsigned long stack;     // stack, dynamic

} PROCESS_SECTION_ADDRS;

typedef struct _core_memory_sections {

	unsigned long vma;       // Virtual memory address
	unsigned long length;    // Memory length

	unsigned long rma;       // Real memory address (mmap)

} CORE_MEMORY_SECTIONS;

typedef struct _mem_ctx_core_data {

	int                  fd;

	Elf32_Ehdr           elfHeader;          // core file elf header
	Elf32_Phdr           *programHeaders;    // Program header array for the core file

	CORE_MEMORY_SECTIONS *sections;          // Array of sections
	unsigned long        numSections;        // Number of sections

} MEM_CTX_CORE_DATA;

struct _mem_ctx;

typedef struct _memgrep_functions {

		unsigned long  (*open)(struct _mem_ctx *ctx);
		unsigned long  (*close)(struct _mem_ctx *ctx);

		unsigned long  (*getSections)(struct _mem_ctx *ctx);
		unsigned char *(*get)(struct _mem_ctx *ctx, unsigned long addr, unsigned long length);
		unsigned long  (*put)(struct _mem_ctx *ctx, unsigned long addr, unsigned char *buf, unsigned long bufLength);
		unsigned long  (*populateKeyword)(struct _mem_ctx *ctx, const char *keyword);

} MEMGREP_FUNCTIONS;

typedef struct _mem_ctx {

	unsigned long         flags;    // Flags

	enum MemoryMedium     medium;

	int                   pid;              // Process id
	char                  *core;            // Core file

	MEMGREP_FUNCTIONS     functions; // Dynamic functions
	PROCESS_SECTION_ADDRS sections;  // Section addresses (bss, data, rodata, stack)

	unsigned long         *addrs;   // Start address(es)
	unsigned long         numAddrs; // Number of start address(es)
	unsigned long         length;   // The length to operate on
	unsigned long         padding;  // The padding used when dumping

	// Core file extended data
	
	MEM_CTX_CORE_DATA     coreData;

} MEM_CTX;

unsigned long memgrep(MEM_CTX *ctx, unsigned long cmd, unsigned long param, unsigned long data);
unsigned long memgrep_initialize(MEM_CTX *ctx, enum MemoryMedium medium, void *data); // 1 for success, 0 for failure
unsigned long memgrep_deinitialize(MEM_CTX *ctx); // 1 for success, 0 for failure
unsigned long memgrep_set(MEM_CTX *ctx, unsigned long param, unsigned long data); // 1 for success, 0 for failure
unsigned long memgrep_get(MEM_CTX *ctx, unsigned long param); // the value associated w/ the param
unsigned long memgrep_populate(MEM_CTX *ctx, const char *addresses); // number of addresses populated
unsigned long memgrep_search(MEM_CTX *ctx, const char *searchPhrase); // number of addresses found
unsigned long memgrep_replace(MEM_CTX *ctx, const char *replacePhrase); // number of addresses replaced
unsigned long memgrep_searchreplace(MEM_CTX *ctx, const char *searchPhrase, const char *replacePhrase); // number of addresses search/replaced
unsigned long memgrep_dump(MEM_CTX *ctx); // 1 for success, 0 for failure

void safeCleanup(int signal);

// Pid operators
unsigned long  pid_open(MEM_CTX *ctx);
unsigned long  pid_close(MEM_CTX *ctx);
unsigned long  pid_getSections(MEM_CTX *ctx);
unsigned char *pid_get(MEM_CTX *ctx, unsigned long addr, unsigned long length);
unsigned long  pid_put(MEM_CTX *ctx, unsigned long addr, unsigned char *buf, unsigned long bufLength);
unsigned long  pid_populateKeyword(MEM_CTX *ctx, const char *keyword);

// Core operators
unsigned long  core_open(MEM_CTX *ctx);
unsigned long  core_close(MEM_CTX *ctx);
unsigned long  core_getSections(MEM_CTX *ctx);
unsigned char *core_get(MEM_CTX *ctx, unsigned long addr, unsigned long length);
unsigned long  core_put(MEM_CTX *ctx, unsigned long addr, unsigned char *buf, unsigned long bufLength);
unsigned long  core_populateKeyword(MEM_CTX *ctx, const char *keyword);

unsigned long translateToHex(const char *fullString, unsigned char **buf, unsigned long *bufLength);
unsigned long translateFormatToHex(const char *format, const char *string, unsigned char **buf, unsigned long *bufLength);
void displayVersion();
void displayHelp();

unsigned long _pid = 0;

int main(int argc, char **argv)
{
	char *addrs = NULL, *from = NULL, *to = NULL, *core = NULL;
	enum MemoryMedium medium = MEMORY_MEDIUM_UNKNOWN;
	unsigned long flags = MEMGREP_FLAG_VERBOSE;
	unsigned long cmd = 0;
	extern char *optarg;
	int c, pid = 0;
	MEM_CTX ctx;

	memset(&ctx, 0, sizeof(ctx));

	while ((c = getopt(argc, argv, "p:o:dsra:l:f:t:b:cvh")) != EOF)
	{
		switch (c)
		{
			case 'p':
				medium = MEMORY_MEDIUM_PID;
				pid    = atoi(optarg) & 0xFFFF;
				break;
			case 'o':
				medium = MEMORY_MEDIUM_CORE;
				core   = optarg;
				break;
			case 'd':
				cmd = MEMGREP_CMD_DUMP;		
				break;
			case 's':
				if (cmd == MEMGREP_CMD_REPLACE)
					cmd = MEMGREP_CMD_SEARCHREPLACE;
				else
					cmd = MEMGREP_CMD_SEARCH;
				break;
			case 'r':
				if (cmd == MEMGREP_CMD_SEARCH)
					cmd = MEMGREP_CMD_SEARCHREPLACE;
				else
					cmd = MEMGREP_CMD_REPLACE;
				break;
			case 'a':
				addrs = optarg;
				break;
			case 'l':
				memgrep(&ctx, MEMGREP_CMD_SET, MEMGREP_PARAM_LENGTH, strtoul(optarg, NULL, 10));
				break;
			case 'f':
				from = optarg;
				break;
			case 't':
				to = optarg;
				break;
			case 'b':
				memgrep(&ctx, MEMGREP_CMD_SET, MEMGREP_PARAM_PADDING, strtoul(optarg, NULL, 10));
				break;
			case 'c':
				flags |= MEMGREP_FLAG_DUMPCLEAN;
				break;
			case 'v':
				displayVersion();
				break;
			case 'h':
				displayHelp();
				break;
		}
	}

	if (cmd == 0 || (medium == MEMORY_MEDIUM_UNKNOWN))
		displayHelp();

	memgrep(&ctx, MEMGREP_CMD_SET, MEMGREP_PARAM_FLAGS, flags);

	if (!memgrep(&ctx, MEMGREP_CMD_INITIALIZE, (unsigned long)medium, (medium == MEMORY_MEDIUM_PID)?pid:(unsigned long)core))
		return 0;

	// Populate addresses

	if (!memgrep(&ctx, MEMGREP_CMD_POPULATE, (unsigned long)addrs, 0))
	{
		fprintf(stderr, "memgrep: No addresses specified.\n");
		return 0;
	}

	switch (cmd)
	{
		case MEMGREP_CMD_SEARCH:
			memgrep(&ctx, cmd, (unsigned long)from, 0);
			break;
		case MEMGREP_CMD_REPLACE:
			memgrep(&ctx, cmd, (unsigned long)to, 0);
			break;
		case MEMGREP_CMD_SEARCHREPLACE:
			memgrep(&ctx, cmd, (unsigned long)from, (unsigned long)to);
			break;
		case MEMGREP_CMD_DUMP:
			memgrep(&ctx, cmd, 0, 0);
			break;
		default:
			break;
	}

	memgrep(&ctx, MEMGREP_CMD_DEINITIALIZE, 0, 0);

	return 0;
}

unsigned long memgrep(MEM_CTX *ctx, unsigned long cmd, unsigned long param, unsigned long data)
{
	unsigned long ret = 0;

	switch (cmd)
	{
		case MEMGREP_CMD_INITIALIZE:
			ret = memgrep_initialize(ctx, (enum MemoryMedium)param, (void *)data);
			break;
		case MEMGREP_CMD_DEINITIALIZE:
			ret = memgrep_deinitialize(ctx);
			break;
		case MEMGREP_CMD_SET:
			ret = memgrep_set(ctx, param, data);
			break;
		case MEMGREP_CMD_GET:
			ret = memgrep_get(ctx, param);
			break;
		case MEMGREP_CMD_POPULATE:
			ret = memgrep_populate(ctx, (const char *)param);
			break;
		case MEMGREP_CMD_SEARCH:
			ret = memgrep_search(ctx, (const char *)param);
			break;
		case MEMGREP_CMD_REPLACE:
			ret = memgrep_replace(ctx, (const char *)param);
			break;
		case MEMGREP_CMD_SEARCHREPLACE:
			ret = memgrep_searchreplace(ctx, (const char *)param, (const char *)data);
			break;
		case MEMGREP_CMD_DUMP:
			ret = memgrep_dump(ctx);
			break;
		default:
			break;
	}

	return ret;
}

unsigned long memgrep_initialize(MEM_CTX *ctx, enum MemoryMedium medium, void *data)
{
	unsigned long ret = 0;

	ctx->medium = medium;

	switch (medium)
	{
		case MEMORY_MEDIUM_PID:
			ctx->pid  = (unsigned long)data;

			ctx->functions.open            = pid_open;
			ctx->functions.close           = pid_close;
			ctx->functions.getSections     = pid_getSections;
			ctx->functions.get             = pid_get;
			ctx->functions.put             = pid_put;
			ctx->functions.populateKeyword = pid_populateKeyword;
			break;
		case MEMORY_MEDIUM_CORE:
			ctx->core = strdup((data)?(char*)data:"");

			if (!ctx->core)
				return 0;
			
			ctx->functions.open            = core_open;
			ctx->functions.close           = core_close;
			ctx->functions.getSections     = core_getSections;
			ctx->functions.get             = core_get;
			ctx->functions.put             = core_put;
			ctx->functions.populateKeyword = core_populateKeyword;
			break;
		default:
			if (ctx->flags & MEMGREP_FLAG_VERBOSE)
				fprintf(stderr, "memgrep_initialize(): Invalid medium specified.\n");
			return 0;
	}

	if (!(ret = ctx->functions.open(ctx)))
	{
		if (ctx->flags & MEMGREP_FLAG_VERBOSE)
			fprintf(stderr, "memgrep_initialize(): Couldn't open medium device.\n");

		return 0;
	}

	return ctx->functions.getSections(ctx);
}

unsigned long memgrep_deinitialize(MEM_CTX *ctx)
{
	unsigned long ret = ctx->functions.close(ctx);

	memset(&ctx->functions, 0, sizeof(MEMGREP_FUNCTIONS));

	switch (ctx->medium)
	{
		case MEMORY_MEDIUM_CORE:
			if (ctx->core)	
				free(ctx->core);
			break;
		default:
			break;
	}

	ctx->medium = MEMORY_MEDIUM_UNKNOWN;

	if (ctx->addrs)
		free(ctx->addrs);

	return ret;
}

unsigned long memgrep_set(MEM_CTX *ctx, unsigned long param, unsigned long data)
{
	unsigned long ret = 0;
	
	switch (param)
	{
		case MEMGREP_PARAM_FLAGS:
			ctx->flags = data;
			break;
		case MEMGREP_PARAM_LENGTH:
			ctx->length = data;
			break;
		case MEMGREP_PARAM_PADDING:
			ctx->padding = data;
			break;
		default:
			break;
	}

	return ret;
}

unsigned long memgrep_get(MEM_CTX *ctx, unsigned long param)
{
	unsigned long ret = 0;

	switch (param)
	{
		case MEMGREP_PARAM_FLAGS:
			ret = ctx->flags;
			break;
		case MEMGREP_PARAM_LENGTH:
			ret = ctx->length;
			break;
		case MEMGREP_PARAM_PADDING:
			ret = ctx->padding;
			break;
		default:
			break;
	}

	return ret;
}

unsigned long memgrep_populate(MEM_CTX *ctx, const char *addresses)
{
	const char *comma = NULL, *curr = addresses;
	unsigned char done = 0;

	while (curr && !done)
	{
		comma = strchr(curr, ',');

		if (comma)
			*(char *)comma = 0;
		else
			done = 1;

		// Invalid format
		if (strlen(curr) < 2)
			break;

		if (!isdigit(*curr))
			ctx->functions.populateKeyword(ctx, curr);
		else
		{
			if (!ctx->addrs)
				ctx->addrs = (unsigned long *)malloc((++ctx->numAddrs) * sizeof(unsigned long));
			else
				ctx->addrs = (unsigned long *)realloc(ctx->addrs, (++ctx->numAddrs) * sizeof(unsigned long));

			if (curr[1] == 'x')
				ctx->addrs[ctx->numAddrs-1] = strtoul(curr+2, NULL, 16);
			else
				ctx->addrs[ctx->numAddrs-1] = strtoul(curr, NULL, 16);
		}

		if (comma)
			*(char *)comma = ',';

		curr = comma + 1;
	}

	return ctx->numAddrs;
}

unsigned long memgrep_replace(MEM_CTX *ctx, const char *replacePhrase)
{
	unsigned long ret = 0, x, replaceLength = 0;
	unsigned char *replaceBuf = NULL;

	if (!translateToHex(replacePhrase, &replaceBuf, &replaceLength))
	{
		if (ctx->flags & MEMGREP_FLAG_VERBOSE)
			fprintf(stderr, "memgrep_replace(): Invalid replace phrase specified.\n");
			
		return 0;
	}

	for (x = 0; x < ctx->numAddrs; x++)
	{
		if (ctx->flags & MEMGREP_FLAG_PROMPT)
		{
			char rep[32];

			fprintf(stdout, "Replace %lu bytes of memory at %.8x [Y/n]?  ", replaceLength, (unsigned int)ctx->addrs[x]);
			fflush(stdout);

			fgets(rep, sizeof(rep) - 1, stdin);

			if (rep[0] == 'n' || rep[1] == 'N')
				continue;
		}

		if (ctx->flags & MEMGREP_FLAG_VERBOSE)
			fprintf(stdout, "Replacing memory %.8x with %lu bytes of data...\n", (unsigned int)ctx->addrs[x], replaceLength);

		if (ctx->functions.put(ctx, ctx->addrs[x], replaceBuf, replaceLength))
			ret++;
	}

	free(replaceBuf);

	return ret;
}

unsigned long memgrep_search(MEM_CTX *ctx, const char *searchPhrase)
{
	return memgrep_searchreplace(ctx, searchPhrase, NULL);
}

unsigned long memgrep_searchreplace(MEM_CTX *ctx, const char *searchPhrase, const char *replacePhrase)
{
	unsigned long ret = 0;
	unsigned char *searchBuf = NULL, *replaceBuf = NULL;
	unsigned long searchLength = 0, replaceLength = 0, x;

	if ((!searchPhrase) || (!translateToHex(searchPhrase, &searchBuf, &searchLength)))
	{
		if (ctx->flags & MEMGREP_FLAG_VERBOSE)
			fprintf(stderr, "memgrep_search(): You did not specify a valid search phrase.\n");

		return 0;
	}

	if (replacePhrase)
		translateToHex(replacePhrase, &replaceBuf, &replaceLength);

	for (x = 0; x < ctx->numAddrs; x++)
	{
		unsigned long addr = ctx->addrs[x], end = (ctx->length)?ctx->addrs[x] + ctx->length:0xffffffff;
		unsigned long left = end - addr, y, getLength, slope = 0;
		unsigned char *buf = NULL, *potential;

		if (ctx->flags & MEMGREP_FLAG_VERBOSE)
			fprintf(stdout, "Searching 0x%.8x => 0x%.8x...\n", (unsigned int)ctx->addrs[x], (unsigned int)end);

		while (left > 0)
		{
			slope = getLength = (left < 256)?left:256;

			if (!(buf = ctx->functions.get(ctx, addr, getLength)))
				break;

			for (y = 0; y < getLength; y++)
			{
				if (buf[y] != searchBuf[0])
					continue;
				
				// Potential match?
			
				if (!(potential = ctx->functions.get(ctx, addr + y, searchLength)))
					continue;

				// So far so good.
			
				if (memcmp(potential, searchBuf, searchLength) == 0)
				{
					// If we're replacing
					
					if (replaceBuf)
					{
						ctx->functions.put(ctx, addr + y, replaceBuf, replaceLength);
							
						if (ctx->flags & MEMGREP_FLAG_VERBOSE)
							fprintf(stdout, "   replaced at 0x%.8x\n", (unsigned int)(addr + y));

						slope = replaceLength + y;
					}
					else
					{
						if (ctx->flags & MEMGREP_FLAG_VERBOSE)
							fprintf(stdout, "   found at 0x%.8x\n", (unsigned int)(addr + y));

						slope = searchLength + y;
					}

					ret++;

					break;
				}

				free(potential);
			}

			left -= slope;
			addr += slope;
	
			free(buf);
		}
	}

	if (searchBuf)
		free(searchBuf);
	if (replaceBuf)
		free(replaceBuf);

	return ret;
}

unsigned long memgrep_dump(MEM_CTX *ctx)
{
	unsigned long ret = 0, x, y, base, len;

	if (!ctx->length)
		return 0;

	for (x = 0; x < ctx->numAddrs; x++)
	{
		unsigned char *buf = ctx->functions.get(ctx, (base = ctx->addrs[x] - ctx->padding), (len = ctx->length + (ctx->padding * 2)));
		unsigned long data, count = 0, inc = sizeof(unsigned long);

		fprintf(stdout, "%lu bytes starting at %.8x (+/- %lu)...\n%.8x: ", ctx->length, (unsigned int)ctx->addrs[x], ctx->padding, (unsigned int)base);

		if (!buf)
			continue;

		if (ctx->flags & MEMGREP_FLAG_DUMPCLEAN)
			inc = 1;

		for (y = 0; y < len; y += inc)
		{
			if (ctx->flags & MEMGREP_FLAG_DUMPCLEAN)
			{
				if (isdigit(buf[y]) || isalpha(buf[y]) || buf[y] == ' ')
					fprintf(stdout, "%c", buf[y]);
				else
					fprintf(stdout, ".");

				count++;

				if (count == 16)
				{
					fprintf(stdout, "\n");
				
					if (y + 1 < len)
						fprintf(stdout, "%.8x: ", (unsigned int)(base + y + 1));

					count = 0;
				}
			}
			else
			{
				memcpy(&data, buf + y, sizeof(unsigned long));

				fprintf(stdout, "%.8x ", (unsigned int)data);

				count++;

				if (count == 4)
				{
					fprintf(stdout, "\n");
										
					if (y + 4 < len)
						fprintf(stdout, "%.8x: ", (unsigned int)(base + y + 4));

					count = 0;
				}
			}
		}

		fprintf(stdout, "\n");

		free(buf);
	}

	return ret;
}

unsigned long translateToHex(const char *fullString, unsigned char **buf, unsigned long *bufLength)
{
	const char *comma = strchr(fullString, ',');
	unsigned long ret = 0;

	if (!comma)
		return 0;

	*(char *)comma = 0;

	ret = translateFormatToHex(fullString, comma + 1, buf, bufLength);

	*(char *)comma = ',';

	return ret;
}

unsigned long translateFormatToHex(const char *format, const char *string, unsigned char **buf, unsigned long *bufLength)
{
	*bufLength = 0;

	switch (format[0])
	{
		case 's': // String
			{
				unsigned long x;
				*bufLength = strlen(string);
				*buf       = (unsigned char *)malloc(*bufLength);

				if (!*buf)
					return 0 ;

				for (x = 0; x < *bufLength; x++)
					(*buf)[x] = string[x];
			}
			break;
		case 'i': // Integer
			{
				*bufLength = sizeof(long);
				*buf       = (unsigned char *)malloc(*bufLength);

				*(*((long **)buf)) = strtol(string, NULL, 10);
			}
			break;
		case 'x': // Hex
			{
				unsigned long x, stringLen, bufPos = 0;
				char hex[3] = { 0, 0, 0 };
				*bufLength  = (stringLen = strlen(string)) / 2;
				*buf        = (unsigned char *)malloc(*bufLength);

				if (!*buf)
					return 0 ;

				for (x = 0; x < stringLen; x += 2)
				{
					hex[0] = string[x];
					hex[1] = string[x+1];

					(*buf)[bufPos++] = strtoul(hex, NULL, 16) & 0xFF;
				}
			}
			break;
	}

	return *bufLength;
}

//
// Pid operators
//

unsigned long pid_open(MEM_CTX *ctx)
{
	if (ptrace(PTRACE_ATTACH, ctx->pid, NULL, NULL) < 0)
	{
		if (ctx->flags & MEMGREP_FLAG_VERBOSE)
			perror("ptrace(ATTACH)");

		return 0;
	}

	// waitpid doesn't work for me, anyone else had that problem?
	while (ptrace(PTRACE_PEEKDATA, ctx->pid, 0, NULL) == -1 && errno == 3)
		usleep(100);

	// For signal handling
	_pid = ctx->pid;
	
	signal(SIGINT, safeCleanup);

	return 1;
}

unsigned long pid_close(MEM_CTX *ctx)
{
	if (ptrace(PTRACE_DETACH, ctx->pid, NULL, NULL) < 0)
		perror("ptrace(DETACH)");

	signal(SIGINT, SIG_DFL);

	return 1;
}

void safeCleanup(int signal)
{
	MEM_CTX t;

	t.pid = _pid;

	pid_close(&t);

	exit(0);
}

unsigned long pid_getSections(MEM_CTX *ctx)
{
	Elf32_Ehdr    elfHeader;
	Elf32_Shdr    *sectionHeaders = NULL, *stringTableHeader = NULL;
	unsigned long index = 0;
	char          path[1024], *stringTable = NULL;
	int           fd = 0;
	struct user_regs_struct regs;

	path[sizeof(path) - 1] = path[0] = 0;

	snprintf(path, sizeof(path) - 1, "/proc/%d/exe", ctx->pid);

	if ((fd = open(path, O_RDONLY)) <= 0)
		return 0;

	do
	{
		if (lseek(fd, 0, SEEK_SET) < 0)
			break;
		
		if (read(fd, &elfHeader, sizeof(elfHeader)) < 0)
			break;

		if (!(sectionHeaders = (Elf32_Shdr *)malloc(elfHeader.e_shentsize * elfHeader.e_shnum)))
			break;
		
		if (lseek(fd, elfHeader.e_shoff, SEEK_SET) < 0)
			break;

		// Read the section headers

		if (read(fd, sectionHeaders, elfHeader.e_shentsize * elfHeader.e_shnum) < 0)
			break;

		// Read the string table

		if (!(stringTableHeader = &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;
			else if (!strcmp(name, ".data"))
				ctx->sections.data = sectionHeaders[index].sh_addr;
		}

	} while (0);

	if (stringTable)
		free(stringTable);
	if (sectionHeaders)
		free(sectionHeaders);

	close(fd);

	// Get current stack address

	memset(&regs, 0, sizeof(regs));

	if (ptrace(PTRACE_GETREGS, ctx->pid, NULL, &regs) != -1)
		ctx->sections.stack = regs.esp;		
	else
	{
		if (ctx->flags & MEMGREP_FLAG_VERBOSE)
			perror("ptrace(GETREGS)");	  
	}
		
	return 1;
}

unsigned char *pid_get(MEM_CTX *ctx, unsigned long addr, unsigned long length)
{
	unsigned char *ret = (unsigned char *)malloc(length);
	unsigned long x = addr, end = addr + length, data, left = length, y, retPos = 0;

	if (!ret)
		return NULL;

	memset(ret, 0, length);

	for (; x < end; x += 4)
	{
		// If the first address is non-readable, break out and return null
		if (((data = ptrace(PTRACE_PEEKDATA, ctx->pid, x, NULL)) == -1) && (errno == 5))
		{
			if (x == addr)
			{
				free(ret);

				ret = NULL;

				break;
			}
		}

		for (y = 0; y < sizeof(unsigned long) && left != 0; y++)
		{
			ret[retPos++] = ((unsigned char *)&data)[y];
			left--;
		}
	}

	return ret;
}

unsigned long pid_put(MEM_CTX *ctx, unsigned long addr, unsigned char *buf, unsigned long length)
{
	unsigned long x = addr, end = addr + length, data, pokedata, left = length, pos = 0;
	int y;

	for (; x < end; x += 4)
	{
		data = ptrace(PTRACE_PEEKDATA, ctx->pid, x, NULL);

		for (y = 0; y < sizeof(unsigned long); y++)
		{
			if (left == 0)
				((unsigned char *)&pokedata)[y] = ((unsigned char *)&data)[y];
			else
			{
				((unsigned char *)&pokedata)[y] = buf[pos++];

				left--;
			}

		}

		ptrace(PTRACE_POKEDATA, ctx->pid, x, pokedata);
	}

	return 1;
}
			
unsigned long pid_populateKeyword(MEM_CTX *ctx, const char *keyword)
{
	unsigned long addr = 0;

	if (!strcmp(keyword, "bss"))
		addr = ctx->sections.bss;
	else if (!strcmp(keyword, "stack"))
		addr = ctx->sections.stack;
	else if (!strcmp(keyword, "rodata"))
		addr = ctx->sections.rodata;
	else if (!strcmp(keyword, "data"))
		addr = ctx->sections.data;	
	else if (!strcmp(keyword, "all"))
	{
		pid_populateKeyword(ctx, "rodata");
		pid_populateKeyword(ctx, "data");
		pid_populateKeyword(ctx, "bss");
		pid_populateKeyword(ctx, "stack");
	}
	else if (ctx->flags & MEMGREP_FLAG_VERBOSE)
		fprintf(stderr, "pid_populateKeyword(): warning: unknown keyword '%s'\n", keyword);

	if (addr)
	{
		if (!ctx->addrs)
			ctx->addrs = (unsigned long *)malloc((++ctx->numAddrs) * sizeof(unsigned long));
		else
			ctx->addrs = (unsigned long *)realloc(ctx->addrs, (++ctx->numAddrs) * sizeof(unsigned long));

		ctx->addrs[ctx->numAddrs - 1] = addr;
	
		return 1;
	}

	return 0;
}

//
// Core operators
//

unsigned long core_open(MEM_CTX *ctx)
{
	int fd = 0, index;

	// Open the file
	
	if ((fd = ctx->coreData.fd = open(ctx->core, O_RDONLY)) <= 0)
	{
		if (ctx->flags & MEMGREP_FLAG_VERBOSE)
			perror("core_open()");

		return 0;
	}

	// Load the elf header and section headers.

	do
	{
		if (lseek(fd, 0, SEEK_SET) < 0)
			break;
		
		if (read(fd, &ctx->coreData.elfHeader, sizeof(ctx->coreData.elfHeader)) < 0)
			break;

		if (!(ctx->coreData.programHeaders = (Elf32_Phdr *)malloc(ctx->coreData.elfHeader.e_phentsize * ctx->coreData.elfHeader.e_phnum)))
			break;
		
		if (lseek(fd, ctx->coreData.elfHeader.e_phoff, SEEK_SET) < 0)
			break;

		// Read the programs headers

		if (read(fd, ctx->coreData.programHeaders, ctx->coreData.elfHeader.e_phentsize * ctx->coreData.elfHeader.e_phnum) < 0)
			break;

		if (ctx->flags & MEMGREP_FLAG_VERBOSE)
			fprintf(stdout, "core file '%s' has the following VMA's:\n", ctx->core);

		for (index = 0; index < ctx->coreData.elfHeader.e_phnum; index++)
		{
			unsigned long rma = 0;

			// We'll mmap everything but executable code and as long as it's a loadable segment.

			if (ctx->coreData.programHeaders[index].p_flags & PF_X)
				continue;

			if (ctx->coreData.programHeaders[index].p_type != PT_LOAD)
				continue;

			// No length? too bad.
			
			if (!ctx->coreData.programHeaders[index].p_filesz)
				continue;

			// mmap this section

			rma = (unsigned long)mmap(NULL, ctx->coreData.programHeaders[index].p_filesz, PROT_READ, MAP_PRIVATE, fd, ctx->coreData.programHeaders[index].p_offset);

			if (!rma)
				continue;

			if (!ctx->coreData.sections)
				ctx->coreData.sections = (CORE_MEMORY_SECTIONS *)malloc(sizeof(CORE_MEMORY_SECTIONS));
			else
				ctx->coreData.sections = (CORE_MEMORY_SECTIONS *)realloc(ctx->coreData.sections, sizeof(CORE_MEMORY_SECTIONS) * (ctx->coreData.numSections + 1));

			ctx->coreData.sections[ctx->coreData.numSections].vma    = ctx->coreData.programHeaders[index].p_vaddr;
			ctx->coreData.sections[ctx->coreData.numSections].rma    = rma;
			ctx->coreData.sections[ctx->coreData.numSections].length = ctx->coreData.programHeaders[index].p_filesz;

			if (ctx->flags & MEMGREP_FLAG_VERBOSE)
				fprintf(stdout, "   %.8x -> %.8x (%lu bytes)\n", 
									 (unsigned int)ctx->coreData.sections[ctx->coreData.numSections].vma,
									 (unsigned int)(ctx->coreData.sections[ctx->coreData.numSections].vma + ctx->coreData.sections[ctx->coreData.numSections].length),
									 ctx->coreData.sections[ctx->coreData.numSections].length);
			
			ctx->coreData.numSections++;
		}

	} while (0);
			
	if (ctx->flags & MEMGREP_FLAG_VERBOSE)
		fprintf(stdout, "\n");

	return ctx->coreData.numSections;
}

unsigned long core_close(MEM_CTX *ctx)
{
	if (ctx->coreData.fd)
		close(ctx->coreData.fd);

	if (ctx->coreData.programHeaders)
		free(ctx->coreData.programHeaders);

	if (ctx->coreData.sections)
	{
		int x = 0;

		for (; x < ctx->coreData.numSections; x++)
		{
			if (ctx->coreData.sections[x].rma)
				munmap((void *)ctx->coreData.sections[x].rma, ctx->coreData.sections[x].length);
		}

		free(ctx->coreData.sections);

		ctx->coreData.sections    = NULL;
		ctx->coreData.numSections = 0;
	}

	return 0;
}

unsigned long core_getSections(MEM_CTX *ctx)
{
	return 1;
}

unsigned char *core_get(MEM_CTX *ctx, unsigned long addr, unsigned long length)
{
	unsigned long copyLength = length;
	unsigned char *ret = NULL;
	int index = 0;

	for (; index < ctx->coreData.numSections; index++)
	{
		unsigned long end = ctx->coreData.sections[index].vma + ctx->coreData.sections[index].length;
		void *real = NULL;

		// In between our range?
		if (!(addr >= ctx->coreData.sections[index].vma) || !(addr < end))
			continue;

		if (addr + length > end)
			copyLength = end - addr;

		real = (void *)(ctx->coreData.sections[index].rma + (addr - ctx->coreData.sections[index].vma));

		if (!(ret = (unsigned char *)malloc(length)))
			break;

		memset(ret, 0, length);

		memcpy(ret, real, copyLength);

		break;
	}

	return ret;
}

unsigned long core_put(MEM_CTX *ctx, unsigned long addr, unsigned char *buf, unsigned long bufLength)
{
	if (ctx->flags & MEMGREP_FLAG_VERBOSE)
		fprintf(stderr, "core_put(): warning: memory write access is not supported with core files.\n");

	return 0;
}

unsigned long core_populateKeyword(MEM_CTX *ctx, const char *keyword)
{
	if (!strcmp(keyword, "all"))
	{
		int x = 0;

		for (; x < ctx->coreData.numSections; x++)
		{
			if (ctx->coreData.sections[x].vma)
			{
				if (!ctx->addrs)
					ctx->addrs = (unsigned long *)malloc((++ctx->numAddrs) * sizeof(unsigned long));
				else
					ctx->addrs = (unsigned long *)realloc(ctx->addrs, (++ctx->numAddrs) * sizeof(unsigned long));

				ctx->addrs[ctx->numAddrs - 1] = ctx->coreData.sections[x].vma;
			}		
		}
	
		return 1;
	}
	else if (ctx->flags & MEMGREP_FLAG_VERBOSE)
		fprintf(stderr, "core_populateKeyword(): warning: unknown keyword '%s'\n", keyword);

	return 0;
}

void displayHelp()
{
	fprintf(stdout, "memgrep -- Run-time/core-time memory searching, dumping and modifying utility.\n"
						 "Usage: ./memgrep [-p pid] [-o core] [-d] [-r] [-s] [-a addr1,addr2,bss,addr3] [-l length] [-f fmt,search data]\n" 
						 "                 [-t fmt,replace data] [-b pad] [-c] [-v] [-h]\n"
						 "\n"
						 "   -p [pid]   The process id to operate on.\n"
						 "   -o [core]  The core file to operate on.\n"
						 "   -d         Dump memory from the specified address(es) for the given length (-l).\n"
						 "   -r         Replace memory at the specified address(es).  If -s is also specified,\n"
						 "              only memory that matches the search criteria will be replaced\n"
						 "   -s         Search memory at the specified address(es).\n"
						 "   -a [addr]  The address(es) to operate on seperated by commas.  Addresses can be\n"
                   "              in the following format:\n"
						 "                 0x821c4ac\n"
						 "                 821c4ac\n"
						 "              Also, the following keywords can be used:\n"
						 "                 bss       -> Uses the VMA associated with the .bss section (commonly heap).\n"
						 "                 stack     -> Dynamically determines the current stack pointer.\n"
						 "                 rodata    -> Uses the VMA associated with the .rodata section (read-only data, ie, static text).\n"
						 "                 data      -> Uses the VMA associated with the .data section (data, ie, global variables).\n"
						 "                 all       -> Uses bss, stack, rodata, data.  Only valid keyword for core files.\n"
						 "   -l [len]   The length to use when searching or dumping.  A length of 0 means search\n"
						 "              till end-of-memory.\n"
						 "   -f [data]  This specifies the search criteria.  Multiple formats are accepted for ease\n"
						 "              of use.  Below are accepted formats and their examples:\n"
						 "                 s -> String format  (Ex: 's,Testing')\n"
						 "                 x -> Hex format     (Ex: 'x,00414100AB')\n"
						 "                 i -> Integer format (Ex: 'i,4724')\n"
						 "   -t [data]  This specifies the replace data.  The same formats used with the -f parameter\n"
						 "              are valid for the -t parameter.\n"
						 "   -b [pad]   Number of bytes of padding to use around dump addresses (default is 0).\n"
						 "   -c         Dump data in clear-text, not hex.\n"
						 "   -v         Version information.\n"
						 "   -h         Help.\n"
			  			 "\n"
						 "   Example search (search for 'Jane' in .bss):\n\n"
						 "      ./memgrep -p 1335 -s -a bss -f s,Jane\n\n"
						 "   Example replace (replace memory at 0x8423143 and 0x8443147 with 0x00ff0041):\n\n"
						 "      ./memgrep -p 1335 -r -a 0x8423143,0x8443147 -t x,00ff0041\n\n"
						 "   Example search/replace (Replace 'Test' with 'Rest' in .bss and .rodata):\n\n"
						 "      ./memgrep -p 1335 -s -r -a bss,rodata -f s,Test -t s,Rest\n\n"
						 "   Example dump (Dump memory starting at 0x8422113 for 16 bytes):\n\n"
						 "      ./memgrep -p 1335 -d -a 0x8422113 -l 16\n\n"
						 "\n"
						 );

	exit(0);
}

void displayVersion()
{
	fprintf(stdout, "memgrep -- Your friendly neighborhood memory mangler. (%s)\n"
						 "           (c) 2002-2003 uninformed research\n\n"
						 "           skape mmiller@hick.org\n", memgrepVersion);

	exit(0);
}

