#include <iostream>
#include <windows.h>


extern "C" {
	#include "libdis.h"
}
static struct DIS_INFO {
	/* file streams */
	FILE *in, *out, *err;
	/* size of input file */
	unsigned long size;
	/* flag for --entry option */
	int entry;
	/* pagesize for buffering STDIN */
	unsigned long pagesize;
	/* output syntax */
	enum x86_asm_format syntax;
} info = { NULL, NULL, NULL, 0, 0, 1, raw_syntax };

void x86dis_manual_print( x86_insn_t *insn, void *arg ) {
	char line[4096];
	int i;


	if ( x86_format_insn(insn, line, 4096, info.syntax) <= 0 ) {
		return;
	}

	if ( info.syntax == att_syntax || info.syntax == intel_syntax ) {
		/* print an address and hex bytes, since libdisasm doesn't */
		printf("%08lX", insn->addr );
		for ( i = 0; i < 10; i++ ) {
			if ( i < insn->size ) {
				printf(" %02X", insn->bytes[i]);
			} else {
				printf("   ");
			}
		}
		printf("\t");
	}

	printf("%s\n", line);
}

struct RVALIST {
	unsigned long rva;
	struct RVALIST *next;
} rva_list_head = {0};

int rva_list_add( unsigned long rva ) {
	struct RVALIST *rl, *rl_new;

	for ( rl = &rva_list_head; rl; rl = rl->next ) {
		/* first rva is always 0 -- the list head */
		if ( rva > rl->rva ) {
			if ( ! rl->next || rva < rl->next->rva ) {
				/* we use exit() to free this, btw */
				rl_new = (RVALIST*)calloc(sizeof(struct RVALIST), 1);
				rl_new->rva = rva;
				rl_new->next = rl->next;
				rl->next = rl_new;
				return(1);
			}
		} else if ( rva == rl->rva ) {
			return(0);	/* already seen this rva */
		}
	}
	return(0);
}

long x86dis_resolver( x86_op_t *op, x86_insn_t *insn ) {
	long retval = -1;

	if (! rva_list_add(insn->addr) ) {
		/* we have seen this one already; return -1 */
		return(-1);
	}

	/* this part is a flat ripoff of internal_resolver in libdis.c */
	/* we don't do any register or stack resolving */
	if ( op->type == op_absolute || op->type == op_offset ) {
		retval = op->data.sdword; /* no need to cast the void* */
	} else if (op->type == op_relative_near ){
		retval = insn->addr + insn->size + op->data.sbyte;
	} else if (op->type == op_relative_far ){
		if ( op->datatype == op_word ) {
			retval = insn->addr + insn->size + op->data.sword;
		} else if ( op->datatype == op_dword ) {
			retval = insn->addr + insn->size + op->data.sdword;
		}
	}

	return( retval );
}


