#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include "byakugan.h"
#include "jutsu.h"
#include "msfpattern.h"
#include "stdwindbg.h"

struct requestQueue	jutsuRequests;
struct trackedBuf	*trackedBufList = NULL;
struct trackedVal	*trackedValList = NULL;

ULONG64				disassemblyBuffer;
HANDLE				processHandle = 0;

SOCKET				ListenSocket = INVALID_SOCKET,
	                ClientSocket = INVALID_SOCKET;


//IDebugClient		msfClient;


char *regs[] = {
    "eax",
    "ebx",
    "ecx",
    "edx",
    "esp",
    "ebp",
    "eip",
    NULL
};

void helpJutsu(void) {
	return;
}

void memDiffJutsu(char *inputType, DWORD size, char *input, ULONG64 address) {
	DWORD	i, j, valResult, readSize, numBadChars = 0;
	BOOL	upperFlag, lowerFlag, nullFlag;
	char	*pureBuf = NULL, findValExpression[64] = {'\x00'};
	char	lineExpected[16], lineActual[16];
	
	struct trackedBuf *curr = trackedBufList;
	struct corruption *badChars;

	// Valid inputs: ASCII, hex, file, buf
	if (!_stricmp(inputType, "ASCII")) {
		pureBuf = input;
	} else if (!_stricmp(inputType, "hex")) {
		if (size != parseHexInput(input, size, pureBuf)) {
			dprintf("[J] Failed to parse %d bytes from hex input.\n", size);
			return;
		}
	} else if (!_stricmp(inputType, "file")) {
		readSize = readFileIntoBuf(input, size, &pureBuf, 0);
		if ((size && size != readSize) || readSize == 0) {
			dprintf("[J] Failed to read %d bytes from %s.\n", size, input);
			return;
		}
		size = (size ? size : readSize);
	} else if (!_stricmp(inputType, "buf")) {
		// Grab the buf by name from the trackedBufList
     
    	while (curr != NULL) {
        	if(!_stricmp(input, curr->bufName)) {
				pureBuf = curr->bufPatt;
            	break;
			}
        	curr = curr->next;
    	}
		if (pureBuf == NULL) {
			dprintf("[J] Unable to find buffer: %s\n", input);
			return;
		}	
	} else {
		dprintf("[J] The valid input types are buf, hex, and file.\n");
		return;
	}

	upperFlag = lowerFlag = nullFlag = FALSE;
	badChars = (struct corruption *) malloc(size * sizeof (struct corruption));

	dprintf("\t\t\tACTUAL\t\t\t\t\t\t\t\tEXPECTED\n");
	for (i = 0; i < size; i++) {
		// Get byte at the important memory location
		StringCchPrintf(findValExpression, sizeof(findValExpression), 
				"poi(0x%08x)", address + i);
		valResult = (GetExpression(findValExpression) & 0xFF);
		
		lineExpected[i%16] = pureBuf[i];
		lineActual[i%16] = valResult;

		if (pureBuf[i] != valResult) {
			badChars[numBadChars].value = pureBuf[i];
			badChars[numBadChars].offset = i;
			badChars[numBadChars].seenAgain = FALSE;
			badChars[numBadChars].seenBefore = FALSE;
			for (j = 0; j < numBadChars; j++) {
				if (badChars[j].value == badChars[numBadChars].value) {
					badChars[numBadChars].seenBefore = TRUE;
				}
			}
			numBadChars++;
		} else {
			for (j = 0; j < numBadChars; j++)
				if (valResult == badChars[j].value)
					badChars[j].seenAgain = TRUE;
		}


		if (i % 16 == 15 || i == size-1) {
			// Print the actual characters with differences in bold
			for (j = 0; j != i % 16 + 1; j++) {
				// Diff the two locations
				if (lineActual[j] != lineExpected[j]) {
					// Store badchars, and bad offsets
					// Print this character in bold!
					StringCchPrintf(findValExpression, sizeof(findValExpression), 
					".printf /D \"<b><red>%02x</red></b> \"", lineActual[j]);
					g_ExtControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, findValExpression, 
										DEBUG_EXECUTE_NOT_LOGGED);

				} else {
					dprintf("%02x ", lineActual[j]);
				}

				// Take note of upper / lower / null exclusions
			}
			if (i == size-1)
				for (j = 0; j < 15 - i % 16; j+=2)
					dprintf("\t");
			dprintf("\t");

			// Now print the Expected characters
			for (j = 0; j != i % 16 + 1; j++) { 
				dprintf("%02x ", lineExpected[j]);
			}
			dprintf("\n");
		}
	}

	// Display bad chars
	i = 0;
	if (numBadChars) {
		dprintf("\n[J] Bytes replaced: ");
		while (i < numBadChars) {
			if (!badChars[i].seenAgain && !badChars[i].seenBefore)
				dprintf("0x%02x ", badChars[i].value);
			i++;
		}
		i = 0;
		dprintf("\n[J] Offset corruption occurs at: ");
        while (i < numBadChars) {
            if (badChars[i].seenAgain) 
                dprintf("%02x ", badChars[i].offset);
            i++;
        }
		dprintf("\n");
	}
	
	// Unless pureBuf came from a tracked buffer, free the memory
	if (_stricmp(inputType, "buf"))
		free(pureBuf);
	free(badChars);
}

