#include <iostream>
#include <windows.h>
#include <fstream>
#include <strstream>
#include <list>
extern "C"
{
	#include "meat.h"
	
}

PROCESS_INFORMATION pi;

std::list <std::pair<long,char> > breakpoints;



/*

	Keygen Stuffs....
*/
#define AVERAGE_BIT_S 0x0AC44
HANDLE hEvent[2];
int thread_id;
bool thread_exit;
int current_buffer;

int sleep_time[2];
int sample[2];
int button_to_click[2];
int button_from_magic[2];
int number_good;
int magic;

HWND hButtons[5];
POINT pts[5];
int nbutton;
bool win_inited;
bool done_upx;

char current_d[500];


void click_button(int button_n)
{
	int button=0;
	switch(button_n)
	{
	case 0x532:
		button=4;
		break;
	case 0x533:
		button=3;
		break;
	case 0x534:
		button=2;
		break;
	case 0x535:
		button=1;
		break;
	case 0x536:
		button=0;
		break;

	}
	//SetCursorPos(pts[button].x,pts[button].y);
	int pos=0x00010001;
	Sleep(100);
	SendMessage(hButtons[button],WM_LBUTTONDOWN,MK_LBUTTON,(LPARAM)pos);
	Sleep(60);
	SendMessage(hButtons[button],WM_LBUTTONUP,0,(LPARAM)pos);
}

BOOL CALLBACK mchildwinproc(HWND child,LPARAM param)
{
	/*if ... == "Bmp_Button_Class_meatkraft"
		buttons[i++]=child;*/
	char name[255] ;
	GetClassName(child,name,255);
	if(!strcmp(name,"Bmp_Button_Class_meatkraft"))
	{
		RECT r;
	GetWindowRect(child,&r);
	int number=GetDlgCtrlID(child);
	switch(number)
	{
	case 0x1FB:
		number=0;
		break;
	case 0x1FC:
		number=1;
		break;
	case 0x1FD:
		number=2;
		break;
	case 0x1FE:
		number=3;
		break;
	case 0x1FF:
		number=4;
		break;
	}
	pts[number].x = (r.left + r.right) / 2;
	pts[number].y = (r.top + r.bottom) / 2;

	nbutton++;
	//std::cout << (r.left + r.right) / 2 << "|" << (r.top + r.bottom) / 2 <<   std::endl;
	hButtons[number]=child;
	}
	/*
		Get Position now
	*/
	
	if(nbutton>4)
		return false;
	else
		return true;
}

void init_vars()
{
	HWND hWindow;
	nbutton=0;
	hWindow=FindWindow("Meloquynthe",0);
	if(!hWindow)
	{
		//MessageBox(0,"Didnt found Window!","Error",MB_OK);
		return;
	}
	win_inited=true;

	EnumChildWindows(hWindow,&mchildwinproc,0);
	//std::cout << 	"found " << nbutton << std::endl;
	
}

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	while(1)
	{
		WaitForSingleObject(hEvent[0],INFINITE);
		if(thread_exit)
			ExitThread(0);
		std::cout << " . " ;
		if(!button_to_click[0] || number_good==5)
		{
			continue;
		}
		if(button_to_click[0] != meat_good_button(number_good,magic))
		{
			//std::cout << button_to_click[0] << " != " << meat_good_button(number_good,magic) << std::endl;
			continue;
		}
		//std::cout << "event signaled" << std::endl;
		//std::cout << "RDY TO CLICK " << button_to_click[0] << std::endl;
		if( (sleep_time[0]-1000)>0 )
			Sleep(sleep_time[0]-1000);
		//std::cout << "||||||||||||||||||||||||||||| " << std::endl;
		//std::cout << "vvvvvvvvvvvvvvvvvvvvvvvvvvvvv " << std::endl;
		if( (sleep_time[0]-1000)>0 )
			Sleep(1000);
		else
			Sleep(sleep_time[0]);
		//std::cout << "[0] GOGO NOW!" << std::endl;
		click_button(button_to_click[0]);
	}
}

DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
	while(1)
	{
		WaitForSingleObject(hEvent[1],INFINITE);
		std::cout << " .. " ;
		if(thread_exit)
			ExitThread(0);
		if(!button_to_click[1] || number_good==5)
		{
			continue;
		}
		if(button_to_click[1] != meat_good_button(number_good,magic))
		{
			//std::cout << button_to_click[1] << " != " << meat_good_button(number_good,magic) << std::endl;
			continue;
		}
		//std::cout << "RDY TO CLICK " << button_to_click[1] << std::endl;
		if( (sleep_time[1]-1000)>0 )
			Sleep(sleep_time[1]-1000);

		//std::cout << "||||||||||||||||||||||||||||| " << std::endl;
		//std::cout << "vvvvvvvvvvvvvvvvvvvvvvvvvvvvv " << std::endl;
		if( (sleep_time[1]-1000)>0 )
			Sleep(1000);
		else
			Sleep(sleep_time[1]);
		//std::cout << "[1] GOGO NOW!" << std::endl;
		click_button(button_to_click[1]);
	}
}

int get_sleep_time(long sample)
{
	switch(sample)
	{
	case 0x83C0F1:
		return 0x483b; // -> dbaf
	case 0x5DCB78:
		return 0xb111; // -> 11dbd
	case 0x461DD4:
		return 0xfa99; // -> 1ae85
	case 0x4F559B:
		return 0x8061; // -> 1c3d5
	case 0x5F9746:
		return 0xa05f; // -> 1c79d
	default:
		return 0;
	}
}

void set_breakpoints(long offset)
{
	std::list< std::pair<long,char> >::iterator iter;

	char breakpoint=0xCC;
	for(iter=breakpoints.begin();iter!=breakpoints.end();++iter)
	{
		if((*iter).first != offset)
		{
			//std::cout << std::hex << (*iter).first << " | "<< std::hex << (long)(*iter).second <<std::endl;
			WriteProcessMemory(pi.hProcess,(LPVOID)(*iter).first,&breakpoint,1,0);
		}
	}
}	

void init_breakpoint()
{
	char file[500];
	sprintf(file,"%s/%s",current_d,"\\breakpoints.inc");
	//std::cout << " directory= " << file << std::endl;
	std::ifstream ifs(file);

	long offset_t;

	while(ifs >> std::hex >>  offset_t)
	{
		std::pair<long,char> mpair;
		mpair.first=offset_t;
		ReadProcessMemory(pi.hProcess,(LPCVOID)offset_t,&(mpair.second),1,0);
		breakpoints.push_back(mpair);
	}
	ifs.close();
}
int get_button(long sample)
{
	switch(sample)
	{
	case 0x83C0F1:
		return 0x532;
	case 0x5DCB78:
		return 0x533;
	case 0x461DD4:
		return 0x534;
	case 0x4F559B:
		return 0x535;
	case 0x5F9746:
		return 0x536;
	default:
		return 0;
	}

}
void do_action(DEBUG_EVENT* Event,HANDLE *hThread)
{
	int btn=0;
	set_breakpoints((long)Event->u.Exception.ExceptionRecord.ExceptionAddress);
	CONTEXT context;
	switch((long)Event->u.Exception.ExceptionRecord.ExceptionAddress)
	{
		//case  0x08C4636:
		case 0x07fe167:
			context.ContextFlags = CONTEXT_FULL;
			GetThreadContext(*hThread , &context);
			number_good=0;
			magic=context.Ebp;
			//std::cout << "MAGIC= -> " << std::hex << context.Ebp << "->" << std::hex << meat_good_button(number_good,magic)  << std::endl;
			//std::cout << "NEXT_BUTTON = " << std::hex << meat_good_button(number_good,magic)  << std::endl;
			std::cout << "step_0 " ;
			break;
		//case 0x08C3FF8: // Set SAMPLE to 0
		case 0x047e984:
			//std::cout << "Sample = 0" << std::endl;
			button_to_click[current_buffer]=0;
			sample[current_buffer]=0;
			break;
		//case 0x08C401D: //Set Sample to eax
		case 0x0781b40:
			context.ContextFlags = CONTEXT_FULL;
			GetThreadContext(*hThread , &context);
			//std::cout << "Sample = " << std::hex << context.Eax << " -> " << get_button(context.Eax) << "sleep time ->" << get_sleep_time(context.Eax) <<  std::endl;
			//std::cout << context.Eax << std::endl;
			btn=get_button(context.Eax);
			//std::cout <<std::hex << magic << "\t\t\t\t\t\t\t" <<std::hex << btn  << std::endl;
			sleep_time[current_buffer]=(int)((float)(get_sleep_time(context.Eax))/AVERAGE_BIT_S*1000);
			button_to_click[current_buffer]=btn;
			sample[current_buffer]=context.Eax;
			break;
		case 0x08C3BBB:
			//context.ContextFlags = CONTEXT_FULL;
			//GetThreadContext(*hThread , &context);
			//std::cout << "Clicked time = " << std::hex << context.Edx <<  std::endl;
			break;
		case 0x008C3BEE:
			//std::cout << "Failed step[1] nothing set..." << std::endl;
			break;
		case 0x08C3C4B:
			//std::cout << "Failed step[2] time failed..." << std::endl;
			break;
		case 0x08C3C8C:
			//std::cout << "Failed step[3] wrong button(1)" << std::endl;
			break;
		case 0x08C3CC1:
			//std::cout << "Failed step[4] al=2..." << std::endl;
			break;
		case 0x08C3CF9:
			//std::cout << "Failed step[5] ecx>5..." << std::endl;
			break;
		case 0x08C3D0E:
			//context.ContextFlags = CONTEXT_FULL;
			//GetThreadContext(*hThread , &context);
			//std::cout << "Failed step[6] wrong button(2) attended " << context.Ecx << "!= "<< context.Ebp << std::endl;
			break;
		//case 0x08C3D2B:
		case 0x07c2a77:
			//std::cout << "Failed step[7] OR DWORD" << std::endl;
			ReadProcessMemory(pi.hProcess,(LPCVOID)0x07FDF57,&magic,4,0);
			number_good++;
			//std::cout << "MAGIC= " << std::hex << magic << " -> " << std::hex << meat_good_button(number_good,magic) << std::endl;
			//std::cout << "NEXT_BUTTON = " << std::hex << meat_good_button(number_good,magic)  << std::endl;
			std::cout << "step_" << number_good <<" ";
			break;
		case 0x08C3CC8:
			//context.ContextFlags = CONTEXT_FULL;
			//GetThreadContext(*hThread , &context);
			//std::cout << "ebx = " << std::hex << context.Ebx << " esi = " << std::hex << context.Esi << std::endl;
			break;
		
		//case 0x08C3EB6:
		case 0x077affb:
			// Flip buffer...
			if(current_buffer)
				current_buffer=0;
			else
				current_buffer=1;
			//std::cout << "set time for buffer " << current_buffer << std::endl;
			SetEvent(hEvent[current_buffer]);
			break;
		case 0x08C4293:

			break;
	}
}