int main(int argc,char **argv)
/*int WinMain(      

    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
)*/
{

#define BUF_SIZE  7
#define LINE_SIZE 80

   //char buf[BUF_SIZE];      /* buffer of bytes to disassemble */
   char buf[BUF_SIZE];
   char buff[500];
   char line[LINE_SIZE];    /* buffer of line to print */
   int pos = 0;             /* current position in buffer */
   int size;                /* size of instruction */
   x86_insn_t insn;         /* instruction */

   x86_init(opt_none, NULL);
   buf[0]=0x6A;
   buf[1]=0x00;
   buf[2]=0xE9;
   buf[3]=0x30;
   buf[4]=0x8E;
   buf[5]=0xC0;
   buf[6]=0xFF;

   
   while ( pos < BUF_SIZE ) {
      /* disassemble address */
      size = x86_disasm((unsigned char *) buf, BUF_SIZE, 0, pos, &insn);
      if ( size ) {
         /* print instruction */
         x86_format_insn(&insn, line, LINE_SIZE, intel_syntax);
         printf("%s - size=%d\n", line,size);
         pos += size;
      } else {
         printf("Invalid instruction\n");
         pos++;
      }
   }
   PROCESS_INFORMATION lpi;
   STARTUPINFO lps;

   GetStartupInfo(&lps);
   int status=CreateProcess("Meloquynthe.exe",0,0,0,0,DEBUG_PROCESS + DEBUG_ONLY_THIS_PROCESS + CREATE_SUSPENDED,
	   0,0,&lps,&lpi);
   printf("CreateProcess -> %X\n",status);

   printf("lpi %X %X\n",lpi.hProcess,lpi.hThread);

   DEBUG_EVENT evt;
   DWORD dwContinueStatus = DBG_CONTINUE;
   MessageBox(0,"Start when you wanna!","Waiting",MB_OK);
   CONTEXT thread_context;


   ResumeThread(lpi.hThread);


   bool loop_condition=true;
   SIZE_T bytes_read=0;
	while(loop_condition)
	{
		dwContinueStatus = DBG_CONTINUE;
		
		WaitForDebugEvent(&evt,INFINITE);
		if(evt.dwDebugEventCode==0) //Dunno what that means!
			continue;
		//	MessageBox(0,"ERREUR code=0!","...",MB_OK);
		printf("Event : %X\n",evt.dwDebugEventCode);
		
		
		// Process the exception code. When handling 
		// exceptions, remember to set the continuation 
		// status parameter (dwContinueStatus). This value 
		// is used by the ContinueDebugEvent function. 
		switch (evt.dwDebugEventCode) 
			{ 
				case EXCEPTION_DEBUG_EVENT: 
					printf("EXCEPTION_DEBUG_EVENT\n");
					//dwContinueStatus=DBG_EXCEPTION_NOT_HANDLED;
					dwContinueStatus=DBG_EXCEPTION_HANDLED;
					printf("address %X\n",evt.u.Exception.ExceptionRecord.ExceptionAddress);
				// Process the exception code. When handling 
				// exceptions, remember to set the continuation 
				// status parameter (dwContinueStatus). This value 
				// is used by the ContinueDebugEvent function. 
				switch(evt.u.Exception.ExceptionRecord.ExceptionCode)
				{ 
					case EXCEPTION_ACCESS_VIOLATION: 
					// First chance: Pass this on to the system. 
					// Last chance: Display an appropriate error. 
						break;

					case EXCEPTION_BREAKPOINT: 
					// First chance: Display the current 
					// instruction and register values. 
						break;

					case EXCEPTION_DATATYPE_MISALIGNMENT: 
					// First chance: Pass this on to the system. 
					// Last chance: Display an appropriate error. 
						break;

					case EXCEPTION_SINGLE_STEP: 
						MessageBox(0,"Continue when you wanna!","Single Step",MB_OK);
						thread_context.ContextFlags=CONTEXT_CONTROL; //Has to set ContextFlags first!
						GetThreadContext(lpi.hThread,&thread_context);
						thread_context.EFlags |= 0x100; //Set Trap Flag
						SetThreadContext(lpi.hThread,&thread_context);
						dwContinueStatus=DBG_CONTINUE;
					// First chance: Update the display of the 
					// current instruction and register values. 
						break;

					case DBG_CONTROL_C: 
					// First chance: Pass this on to the system. 
					// Last chance: Display an appropriate error. 
						break;

					default:
					// Handle other exceptions. 
						break;
				} 

				case CREATE_THREAD_DEBUG_EVENT: 
				// As needed, examine or change the thread's registers 
				// with the GetThreadContext and SetThreadContext functions; 
				// and suspend and resume thread execution with the 
				// SuspendThread and ResumeThread functions. 
				break;

				case CREATE_PROCESS_DEBUG_EVENT: 
				// As needed, examine or change the registers of the
				// process's initial thread with the GetThreadContext and
				// SetThreadContext functions; read from and write to the
				// process's virtual memory with the ReadProcessMemory and
				// WriteProcessMemory functions; and suspend and resume
				// thread execution with the SuspendThread and ResumeThread
				// functions. Be sure to close the handle to the process image
				// file with CloseHandle.
					printf("CREATE_PROCESS_DEBUG_EVENT %X\n",evt.u.CreateProcessInfo.lpStartAddress);
					
					
					ReadProcessMemory(lpi.hProcess,evt.u.CreateProcessInfo.lpStartAddress,buff,500,&bytes_read);
					x86_disasm_forward( (unsigned char *) buff, 500, 
						(unsigned long) evt.u.CreateProcessInfo.lpStartAddress, 0, 
						x86dis_manual_print, NULL,
						x86dis_resolver );
					MessageBox(0,"Disasm END!","Single Step",MB_OK);
					
					//thread_context.ContextFlags=CONTEXT_CONTROL; //Has to set ContextFlags first!
					//GetThreadContext(lpi.hThread,&thread_context);
					//thread_context.EFlags |= 0x100; //Set Trap Flag
					//SetThreadContext(lpi.hThread,&thread_context);

				break;

				case EXIT_THREAD_DEBUG_EVENT: 
				// Display the thread's exit code. 
					printf("EXIT_THREAD_DEBUG_EVENT\n");
				break;

				case EXIT_PROCESS_DEBUG_EVENT: 
					printf("EXIT_PROCESS_DEBUG_EVENT\n");
					loop_condition=false;
				// Display the process's exit code. 
				break;

				case LOAD_DLL_DEBUG_EVENT: 
					//printf("LOAD_DLL_DEBUG_EVENT\n");
				// Read the debugging information included in the newly 
				// loaded DLL. Be sure to close the handle to the loaded DLL 
				// with CloseHandle.
				break;

				case UNLOAD_DLL_DEBUG_EVENT: 
					printf("UNLOAD_DLL_DEBUG_EVENT\n");
				// Display a message that the DLL has been unloaded. 
				break;

				case OUTPUT_DEBUG_STRING_EVENT: 
					printf("OUTPUT_DEBUG_STRING_EVENT\n");
				// Display the output debugging string. 
				break;

				} 
	ContinueDebugEvent(evt.dwProcessId, 
                   evt.dwThreadId, 
                   dwContinueStatus);
	}

   x86_cleanup();
}