void listTrackedVals() {
	struct trackedVal   *newTrackedVal;

	if (trackedValList == NULL) {
		dprintf("[J] Currently tracking no primitive values.\n");
		return;
	}

	dprintf("[J] Currently tracking:\n");

	newTrackedVal = trackedValList;
	while (newTrackedVal != NULL) {
		dprintf("\tName: %s\t\tSize: %d\tCandidates: %d\n", 
				newTrackedVal->valName, newTrackedVal->valSize, newTrackedVal->candidates);
		newTrackedVal = newTrackedVal->next;
	}
}

void listTrackedValByName(char *name) {
	struct trackedVal   *newTrackedVal;
	struct valInstance	*curr;

	newTrackedVal = trackedValList;
	if (newTrackedVal == NULL) goto nada;

	while (newTrackedVal != NULL) {
        if (!_stricmp(newTrackedVal->valName, name))
            break;
        newTrackedVal = newTrackedVal->next;
    }

	if (newTrackedVal) {

		curr = newTrackedVal->instances;
		
		if (curr != NULL)
			dprintf("[J] Currently tracking %d candidates for %s:\n", 
					newTrackedVal->candidates, newTrackedVal->valName);
		else
			goto nada;

		while (curr != NULL) {
			dprintf("\tAddress: 0x%08x\n", curr->address);
			curr = curr->next;
		}
		return;
	}
nada:
	dprintf("[J] No candidates are being tracked for %s.\n", name);	
}

void trackValJutsu(char *name, DWORD size, DWORD value) {
    struct trackedVal   *newTrackedVal, *parent = NULL;
	struct valInstance	*last, *curr;
	char				findValExpression[18] =  {'\x00'};
	DWORD				valResult, andExpression;

	switch(size) {
		case 1: andExpression = 0xFF;			break;
		case 2: andExpression = 0xFFFF;			break;
		case 4: andExpression = 0xFFFFFFFF;		break;
		default:
			dprintf("[J] Valid primitive sizes are 1, 2, and 4.\n");
			return;
	}                    	
	
	newTrackedVal = trackedValList;
	while (newTrackedVal != NULL) {
		if (!_stricmp(newTrackedVal->valName, name))
			break;
		newTrackedVal = newTrackedVal->next;
	}

	// Search the list for the new value, purge old addresses
	if (newTrackedVal) {
		dprintf("[J] Narrowing down candidate list for %s from %d candidates.\n", name, newTrackedVal->candidates);
		curr = newTrackedVal->instances;
		last = NULL;

		while (curr != NULL) {
			StringCchPrintf(findValExpression, sizeof(findValExpression), "poi(0x%08x)", curr->address);
			valResult = (GetExpression(findValExpression) & andExpression);
        	if (value != valResult) {
				if (last) {
					last->next = curr->next;
					free(curr);
					curr = last->next;
				} else {
					newTrackedVal->instances = curr->next;
					free(curr);
					curr = newTrackedVal->instances;
				}
				newTrackedVal->candidates--;
				if (newTrackedVal->candidates == 1) {
					dprintf("[J] Value %s is stored at address 0x%08x\n", 
							newTrackedVal->valName, newTrackedVal->instances->address);
					return;
				}
			} else {
				last = curr; curr = curr->next;
			}
		}
		dprintf("[J] Narrowed down address of %s to %d possible candidates.\n", name, newTrackedVal->candidates);
		return;
	} 
	dprintf("[J] Creating new list of candidates for %s.\n", name);

	// Create a new list and search all memory for the value
    newTrackedVal = (struct trackedVal *) malloc(sizeof (struct trackedVal));
    if (newTrackedVal == NULL) {
        dprintf("[J] OOM!");
        return;
    }
    newTrackedVal->next 		= NULL;
	newTrackedVal->valSize 		= size;
	newTrackedVal->valName 		= _strdup(name);
	if(!newTrackedVal->valName) {
		free(newTrackedVal);
		dprintf("[J] OOM!\n");
		return;
	}

	newTrackedVal->candidates = findAllVals((BYTE*) &value, size, &(newTrackedVal->instances));	
	dprintf("[J] Discovered %d possible candidate addresses for %s\n", newTrackedVal->candidates, name);

	newTrackedVal->next = trackedValList;
	trackedValList = newTrackedVal;

	return;
}