void remove_breakpoint(DEBUG_EVENT *DebugEvt)
{
	if(DebugEvt->u.Exception.ExceptionRecord.ExceptionAddress == (PVOID) 0x08C4293)
	{
		/*
			End UPX decryption
		*/
		char jmp=0xE9;
		WriteProcessMemory(pi.hProcess,(LPVOID)0x08C4293,&jmp,1,0);
		
		CONTEXT context;
		context.ContextFlags = CONTEXT_FULL;
			
		HANDLE hThread = OpenThread(READ_CONTROL | SYNCHRONIZE  | THREAD_ALL_ACCESS,0,DebugEvt->dwThreadId);
		if( hThread && GetThreadContext(hThread , &context))
		{
			// The EIP in the context structure points past the breakpoint, so
			// decrement EIP to point at the original instruction
			context.Eip = context.Eip-1;
			
			// set the new context
			SetThreadContext( hThread, &context);
			do_action(DebugEvt,&hThread);
		}
		CloseHandle(hThread);


		init_breakpoint();
		set_breakpoints(0);
		return;
	}
	std::list< std::pair<long,char> >::iterator iter;
	for(iter=breakpoints.begin();iter!=breakpoints.end();++iter)
	{
		if((*iter).first == (long) DebugEvt->u.Exception.ExceptionRecord.ExceptionAddress)
		{
			if(!WriteProcessMemory(pi.hProcess,(LPVOID)(*iter).first,&((*iter).second),1,0))
				std::cout << "WriteProcessMemory Error" << std::endl;
			//else
			//	std::cout << "Removing breakpoint at " << std::hex << (*iter).first << " with " << std::hex << (int)((char)((*iter).second))<< std::endl;

			CONTEXT context;
			context.ContextFlags = CONTEXT_FULL;
			
			HANDLE hThread = OpenThread(READ_CONTROL | SYNCHRONIZE  | THREAD_ALL_ACCESS,0,DebugEvt->dwThreadId);
			if( hThread && GetThreadContext(hThread , &context))
			{
				// The EIP in the context structure points past the breakpoint, so
				// decrement EIP to point at the original instruction
				context.Eip = context.Eip-1;

				
				// set the new context
				SetThreadContext( hThread, &context);
				do_action(DebugEvt,&hThread);

			}
			CloseHandle(hThread);
		}
	}
}

