#include "pcsinjector.h"

void WriteConsole(char *str)
{
	HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
	DWORD dwBytesWriteen = 0;
	WriteFile(hstdout, str, lstrlenA(str), &dwBytesWriteen, NULL);
}

int CALLBACK WinMain(HINSTANCE hInst,
	HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
	PROCESSENTRY32 me;
	DWORD dwSelIdx = 0, dwMaxIdx = 0, dwProcId = 0, dwBytesWritten = 0;
	COORD pos; pos.X = 0; pos.Y = 0;
	char szSelExeName[260];

	AllocConsole();

	lua_State *l = 0;
	l = lua_open();
	luaL_openlibs(l);
	LUA_REG(l, WindowExists);

	char *RootPath = (char*)GlobalAlloc(GMEM_ZEROINIT, MAX_PATH);
	ExpandEnvironmentStringsA(BASEPATH, RootPath, MAX_PATH);
	char *OldPath = (char*)GlobalAlloc(GMEM_ZEROINIT, MAX_PATH);
	GetCurrentDirectoryA(MAX_PATH, OldPath);
	SetCurrentDirectoryA(RootPath); //comment out to load from current dir
	GlobalFree(RootPath);
	int err = luaL_dofile(l, "__injector__.lua");
	if (err == 1)
	{
		WriteConsole("Failed to open __injector__.lua, using defaults\n");
		MessageBoxA(0, lua_tostring(l, -1), "PCS", MB_OK);
		Sleep(500);
	}
	SetCurrentDirectoryA(OldPath);
	GlobalFree(OldPath);

	lua_getglobal(l, "InjectOnStart");
	if (lua_isstring(l, -1))
	{
		char *exename = (char*)lua_tostring(l, -1);
		WriteConsole("Waiting for \"");
		WriteConsole(exename);
		WriteConsole("\" to start...\n");

		DWORD dwFoundProcId = 0;
		while (dwFoundProcId == 0)
		{
			HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
			if (hSnapshot == INVALID_HANDLE_VALUE)
			{
				WriteConsole("Failed to create snapshot\n");
				Sleep(500);
				FreeConsole();
				return 1;
			}
			else
			{
				me.dwSize = sizeof(PROCESSENTRY32);
				if (!Process32First(hSnapshot, &me))
				{
					WriteConsole("Failed to get first proccess\n");
					Sleep(500);
					CloseHandle(hSnapshot);
				}
				else
				{
					do
					{
						if (strncmp(me.szExeFile, exename, strlen(exename)) == 0)
						{
							dwFoundProcId = me.th32ProcessID;
							break;
						}
					}
					while (Process32Next(hSnapshot, &me));
				}
			}
		}

		dwProcId = dwFoundProcId;
		lua_pop(l, 1);
	}
	else
	{
		HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
		if (hSnapshot == INVALID_HANDLE_VALUE)
		{
			WriteConsole("Failed to create snapshot\n");
			Sleep(500);
			FreeConsole();
			return 1;
		}
		else
		{
			me.dwSize = sizeof(PROCESSENTRY32);
			if (!Process32First(hSnapshot, &me))
			{
				WriteConsole("Failed to get first proccess\n");
				Sleep(500);
				CloseHandle(hSnapshot);
			}
			else
			{
				pos.X = 0; pos.Y = 3;
				do
				{
					DWORD dwMaxNameLen = 8;
					lua_getglobal(l, "MaxExeNameLen");
					if (lua_isnumber(l, -1))
					{
						dwMaxNameLen = lua_tointeger(l, -1);
						lua_pop(l, 1);
					}
					if (lstrlenA(me.szExeFile) <= dwMaxNameLen)
					{
						pos.Y++;
					}
				}
				while (Process32Next(hSnapshot, &me));
				dwMaxIdx = pos.Y - 3 - 1;
			}
		}

		/*wait for user to select process*/
		HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
		while (!GetAsyncKeyState(VK_SPACE))
		{
			Sleep(150);
			//clear screen
			pos.X = 0; pos.Y = 0;
			SetConsoleCursorPosition(hstdout, pos);
			CONSOLE_SCREEN_BUFFER_INFO sbi;
			GetConsoleScreenBufferInfo(hstdout, &sbi);
			FillConsoleOutputCharacter(hstdout, ' ', sbi.dwSize.X * sbi.dwSize.Y, pos, &dwBytesWritten);
			//draw process selection screen
			WriteConsole("Select Process (only processes with exe names less than 8 chars are listed)\n");
			WriteConsole("*** Press ");
			SetConsoleTextAttribute(hstdout, BACKGROUND_GREEN);
			WriteConsole("SPACE");
			SetConsoleTextAttribute(hstdout, sbi.wAttributes);
			WriteConsole(" to inject into process ***\n");
			WriteConsole("==[ exe name ]=================================================================\n");
			if (hSnapshot != INVALID_HANDLE_VALUE)
			{
				me.dwSize = sizeof(PROCESSENTRY32);
				if (!Process32First(hSnapshot, &me))
				{
					WriteConsole("Failed to get first proccess\n");
					Sleep(500);
					CloseHandle(hSnapshot);
				}
				else
				{
					pos.X = 2; pos.Y = 3;
					do
					{
						if (lstrlenA(me.szExeFile) <= 8)
						{
							SetConsoleCursorPosition(hstdout, pos);
							if ((pos.Y - 3) == dwSelIdx) {
								SetConsoleTextAttribute(hstdout, BACKGROUND_GREEN);
								dwProcId = me.th32ProcessID;
								memset(szSelExeName, 0, 260);
								memcpy(szSelExeName, me.szExeFile, 260);
							}
							WriteConsole(me.szExeFile);
							if ((pos.Y - 3) == dwSelIdx) {
								SetConsoleTextAttribute(hstdout, sbi.wAttributes);
							}
							pos.Y++;
						}
					}
					while (Process32Next(hSnapshot, &me));
				}
			}
			//key detection
			while ((!GetAsyncKeyState(VK_UP)) && (!GetAsyncKeyState(VK_DOWN)) && (!GetAsyncKeyState(VK_SPACE)));
			if ((dwSelIdx >= 1) && (GetAsyncKeyState(VK_UP))) dwSelIdx--;
			if ((dwSelIdx < dwMaxIdx) && (GetAsyncKeyState(VK_DOWN))) dwSelIdx++;
		}
		CloseHandle(hSnapshot);
		/*wait for user to select process*/
	}

	HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, 0, dwProcId);
	if (hProc == 0)
	{
		WriteConsole("    Failed to open process...");
		Sleep(1000);
		return 1;
	}

	char *szModule = (char*)GlobalAlloc(GMEM_ZEROINIT, MAX_PATH);
	
	lua_getglobal(l, "GetDll");
	if (lua_isfunction(l, -1))
	{
		lua_pushstring(l, szSelExeName);
		lua_pcall(l, 1, 1, 0);
		char *dllname = (char*)lua_tostring(l, -1);
		ExpandEnvironmentStringsA(dllname, szModule, MAX_PATH);
		lua_pop(l, -1);
	}
	else
	{
		WriteConsole("    Failed to call GetDll, using default PCS.dll");
		Sleep(500);
		ExpandEnvironmentStringsA(BASEPATH"\\PCS.dll", szModule, MAX_PATH);
	}

	//copy lua5.1.dll to exe path
	DWORD dwTempMax = MAX_PATH;
	char *szTemp = (char*)GlobalAlloc(GMEM_ZEROINIT, dwTempMax);
	QueryFullProcessImageName(hProc, 0, szTemp, &dwTempMax);
	char *szTempPtr = szTemp + lstrlenA(szTemp) - 1;
	while (*szTempPtr != '\\') szTempPtr--;
	*(szTempPtr+1) = 0; //stip exe name
	lstrcatA(szTemp, "lua5.1.dll");
	char *szLua51 = (char*)GlobalAlloc(GMEM_ZEROINIT, MAX_PATH);
	ExpandEnvironmentStringsA(BASEPATH"\\lua5.1.dll", szLua51, MAX_PATH);
	WriteConsole("    Copying lua5.1.dll...");
	CopyFileA(szLua51, szTemp, FALSE);
	GlobalFree(szLua51);
	GlobalFree(szTemp);
	
	WriteConsole("    Injecting...");

	//Write module name to process
	void *rModule = VirtualAllocEx(hProc, 0, MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
	if (rModule == 0)
	{
		WriteConsole("    Failed to allocate memory...");
		Sleep(1000);
		return 1;
	}
	WriteProcessMemory(hProc, rModule, (void*)szModule, MAX_PATH, 0);

	//Get module handle in process
	HANDLE rModThread = CreateRemoteThread(hProc, 0, 0, (LPTHREAD_START_ROUTINE)
		GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA"), rModule, 0, 0);
	DWORD rModResult = WaitForSingleObject(rModThread, INFINITE);

	//Retrieve remote module address
	DWORD rModHandle = 0;
	GetExitCodeThread(rModThread, (DWORD*)&rModHandle);
	if (rModHandle == 0)
	{
		WriteConsole("    Possible injection failure.");
		Sleep(1000);
	}

	//Free module name in process
	VirtualFreeEx(hProc, (void*)rModule, MAX_PATH, MEM_RELEASE);

	lua_close(l);
	CloseHandle(hProc);
	return 0;
}

LUA_FUNC(WindowExists)
{
	if (lua_gettop(l) != 1) return 0;
	char *title = (char*)lua_tostring(l, 1);
	HWND hwnd = FindWindowA(0, title);
	if (hwnd == 0) lua_pushboolean(l, 0);
	else lua_pushboolean(l, 1);
	return 1;
}
