//////////////////////////////////////////////////////////////////////////
//		Stripped down version of Flapadar and ScriptEnforcer bypass		//
//								By Elenion (C) 2010						//
//		Please buy the full version at: no just kidding. :P				//
//////////////////////////////////////////////////////////////////////////

#include "stdafx.h"

CDetour det;
FILE * fout = NULL;

DWORD dwIsEnforcerEnabled = NULL;
DWORD dwLuaExecute		  = NULL;

typedef int		(__cdecl * Msg_t)(const char * Format, ...);
Msg_t cMsg;

typedef int		(__cdecl * IsScriptEnforcerEnabled_t)();
IsScriptEnforcerEnabled_t oIsScriptEnforcerEnabled;
int __cdecl hIsScriptEnforcerEnabled()
{
	return 0;
}

typedef void		(__stdcall * LuaExecute_t)( const char * szOriginalFileName, int UnkParm3, const char * szToExec, int UnkParm5, int UnkParm6 );
LuaExecute_t oLuaExecute;
void __stdcall hLuaExecute( const char * szOriginalFileName, int UnkParm3, const char * szToExec, int UnkParm5, int UnkParm6 )
{
	void * pThis = NULL;
	__asm mov pThis, ecx

	if( fout == NULL )
		fout = fopen("lua-exec.txt", "w");
	
	fprintf( fout, "szOriginalFileName: %s\n", szOriginalFileName );
	fprintf( fout, "szToExec: %s\n", szToExec );

	// TODO: - Implement a decent filtering mechanism
	//		 - implement CRC checks and more indepth control
	if( strstr(szToExec, "local vartbl = {--[\"sv_scriptenforcer\"] = 1,") )
		return;

	__asm
	{
		push UnkParm6
		push UnkParm5
		push szToExec
		push UnkParm3
		push szOriginalFileName
		mov ecx, pThis
		call oLuaExecute
	}
}

DWORD RunActionChecker(LPVOID pArgs)
{
	oIsScriptEnforcerEnabled = NULL;

	while (1)
	{
		// Enable
		if( GetAsyncKeyState(VK_F11) & 0x1 )
		{
			if( oIsScriptEnforcerEnabled == NULL )
			{
				oIsScriptEnforcerEnabled = (IsScriptEnforcerEnabled_t)det.Create(
					(PBYTE)dwIsEnforcerEnabled,
					(PBYTE)&hIsScriptEnforcerEnabled,
					DETOUR_TYPE_JMP
					);
				cMsg("Disabled ScriptEnforcer protection.\n");
			}
			else
			{
				det.Remove( (PBYTE)dwIsEnforcerEnabled, (PBYTE)oIsScriptEnforcerEnabled, DETOUR_TYPE_JMP );
				cMsg("Enabled ScriptEnforcer protection.\n");
				oIsScriptEnforcerEnabled = NULL;
			}
		}
	}
	return 0;
}

DWORD WINAPI DllMain(HINSTANCE hModule, DWORD _Reason, LPVOID _Reserved)
{
	if( _Reason == DLL_PROCESS_ATTACH )
	{
		DisableThreadLibraryCalls(hModule);

		HMODULE hMod = GetModuleHandleA("client.dll");
		while ( hMod == NULL)
		{
			Sleep(500);
			hMod = GetModuleHandleA("client.dll");
		}

		DWORD dwMsg = (DWORD)GetProcAddress( GetModuleHandleA( "tier0.dll" ), "Msg" );
		if( dwMsg != NULL )
		{
			cMsg = (Msg_t)dwMsg;
			cMsg("Successfully launched and hooked GMOD.\n");
			cMsg("This module will automaticly unload when you close the game.\n");
		}

		cMsg("Detecting ScriptEnforcer's Constructor.. ");
		DWORD dwScriptEnforcerConstructor = reinterpret_cast<DWORD>( hMod ) + 0x1F5190;
		if( dwScriptEnforcerConstructor == NULL )
		{
			cMsg("Could not find address.\n");
			return 0;
		}
		cMsg("ScriptEnforcer::ScriptEnforcer: 0x%08X\n", dwScriptEnforcerConstructor);
		cMsg("Finding vtable.. ");
		DWORD dwScriptEnforcerVtable = GET_INT( dwScriptEnforcerConstructor + 116 );
		cMsg("ScriptEnforcer__vftable: 0x%08X\n", dwScriptEnforcerVtable);

		// Hook vtable function
		cMsg("Calculating relative offset of ScriptEnforcer::IsEnabled in vtable.. ");
		dwIsEnforcerEnabled = GET_INT( &((DWORD*)dwScriptEnforcerVtable)[16] );
		cMsg("0x%08X\n", dwIsEnforcerEnabled);

		hMod = GetModuleHandleA( "lua_shared.dll" );

		DWORD dwLuaExecute = reinterpret_cast<DWORD>( hMod ) + 0x2630;
		cMsg("Lua::Execute: 0x%08X\n", dwLuaExecute);

		oLuaExecute = (LuaExecute_t)det.Create( (PBYTE)dwLuaExecute, (PBYTE)hLuaExecute, DETOUR_TYPE_JMP );

		cMsg("\nPress F11 to enable/disable ScriptEnforcer.\n");

		CreateThread( NULL, NULL, (LPTHREAD_START_ROUTINE)RunActionChecker, NULL, NULL, NULL );

		return true;
	}
	
	if(_Reason == DLL_PROCESS_DETACH)
	{
		FreeLibrary(hModule);
	}
}