void bindJutsu(char  *bindPort) {
	HANDLE					hThread;
	DWORD					dwThreadId;
	IDebugOutputCallbacks	*fuzzerOutputCallback;

	// Initialize Request Queue
	memset(&jutsuRequests, 0, sizeof (struct requestQueue));

	// Fire up backchannel thread
    hThread = CreateThread(     NULL,
                                0,
                                listenJutsu,
                                (LPVOID) bindPort,
                                0,
                                &dwThreadId);

    if (hThread == NULL)
        dprintf("[Byakugan] CreateThread() failed.\n");
}

DWORD WINAPI listenJutsu(LPVOID lpvParam) {
	WSADATA		wsaData;
    char		recvbuf[DEFAULT_BUFLEN];
    ULONG		iResult, iSendResult;
    ULONG		recvbuflen = DEFAULT_BUFLEN;
	char		*bindPort = (char *) lpvParam;
    struct addrinfo		*result = NULL,
						hints;
	

	dprintf("[J] Creating Metasploit back channel on port %s... ", bindPort);

	if (WSAStartup( MAKEWORD( 2, 2 ), &wsaData) != 0) {
        dprintf("Failed!: %d\n", WSAGetLastError());
		return (-1);
	}

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the server address and port
    iResult = getaddrinfo(NULL, bindPort, &hints, &result);
    if ( iResult != 0 ) {
        dprintf("Failed!: %d\n", WSAGetLastError());
        WSACleanup();
        return (-1);
    }

    // Create a SOCKET for connecting to server
    ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ListenSocket == INVALID_SOCKET) {
        dprintf("Failed!: %d\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return (-1);
    }

    // Setup the TCP listening socket
    iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        dprintf("Failed!: %d\n", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        WSACleanup();
        return (-1);
    }

    freeaddrinfo(result);

    iResult = listen(ListenSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR) {
        dprintf("Failed!: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return (-1);
    }

	dprintf("Listening.\n");
    
	// Accept a client socket
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        dprintf("[J] Back channel accept failed: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return (-1);
    }

    // No longer need server socket
    closesocket(ListenSocket);

	// Register new output callback
	//fuzzerOutputCallback = new IDebugOutputCallbacks();

	// Register new event callback

    // Receive until the peer shuts down the connection
    do {
		memset(recvbuf, 0, DEFAULT_BUFLEN);
        
		iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0)
			parseJutsu(recvbuf, iResult);
        else if (iResult == 0)
            dprintf("[J] Back channel connection closing...\n");
        else  {
            dprintf("[J] Back channel recv failed: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
            return (-1);
        }
    } while (iResult > 0);

    // shutdown the connection since we're done
    iResult = shutdown(ClientSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        dprintf("[J] Back channel shutdown failed: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return (-1);
    }

    // cleanup
    closesocket(ClientSocket);
    WSACleanup();


	return (0);
}

void parseJutsu(char *buf, ULONG buflen) {
	struct request			*newRequest, *node;
	struct requestHeader	*reqHead;

	//dprintf("[J] Back channel got: %s\n", buf);
	
	reqHead = (struct requestHeader *) buf;
	if ((reqHead->length + 4) > buflen || buflen < 5 || reqHead->length > 0xFFFD) {
		dprintf("[J] Received a malformed jutsu request! :(\n");
		return;
	}
		
	newRequest = (struct request *) malloc(sizeof (struct request));
	if (newRequest == NULL) {
		dprintf("[J] Failed to allocate! :(\n");
		return;
	}
	
	newRequest->type	= reqHead->type;
	newRequest->length	= reqHead->length;
	newRequest->data	= (BYTE *) malloc(newRequest->length + 1);
	if (newRequest->data == NULL) {
		dprintf("[J] Failed to allocate! :(\n"); 
		free(newRequest);
		return; 
	}
	newRequest->next	= NULL;

	memcpy(newRequest->data, (buf+4), newRequest->length);

	if (jutsuRequests.head != NULL) {
		node = jutsuRequests.head;
		while (node->next != NULL)
			node = node->next;
		node->next = newRequest;
	} else {
		jutsuRequests.head = newRequest;
	}
	jutsuRequests.length++;

	return;
}

void showRequestsJutsu() {
	struct request	*node;
	USHORT			i;

	dprintf("[J] Currently waiting on %d requests:\n", jutsuRequests.length);

	node = jutsuRequests.head;
	while (node != NULL) {
		dprintf("Type: 0x%04x\tLength: 0x%04x\nData:",
				node->type, node->length);
		for (i = 0; i < node->length; i++) {
			if (i % 32 == 0) dprintf("\n");
			if (i % 8 == 0) dprintf("\t0x");
			dprintf("%01x", node->data[i]);
		}
		dprintf("\n\n");
		node = node->next;
	}
}
void identBufJutsu(char *inputType, char *bufName, char *bufPatt, DWORD size, DWORD offset) {
	struct trackedBuf	*newTrackedBuf, *curBuf;
	char				*msfPattern;
	DWORD				readSize;
	HANDLE				hFile;

	if (trackedBufList != NULL) {
		curBuf = trackedBufList;
		while (curBuf->next != NULL) {
			if (!_stricmp(bufName, curBuf->bufName)) {
				dprintf("[J] Buf %s already exists. Please delete it first.\n", bufName);
				return;
			}
			curBuf = curBuf->next;
		}
	}

	newTrackedBuf = (struct trackedBuf *) malloc(sizeof (struct trackedBuf));
	if (newTrackedBuf == NULL) {
		dprintf("[J] OOM!");
		return;
	}

	newTrackedBuf->next = NULL;
	newTrackedBuf->prev = NULL;
	if (!_stricmp(inputType, "msfpattern")) {
		size = strtoul(bufPatt, NULL, 10);
		msfPattern = (char *) malloc(size+1);
		if (msfPattern == NULL) {
			dprintf("[J] Failed to allocate %d bytes!\n", size+1);
			return;
		}
		msf_pattern_create(size, msfPattern);
		msfPattern[size] = '\x00';
		newTrackedBuf->bufPatt = msfPattern;
	} else if (!_stricmp(inputType, "ascii")){
		newTrackedBuf->bufPatt = _strdup(bufPatt);
		size = strlen(bufPatt);
	} else if (!_stricmp(inputType, "file")) {
      	readSize = readFileIntoBuf(bufPatt, size, &(newTrackedBuf->bufPatt), 0);
      	if ((size && readSize != size) || readSize == 0) {
              dprintf("[J] Unable to read %d bytes from %s\n", size, bufName);
              dprintf("\nThis command requires a buffer type, name, (sometimes) value, and size - maybe you forgot one?\n");
              return;
      	}
      	size = (size ? size : readSize);
   	} else if (!_stricmp(inputType, "smartFile")) {
        readSize = readFileIntoBuf(bufPatt, size, &(newTrackedBuf->bufPatt), offset);
		if ((size && readSize != size) || readSize == 0) {
			dprintf("[J] Unable to read %d bytes from %s\n", size, bufName);
			dprintf("\nThis command requires a buffer type, name, (sometimes) value, and size - maybe you forgot one?\n");
			return;
		}
		size = (size ? size : readSize);
	}


	newTrackedBuf->bufName = _strdup(bufName);
	newTrackedBuf->bufSize = size;
	if (newTrackedBuf->bufName == NULL || newTrackedBuf->bufPatt == NULL) {
		dprintf("[J] OOM!");
		return;
	}

	if (trackedBufList == NULL) {
		trackedBufList = newTrackedBuf;
	} else {
		curBuf->next			= newTrackedBuf;
		newTrackedBuf->prev		= curBuf;
	}
	dprintf("[J] Creating buffer %s.\n", bufName);
}

void rmBufJutsu(char *bufName) {
	struct trackedBuf	*curBuf;
	
	curBuf = trackedBufList;
	while (curBuf != NULL) {
		if(!_stricmp(bufName, curBuf->bufName))
			break;
		curBuf = curBuf->next;
	}
	if (curBuf != NULL) {
		if (curBuf->prev != NULL)
			curBuf->prev->next = curBuf->next;
		if (curBuf->next != NULL)
			curBuf->next->prev = curBuf->prev;
		if (curBuf == trackedBufList)
			trackedBufList = curBuf->next;
		free(curBuf->bufName);
		free(curBuf->bufPatt);
		free(curBuf);
		dprintf("[J] Removed buffer: %s\n", bufName);
	} else {
		dprintf("[J] Unable to find buffer: %s\n", bufName);
	}
}

void listTrackedBufJutsu() {
	struct trackedBuf	*curBuf;
	DWORD				i;

	curBuf = trackedBufList;
	if (curBuf == NULL) {
		dprintf("[J] Currntly tracking no buffer patterns.\n");
	} else {
		dprintf("[J] Currently tracked buffer patterns:\n");
		while (curBuf != NULL) {
			dprintf("\tBuf: %s\tPattern: ", curBuf->bufName);
			for (i = 0; i < curBuf->bufSize; i++)
				dprintf("%c", curBuf->bufPatt[i]);
			dprintf("\n");
			curBuf = curBuf->next;
		}
	}
	dprintf("\n");
}

void hunterJutsu() {
	struct trackedBuf	*curBuf;
	struct bufInstance	*instance;
	ULONG				i, j, range, addr, *nextNum, foundInstance;
	BOOLEAN				caught;
	char				*corUpper, *corLower, *corUni;
	ULONG64				ptr = 0;

    for (i = 0; regs[i] != NULL; i++) {
		addr = GetExpression(regs[i]);
		curBuf = trackedBufList;
		caught = FALSE;
    	while (curBuf != NULL) {
			range = curBuf->bufSize;
			for (j = 0; j < range-3; j++) {
				nextNum = (ULONG *) ((curBuf->bufPatt) + j);
				if (addr != 0 && *nextNum == addr) {
					dprintf("[J] Controlling %s with %s at offset %d.\n", 
							regs[i], curBuf->bufName, j);
					caught = TRUE;
					break;
				}
			}
			curBuf = curBuf->next;
			if (caught)
				break;
		}

    }
	
	// Now, find all instances of buffers in memory with a fuzzy match! :)
	curBuf = trackedBufList;
	while (curBuf != NULL) {
		foundInstance = searchMemory((unsigned char *) curBuf->bufPatt, 
				(curBuf->bufSize > 32) ? 32 : curBuf->bufSize, &ptr);
		if (foundInstance != 0) {
			// try for larger increments
			instance = (struct bufInstance *) malloc(sizeof (struct bufInstance));
			memset(instance, 0, sizeof (struct bufInstance));
			instance->address = foundInstance;
			dprintf("[J] Found buffer %s @ 0x%08x\n", curBuf->bufName, foundInstance);
		}
			// try standard corruptions
			range = (curBuf->bufSize > 32) ? 32 : curBuf->bufSize;
			corUpper	= (char *) malloc(range + 1);
			corLower	= (char *) malloc(range + 1);
			corUni		= (char *) malloc((range + 1) * 2);
			for (i = j = 0; i < range; i++) {
				corUpper[i] = (char) toupper(curBuf->bufPatt[i]);
				corLower[i] = (char) tolower(curBuf->bufPatt[i]);
				corUni[j++] = curBuf->bufPatt[i];
				corUni[j++] = '\x00';
			}
			if ((foundInstance = searchMemory((unsigned char *) corUpper, range, &ptr)) != 0)
				dprintf("[J] Found buffer %s @ 0x%08x - Victim of toUpper!\n",
						curBuf->bufName, foundInstance);
			if ((foundInstance = searchMemory((unsigned char *) corLower, range, &ptr)) != 0)
				dprintf("[J] Found buffer %s @ 0x%08x - Victim of toLower!\n",
						curBuf->bufName, foundInstance);
			if ((foundInstance = searchMemory((unsigned char *) corUni, range*2, &ptr)) != 0)
				dprintf("[J] Found buffer %s @ 0x%08x - Victim of Unicode Conversion!\n",
						curBuf->bufName, foundInstance);
			free(corUpper);
			free(corLower);
			free(corUni);
		

		curBuf = curBuf->next;
	}
	
}

ULONG64 allocateMemoryBlock(unsigned long size){
	unsigned long processId = 0;
	void * allocBuffer = 0;
	ULONG	Class;
	ULONG	Qualifier;

	g_ExtControl->GetDebuggeeType(&Class, &Qualifier);
	if (Class & DEBUG_CLASS_USER_WINDOWS) {
		// USER LAND!
		if(g_ExtSystem->GetCurrentProcessSystemId(&processId) != S_OK){
			dprintf("[J] failed to find process id\n");
			return 0;
		}
	
		if(!(processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId))){
			dprintf("[J] OpenProcess failed\n");
			return 0;
		}
	
		if(!(allocBuffer = VirtualAllocEx(processHandle, NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE))){
			dprintf("[J] VirtualAllocEx failed\n");
			CloseHandle(processHandle);
			return 0;
		}
	} else {
		// KERNEL LAND!
		
	}
	//CloseHandle(processHandle);

	return ((ULONG64)allocBuffer);
}

unsigned short getInstructionBytes(char * instruction, unsigned char * opcodeBuffer){
	BYTE zero = 0;
	BYTE byteCounter = 0;
	ULONG64 byteEnd = 0;
	BYTE i = 0;

    if(!disassemblyBuffer){
        if(!(disassemblyBuffer = allocateMemoryBlock(0x1000))){
            dprintf("[J] allocateMemoryBlock failed\n");
            return (0);
        }
    }
	

	if(g_ExtControl->Assemble(disassemblyBuffer, instruction, &byteEnd) != S_OK){
		dprintf("[J] failed to assemble instruction\n");
		return (0);
	}

	if(!ReadMemory(disassemblyBuffer, opcodeBuffer, (byteEnd-disassemblyBuffer), NULL)){
		dprintf("[J] failed to read opcode sequence\n");
		return (0);
	}

	for(i=0; i<(byteEnd-disassemblyBuffer); i++){
		if(!WriteMemory((disassemblyBuffer+i), &zero, 1, NULL)){ 
			dprintf("[J] failed to zero memory\n");
			return (0);
		}
	}
#if 0
	dprintf("[J] Opcode sequence for instruction %s:", instruction);

	for(byteCounter=0; ((disassemblyBuffer+byteCounter)<byteEnd); byteCounter++){
		dprintf("%02x ", opcodeBuffer[byteCounter]);
	}

	dprintf("\n");
#endif
	return (byteEnd-disassemblyBuffer);
}

ULONG64 searchMemory(unsigned char * byteBuffer, unsigned long length, ULONG64 *addressHit){
	HRESULT memSearch = S_OK;

	if((memSearch = g_ExtData->SearchVirtual((ULONG64)*addressHit, (ULONG64)-1, byteBuffer, 
		length, 1, addressHit)) != S_OK){
#if 0
			if(memSearch == HRESULT_FROM_NT(STATUS_NO_MORE_ENTRIES)){
				dprintf("[J] byte sequence not found in virtual memory\n");
			}
			else{
				dprintf("[J] byte search failed for another reason\n");
			}
#endif
			return (0);
	}
	return (*addressHit);
}

DWORD	findAllVals(unsigned char *byteBuffer, BYTE size, struct valInstance **instance) {
	ULONG64 addressHit = 0;
	DWORD	addressCount = 0;
	HRESULT memSearch;
	struct	valInstance	*newValInstance;
	
	*instance = NULL;

	while ((memSearch = g_ExtData->SearchVirtual(addressHit+size, (ULONG64)-1, byteBuffer,
        size, 1, &addressHit)) == S_OK) {
		
		if (!*instance) {
			*instance = (struct valInstance *) malloc(sizeof (struct valInstance));
			newValInstance = *instance;
		} else {
			newValInstance->next = (struct valInstance *) malloc(sizeof (struct valInstance));
			newValInstance = newValInstance->next;
		}
		newValInstance->address = addressHit;
		newValInstance->next = NULL;
		addressCount++;
	}

	return (addressCount);
}

BOOL checkExecutability(ULONG64 checkAddress){
	MEMORY_BASIC_INFORMATION protectionInfo;

	if(!VirtualQueryEx(processHandle, (LPVOID)checkAddress, &protectionInfo, sizeof(MEMORY_BASIC_INFORMATION))){
		dprintf("[J] Unable to obtain protection information for address 0x%08x\n", checkAddress);
		return FALSE;
	}

	//dprintf("allocation info: 0x%08x and 0x%08x\n", protectionInfo.AllocationProtect, protectionInfo.Protect);

	if((protectionInfo.Protect & PAGE_EXECUTE_READ) != 0)
		return TRUE;

	//dprintf("[J] 0x%08x isn't executable\n");
	return FALSE;
}

void searchOpcodes(char *instructions) {
	char			**instructionList;
	unsigned char	*byteSequence;
	DWORD			length, i, j, semiCount = 1, offset = 0; 
	ULONG64			ptr = 0;

	// Split instructions into seperate strings at pipes
	length = 0;
	while (instructions[length] != NULL) {
		if (instructions[length] == '|')
			semiCount++; 
		length++;
	}

	// Malloc space for instructionList;
	instructionList = (char **) malloc((semiCount+1) * sizeof (char *));
	if (instructionList == NULL) {
		dprintf("[J] OOM!\n");
		return;
	}
	instructionList[0] = instructions;
	dprintf("[J] Searching for:\n");
	i = 0; j = 0;
	while (i < length) {
		if (instructions[i] == '|') {
			instructions[i] = '\x00';
			dprintf("> %s\n", instructionList[j++]);
			instructionList[j] = &(instructions[i+1]);
		}
		i++;
	}
	dprintf("> %s\n", instructionList[j]);


	// Allocate space for byteSequence
	byteSequence = (unsigned char *) malloc(semiCount * 6);
	if (byteSequence == NULL) {
		dprintf("[J] OOM!\n");
		return;
	}

	// Generate byte sequence and display it
	for (i = 0; i < semiCount; i++) {
		unsigned char	tmpbuf[8];
		offset += getInstructionBytes(instructionList[i], byteSequence+offset);
	}
	
	dprintf("[J] Machine Code:\n> ");
	for (i = 0; i < offset; i++) {
		dprintf("%02x ", byteSequence[i]);
		if (i != 0 && !(i % 16))
			dprintf("\n> ");
	}
	dprintf("\n");

	// Search for sequence in executable memory
	while((ptr = searchMemory(byteSequence, offset, &ptr)) != 0) {
		if (ptr && checkExecutability(ptr)) {
			dprintf("[J] Executable opcode sequence found at: 0x%08x\n", ptr);
		}
		ptr++;
	}
	return;
}

void searchVtptr(DWORD vtOffset, char *instructions) {
    char            **instructionList;
    unsigned char   *byteSequence;
    DWORD           length, i, j, semiCount = 1, offset = 0;
    ULONG64         ptr = 0;

    // Split instructions into seperate strings at pipes
    length = 0;
    while (instructions[length] != NULL) {
        if (instructions[length] == '|')
            semiCount++;
        length++;
    }

    // Malloc space for instructionList;
    instructionList = (char **) malloc((semiCount+1) * sizeof (char *));
    if (instructionList == NULL) {
        dprintf("[J] OOM!\n");
        return;
    }
    instructionList[0] = instructions;
    dprintf("[J] Searching for:\n");
    i = 0; j = 0;
    while (i < length) {
        if (instructions[i] == '|') {
            instructions[i] = '\x00';
            dprintf("> %s\n", instructionList[j++]);
            instructionList[j] = &(instructions[i+1]);
        }
        i++;
    }
    dprintf("> %s\n", instructionList[j]);


    // Allocate space for byteSequence
    byteSequence = (unsigned char *) malloc(semiCount * 6);
    if (byteSequence == NULL) {
        dprintf("[J] OOM!\n");
        return;
    }

    // Generate byte sequence and display it
    for (i = 0; i < semiCount; i++) {
        unsigned char   tmpbuf[8];
        offset += getInstructionBytes(instructionList[i], byteSequence+offset);
    }

    dprintf("[J] Machine Code:\n> ");
    for (i = 0; i < offset; i++) {
        dprintf("%02x ", byteSequence[i]);
        if (i != 0 && !(i % 16))
            dprintf("\n> ");
    }
    dprintf("\n");

    // Search for sequence in executable memory
    while((ptr = searchMemory(byteSequence, offset, &ptr)) != 0) {
        if (ptr && checkExecutability(ptr)) {
            ULONG64 copyPtr = 0, otherPtr = ptr;
            //dprintf("[J] Executable opcode sequence found at: 0x%08x\n", ptr);
            while((copyPtr = searchMemory((unsigned char *)&otherPtr, 4, &copyPtr)) != 0) {
                ULONG64 fptr = 0, vtPtr = copyPtr-vtOffset;
                //dprintf("\tPtr @ 0x%08x\n", copyPtr);
                while((fptr = searchMemory((unsigned char *)&vtPtr, 4, &fptr)) != 0) {
                    //dprintf("\t\tvTable Ptr @ 0x%08x\n", vtPtr);
                    dprintf("0x%08x -> 0x%08x -> 0x%08x -> sequence\n", 
                            (ULONG)fptr, (ULONG)vtPtr, (ULONG)otherPtr);
                    fptr++;
                }
                copyPtr++;
            }
        }
        ptr++;
    }
    return;
}

void returnAddressHuntJutsu(){
	struct trackedBuf *curBuf;
	int i = 0, bufferIndex = 0;
	ULONG offset = 0, bytes = 0;	
	char findBufferExpression[25];
	ULONG64 returnAddress = 0, ptr = 0;
	HRESULT memSearch = S_OK;

	//disassembly variables
	char returnInstruction[30];
	unsigned char opcodeBuffer[30];
	unsigned short instructionLength = 0;
	dprintf("[J] started return address hunt\n");


	for(i; i<6; i++){			//6, because we don't want to waste time on the eip register
		curBuf = trackedBufList;	
		memset(findBufferExpression, 0x00, sizeof(findBufferExpression));

		if(!(bytes = GetExpression(regs[i]))){
			dprintf("[J] skipping %s as register - it is a null pointer\n", regs[i]);
			continue;
		}

		StringCchPrintf(findBufferExpression, sizeof(findBufferExpression), "poi(%s)", regs[i]);
		bytes = GetExpression(findBufferExpression);
	
		//tests if a register points to a location in user controlled data
		while(curBuf != NULL){
			for(bufferIndex=0; bufferIndex < curBuf->bufSize; bufferIndex++){
				if(*(PULONG)((curBuf->bufPatt)+bufferIndex) == bytes){
					memset(opcodeBuffer, 0x00, sizeof(opcodeBuffer));
					memset(returnInstruction, 0x00, sizeof(returnInstruction));

					//find the opcodes for the desired instruction

					//first, for call reg
					StringCchPrintf(returnInstruction, sizeof(returnInstruction), "call %s", regs[i]);
					if(!(instructionLength = getInstructionBytes(returnInstruction, opcodeBuffer)))
						dprintf("[J] getInstructionBytes failed for '%s'\n", returnInstruction);
					if(returnAddress = searchMemory(opcodeBuffer, instructionLength, &ptr)){
						if(checkExecutability(returnAddress))
							dprintf("[J] valid return address (call %s) found at 0x%08x\n", regs[i], returnAddress);
					}
							

					//now, for jmp reg
					memset(returnInstruction, 0x00, sizeof(returnInstruction));
					StringCchPrintf(returnInstruction, sizeof(returnInstruction), "jmp %s", regs[i]);
					if(!(instructionLength = getInstructionBytes(returnInstruction, opcodeBuffer)))
						dprintf("[J] getInstructionBytes failed for '%s'\n", returnInstruction);
					if(returnAddress = searchMemory(opcodeBuffer, instructionLength, &ptr)){
						if(checkExecutability(returnAddress))
							dprintf("[J] valid return address (jmp %s) found at 0x%08x\n", regs[i], returnAddress);
					}
				}
			}
			curBuf = curBuf->next;
		}

		curBuf = trackedBufList;	

		for(offset=0; offset<0x1000; offset+=4){
			memset(findBufferExpression, 0x00, sizeof(findBufferExpression));
			StringCchPrintf(findBufferExpression, sizeof(findBufferExpression), "poi(poi(%s+0x%08x))", regs[i], offset);
			if(!(bytes = GetExpression(findBufferExpression)))
				continue;								//this is basically a replacement for the
													//ddp windbg command, except more automated
			//walk through the buffer to see if any dword in there matches the current 
			//value returned by the expression 
			while(curBuf != NULL){
				for(bufferIndex=0; bufferIndex < curBuf->bufSize; bufferIndex++){
					if(*(PULONG)((curBuf->bufPatt)+bufferIndex) == bytes){
						memset(opcodeBuffer, 0x00, sizeof(opcodeBuffer));
						memset(returnInstruction, 0x00, sizeof(returnInstruction));
						dprintf("[J] %s + 0x%08x points into offset 0x%x of buffer %s\n",
								regs[i], offset, bufferIndex, curBuf->bufName);

						
						//first, build the instruction to find the bytes for
						//for now, we will support jmp [reg+offset] and call [reg+offset]

						//first, for call [reg+offset]
						StringCchPrintf(returnInstruction, sizeof(returnInstruction), "call [%s+%x]", regs[i], offset);
						if(!(instructionLength = getInstructionBytes(returnInstruction, opcodeBuffer)))
							dprintf("[J] getInstructionBytes failed for '%s'\n", returnInstruction);
						if(returnAddress = searchMemory(opcodeBuffer, instructionLength, &ptr)){
							if(checkExecutability(returnAddress))
								dprintf("[J] valid return address (call [%s+%x]) found at 0x%08x\n", regs[i], offset, returnAddress);
						}


						//now, for jmp [reg+offset]
						memset(returnInstruction, 0x00, sizeof(returnInstruction));
						StringCchPrintf(returnInstruction, sizeof(returnInstruction), "jmp [%s+%x]", regs[i], offset);
						if(!(instructionLength = getInstructionBytes(returnInstruction, opcodeBuffer)))
							dprintf("[J] getInstructionBytes failed for '%s'\n", returnInstruction);
						if(returnAddress = searchMemory(opcodeBuffer, instructionLength, &ptr)){
							if(checkExecutability(returnAddress))
								dprintf("[J] valid return address (jmp [%s+%x]) found at 0x%08x\n", regs[i], offset, returnAddress);
						}
					}	
				}
			curBuf = curBuf->next;
			}
		curBuf = trackedBufList;	
		}
	}
}