void push_spec()
	{
		STARTUPINFO si;
		OPENFILENAME ofn;
		char filename[255];
		number_good=0;
		win_inited=false;
		done_upx=false;

		DEBUG_EVENT DebugEv;                   // debugging event information 
		DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation 

		hEvent[0]=CreateEvent(0,0,0,0);
		hEvent[1]=CreateEvent(0,0,0,0);
		current_buffer=false;
		thread_exit=false;
		CreateThread(0,0,ThreadProc,0,0,(LPDWORD)&thread_id);
		CreateThread(0,0,ThreadProc2,0,0,(LPDWORD)&thread_id);

		ZeroMemory( &si, sizeof(si) );
		si.cb = sizeof(si);
		ZeroMemory( &pi, sizeof(pi) );
		ZeroMemory(&ofn, sizeof(ofn));
		breakpoints.clear();

		GetStartupInfo(&si);
		current_d[0]='\0';
		GetCurrentDirectory(500,current_d);

		ofn.lpstrFile = filename;
		ofn.lpstrFile[0] = '\0';
		ofn.nFilterIndex = 0;
		ofn.lStructSize = sizeof(ofn);
		ofn.lpstrFilter = "Meloquynthe\0Meloquynthe.exe\0\0";

		
		ofn.nMaxFile = 255;
		ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_LONGNAMES | OFN_EXPLORER | OFN_HIDEREADONLY;
		//ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

		GetOpenFileName(&ofn);
		// Start the child process. 
		if( !CreateProcess( NULL/*"Meloq.p.exe"*/,   // No module name (use command line). 
			filename, // Command line. 
			NULL,             // Process handle not inheritable. 
			NULL,             // Thread handle not inheritable. 
			FALSE,            // Set handle inheritance to FALSE. 
			DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS | PROCESS_VM_WRITE | PROCESS_VM_OPERATION,                // No creation flags. 
			NULL,             // Use parent's environment block. 
			NULL,             // Use parent's starting directory. 
			&si,              // Pointer to STARTUPINFO structure.
			&pi )             // Pointer to PROCESS_INFORMATION structure.
		) 
		{
			printf( "CreateProcess failed (%d).\n", GetLastError() );
			return;
		}

		
    	 bool loop=true;
		 char breakp;
		while(loop)
		{ 
		// Wait for a debugging event to occur. The second parameter indicates
		// that the function does not return until a debugging event occurs. 
		 
			WaitForDebugEvent(&DebugEv, INFINITE); 
			dwContinueStatus = DBG_CONTINUE;
		 
		// Process the debugging event code. 
		 
			switch (DebugEv.dwDebugEventCode) 
			{ 
				case EXCEPTION_DEBUG_EVENT: 
				// Process the exception code. When handling 
				// exceptions, remember to set the continuation 
				// status parameter (dwContinueStatus). This value 
				// is used by the ContinueDebugEvent function. 
		 
					switch(DebugEv.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. 
							remove_breakpoint(&DebugEv);
							if(!win_inited)
								init_vars();
							//DBG_EXCEPTION_HANDLED
							//dwContinueStatus = DBG_EXCEPTION_HANDLED;
							//std::cout << "Breakpoint met at " << std::hex << DebugEv.u.Exception.ExceptionRecord.ExceptionAddress << std::endl;
							break;
		 
						case EXCEPTION_DATATYPE_MISALIGNMENT: 
						// First chance: Pass this on to the system. 
						// Last chance: Display an appropriate error. 
							break;
		 
						case EXCEPTION_SINGLE_STEP: 
						// 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.
					/*
						Set upx end breakpoint
					*/
					breakp=0xCC;
					WriteProcessMemory(pi.hProcess,(LPVOID)0x08C4293,&breakp,1,0);
					break;
		 
				case EXIT_THREAD_DEBUG_EVENT: 
				// Display the thread's exit code. 
					thread_exit=true;
					break;
		 
				case EXIT_PROCESS_DEBUG_EVENT: 
				// Display the process's exit code. 
					loop=false;

					break;
		 
				case LOAD_DLL_DEBUG_EVENT: 
				// 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: 
				// Display a message that the DLL has been unloaded. 
					break;
		 
				case OUTPUT_DEBUG_STRING_EVENT: 
				// Display the output debugging string. 
					break;
		 
			} 
		 
		// Resume executing the thread that reported the debugging event. 
		 
		ContinueDebugEvent(DebugEv.dwProcessId, 
						DebugEv.dwThreadId, 
						dwContinueStatus);
		}

		// Close process and thread handles. 
		CloseHandle( pi.hProcess );
		CloseHandle( pi.hThread );

	}