// Inject.cpp : Implementation of DLL Exports. // Note: Proxy/Stub Information // To build a separate proxy/stub DLL, // run nmake -f Injectps.mk in the project directory. #include "stdafx.h" #include "resource.h" #include #include "Inject.h" #include "InjectApi.h" #include "Inject_i.c" #include "DirectDrawHook.h" #include "Manager.h" #include "Button.h" #include "Pager.h" #include "InjectService.h" #include "PluginAdapterV1.h" #include "RootLayer.h" // one instance for all processes #pragma data_seg( ".InjectDll" ) HHOOK g_hHook = NULL; #pragma data_seg() CComModule _Module; LPFNDLLFUNCALPHABLEND AlphaBlendF; BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_Button, cButton) OBJECT_ENTRY(CLSID_Pager, cPager) OBJECT_ENTRY(CLSID_InjectService, cInjectService) OBJECT_ENTRY(CLSID_PluginAdapterV1, cPluginAdapterV1) END_OBJECT_MAP() // These functions and structures are used for hooking functions struct cHookDescriptor; bool hookFunctions( cHookDescriptor *pHook, DWORD nCount ); // Functions for hooking from ddraw.dll HRESULT WINAPI DirectDrawCreateF( GUID *lpGuid, LPDIRECTDRAW *lplpDD, IUnknown *pUnkOut ); // Functions for hooking from kernel32.dll HANDLE WINAPI CreateFileF( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ); // Function for dual logging HANDLE WINAPI Replacement_CreateSemaphoreA( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName ); // Function for no movies HFILE WINAPI Replacement_OpenFile( LPCSTR lpFileName, LPOFSTRUCT lpReOpenBuff, UINT uStyle ); enum eAddressing { eByName, eByOrdinal }; struct cHookDescriptor { eAddressing m_addr; LPCTSTR m_szModule, m_szFunction; DWORD m_dwOrdinal, m_pNewFunction, m_pOldFunction; }; static bool bRegisteredInAC = false; HANDLE _hUpdateEnabled = NULL; static cHookDescriptor _hooks[] = { { eByName, _T( "ddraw.dll" ), _T( "DirectDrawCreate" ), 0, reinterpret_cast< DWORD >( DirectDrawCreateF ), 0 }, { eByName, _T( "kernel32.dll" ), _T( "CreateFileA" ), 0, reinterpret_cast< DWORD >( CreateFileF ), 0 }, }; static bool bDualLog = false; static bool bWindowed = false; static bool bNoSplash = false; static bool bNoMovies = false; static cHookDescriptor _hooksDualLog[] = { { eByName, _T( "kernel32.dll" ), _T( "CreateSemaphoreA" ), 102, reinterpret_cast< DWORD >( Replacement_CreateSemaphoreA ), 0 }, }; static cHookDescriptor _hooksNoMovies[] = { { eByName, _T( "kernel32.dll" ), _T( "OpenFile" ), 630, reinterpret_cast< DWORD >( Replacement_OpenFile ), 0 }, }; bool CheckClientVersion( MSXML::IXMLDOMDocument *pDoc ); bool PatchWindowMode( MSXML::IXMLDOMDocument *pDoc ); bool PatchNoSplash( MSXML::IXMLDOMDocument *pDoc ); HINSTANCE hAlphaBlendDLL; ///////////////////////////////////////////////////////////////////////////// // DLL Entry Point extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) { if (dwReason == DLL_PROCESS_ATTACH) { _Module.Init(ObjectMap, hInstance, &LIBID_DecalPlugins); DisableThreadLibraryCalls(hInstance); // Check out the project we're in - if it's client.exe, hook stuff up TCHAR szFilename[ MAX_PATH ]; ::GetModuleFileName( NULL, szFilename, MAX_PATH ); LPTSTR strProcessName = ::_tcsrchr( szFilename, _T( '\\' ) ); strProcessName[ 7 ] = _T( '\0' ); if( ::_tcsicmp( strProcessName + 1, _T( "client" ) ) == 0 ) { hookFunctions( _hooks, 2 ); bRegisteredInAC = true; RegKey key; if( key.Open( HKEY_LOCAL_MACHINE, "SOFTWARE\\Decal" ) == ERROR_SUCCESS ) { // Check if dual logging is enabled DWORD dwReg = 0; if( key.QueryDWORDValue( "AllowDualLog", dwReg ) == ERROR_SUCCESS ) { if( dwReg ) { bDualLog = true; hookFunctions( _hooksDualLog, 1 ); } } // Check if movies are disabled dwReg = 0; if( key.QueryDWORDValue( "NoMovies", dwReg ) == ERROR_SUCCESS ) { if( dwReg ) { bNoMovies = true; hookFunctions( _hooksNoMovies, 1 ); } } } key.Close(); // Get decal path char szPath[ MAX_PATH ]; DWORD dwCount = MAX_PATH; key.Open( HKEY_LOCAL_MACHINE, "SOFTWARE\\Decal\\Agent" ); key.QueryStringValue( "AgentPath", szPath, &dwCount ); key.Close(); strncat( szPath, "clientpatches.xml", MAX_PATH ); ::CoInitialize( NULL ); MSXML::IXMLDOMDocumentPtr pPatchesDoc; pPatchesDoc.CreateInstance( __uuidof( MSXML::DOMDocument ) ); pPatchesDoc->async = false; bool bSuccess = (pPatchesDoc->load( szPath )) ? true : false; if( bSuccess ) { if( CheckClientVersion( pPatchesDoc ) ) { RegKey key; if( key.Open( HKEY_LOCAL_MACHINE, "SOFTWARE\\Decal" ) == ERROR_SUCCESS ) { // Windowed Mode patch DWORD dwReg = 0; if( key.QueryDWORDValue( "AllowWindowed", dwReg ) == ERROR_SUCCESS ) { if( dwReg ) bWindowed = PatchWindowMode( pPatchesDoc ); } // Splash hax patches dwReg = 0; if( key.QueryDWORDValue( "NoSplash", dwReg ) == ERROR_SUCCESS ) { if( dwReg ) bNoSplash = PatchNoSplash( pPatchesDoc ); } } } } } FlushInstructionCache( GetCurrentProcess( ), NULL, NULL ); _hUpdateEnabled = ::CreateMutex( NULL, FALSE, _T( "InjectEnablePlugins" ) ); hAlphaBlendDLL = LoadLibrary("Msimg32.dll"); if (hAlphaBlendDLL != NULL) { AlphaBlendF = (LPFNDLLFUNCALPHABLEND)GetProcAddress(hAlphaBlendDLL,"AlphaBlend"); if (!AlphaBlendF) AlphaBlendF=NULL; } } else if (dwReason == DLL_PROCESS_DETACH) { ::CloseHandle( _hUpdateEnabled ); ::CoUninitialize(); if( hAlphaBlendDLL ) FreeLibrary( hAlphaBlendDLL ); _Module.Term(); } return TRUE; // ok } ///////////////////////////////////////////////////////////////////////////// // Used to determine whether the DLL can be unloaded by OLE STDAPI DllCanUnloadNow(void) { return (_Module.GetLockCount()==0) ? S_OK : S_FALSE; } ///////////////////////////////////////////////////////////////////////////// // Returns a class factory to create an object of the requested type STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { return _Module.GetClassObject(rclsid, riid, ppv); } ///////////////////////////////////////////////////////////////////////////// // DllRegisterServer - Adds entries to the system registry STDAPI DllRegisterServer(void) { // registers object, typelib and all interfaces in typelib return _Module.RegisterServer(TRUE); } ///////////////////////////////////////////////////////////////////////////// // DllUnregisterServer - Removes entries from the system registry STDAPI DllUnregisterServer(void) { return _Module.UnregisterServer(TRUE); } // The hook function - all it does is continue calls LRESULT CALLBACK hookCBTProc( int nCode, WPARAM wParam, LPARAM lParam ) { return ::CallNextHookEx( g_hHook, nCode, wParam, lParam ); } // Functions from inject api void InjectEnable() { // Get the image path (path of parent executable) TCHAR szImagePath[ MAX_PATH ]; ::GetModuleFileName( NULL, szImagePath, MAX_PATH ); LPTSTR strProcessName = ::_tcsrchr( szImagePath, _T( '\\' ) ); *( strProcessName + 1 ) = _T( '\0' ); RegKey key; key.Create( HKEY_LOCAL_MACHINE, _T( "SOFTWARE\\Decal\\Agent" ) ); key.SetStringValue( _T( "AgentPath" ), szImagePath ); // Install the global hook, injecting this DLL into every other process g_hHook = ::SetWindowsHookEx( WH_CBT, hookCBTProc, _Module.m_hInst, 0 ); } void InjectDisable() { ::UnhookWindowsHookEx( g_hHook ); g_hHook = NULL; // Send a broadcast message, this forces the hook to trigger, and thus unload SendMessage(HWND_BROADCAST,WM_NULL,0,0); } struct cPathRegistry { eInjectPath ePathType; LPCTSTR szKey, szValue; }; LPTSTR InjectMapPath( eInjectPath pathType, LPCTSTR szFilename, LPTSTR szBuffer ) { static cPathRegistry _paths[] = { { eInjectPathDatFile, _T( "SOFTWARE\\Microsoft\\Microsoft Games\\Asheron's Call\\1.00" ), _T( "Portal Dat" ) }, { eInjectPathAgent, _T( "SOFTWARE\\Decal\\Agent" ), _T( "AgentPath" ) } }; // Validate the szFilename path - If it is already complete, just pass it into the buffer and return // FIXME? : The will also "fall through" for files on our path(es)/working directory (which may later be changed...) OFSTRUCT fileDetails; if (OpenFile(szFilename, &fileDetails, OF_EXIST) != HFILE_ERROR) { ::_tcscpy(szBuffer, szFilename); } else { DWORD dwCount = MAX_PATH; RegKey key; key.Open( HKEY_LOCAL_MACHINE, _paths[ pathType ].szKey ); key.QueryStringValue( _paths[ pathType ].szValue, szBuffer, &dwCount ); ::_tcscat( szBuffer, _T( "\\" ) ); ::_tcscat( szBuffer, szFilename ); } return szBuffer; } bool CheckForHardwareMode () { RegKey key; if (key.Open (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Microsoft Games\\Asheron's Call\\1.00", KEY_READ) != ERROR_SUCCESS) { return false; } DWORD dwValue = 0; if (key.QueryDWORDValue ("UseHardware", dwValue) != ERROR_SUCCESS) { return false; } return dwValue ? true : false; } HRESULT WINAPI DirectDrawCreateF( GUID *lpGuid, LPDIRECTDRAW *lplpDD, IUnknown *pUnkOut ) { // Add another instance of the library TCHAR szModule[ MAX_PATH ]; ::GetModuleFileName( _Module.GetModuleInstance(), szModule, MAX_PATH ); ::LoadLibrary( szModule ); if( cManager::_p == NULL ) { CComObject< cManager >::CreateInstance( &cManager::_p ); cManager::_p->AddRef(); } CComPtr< IDirectDraw > pDD; HRESULT hRes = ::DirectDrawCreate( lpGuid, &pDD, pUnkOut ); if( FAILED( hRes ) ) // Boo - failure return hRes; CComObject< CDirectDrawHook > *pDDHook; CComObject< CDirectDrawHook >::CreateInstance( &pDDHook ); pDDHook->setObject( pDD ); pDDHook->QueryInterface( IID_IDirectDraw, reinterpret_cast< void ** >( lplpDD ) ); if( !CheckForHardwareMode( ) ) cManager::_p->enableSoftwareMode( ); return hRes; } // Just call to CreateFile but supplement the share modes to allow more handles to the file HANDLE WINAPI CreateFileF( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ) { // ::MessageBox( NULL, _T( "CreateFileF" ), _T( "Inject.dll" ), MB_OK ); HANDLE hFile = CreateFile( lpFileName, dwDesiredAccess, dwShareMode | FILE_SHARE_READ, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile ); DWORD dwError = GetLastError(); return hFile; } HANDLE __stdcall Replacement_CreateSemaphoreA( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName ) { if( stricmp( lpName, "Empyrean Client" ) == 0 ) { HANDLE hSem = CreateSemaphore( lpSemaphoreAttributes, lInitialCount, /* lMaximumCount */ 0x7F, lpName ); SetLastError( 0 ); return hSem; } else return CreateSemaphore( lpSemaphoreAttributes, lInitialCount, lMaximumCount, lpName ); } HFILE WINAPI Replacement_OpenFile( LPCSTR lpFileName, LPOFSTRUCT lpReOpenBuff, UINT uStyle ) { if( strstr( lpFileName, "\\movies\\" ) != NULL ) return HFILE_ERROR; else return OpenFile( lpFileName, lpReOpenBuff, uStyle ); } bool CheckClientVersion( MSXML::IXMLDOMDocument *pDoc ) { USES_CONVERSION; MSXML::IXMLDOMElementPtr pNode = pDoc->selectSingleNode( _bstr_t( "patches" ) ); long lVerMajor = 0, lVerMinor = 0, lVerRelease = 0; _variant_t vVersion = pNode->getAttribute( _bstr_t( "version" ) ); char *szVersion = OLE2A( vVersion.bstrVal ); sscanf( szVersion, "%d.%d.%d", &lVerMajor, &lVerMinor, &lVerRelease ); RegKey rk; if( rk.Open( HKEY_LOCAL_MACHINE, _T( "Software\\Microsoft\\Microsoft Games\\Asheron's Call\\1.00" ) ) == ERROR_SUCCESS ) { TCHAR szClientPath[ MAX_PATH ]; DWORD dwPathLength = MAX_PATH; if( rk.QueryStringValue ( _T( "path" ), szClientPath, &dwPathLength ) == ERROR_SUCCESS ) { ::_tcscpy( szClientPath + ( dwPathLength - 1 ), _T( "\\client.exe" ) ); DWORD dwDummy, dwVerSize = ::GetFileVersionInfoSize( const_cast< LPTSTR > ( szClientPath ), &dwDummy ); if( dwVerSize != 0 ) { BYTE *pbVersionInfo = reinterpret_cast< BYTE * >( ::_alloca( dwVerSize ) ); ::GetFileVersionInfo( const_cast< LPTSTR > ( szClientPath ), 0, dwVerSize, pbVersionInfo ); VS_FIXEDFILEINFO *vffi; UINT nLength = sizeof( VS_FIXEDFILEINFO ); if( ::VerQueryValue( pbVersionInfo, _T( "\\" ), reinterpret_cast< LPVOID * >( &vffi ), &nLength ) ) { if( (static_cast< long >( HIWORD( vffi->dwFileVersionMS ) ) == lVerMajor) && (static_cast< long >( LOWORD( vffi->dwFileVersionMS ) ) == lVerMinor) && (static_cast< long >( HIWORD( vffi->dwFileVersionLS ) ) == lVerRelease) ) { return true; } } } } } return false; } bool PatchWindowMode( MSXML::IXMLDOMDocument *pDoc ) { if( pDoc == NULL ) return false; USES_CONVERSION; MSXML::IXMLDOMElementPtr pNode; MSXML::IXMLDOMNodeListPtr pNodes = pDoc->selectNodes( _bstr_t( "/patches/patch" ) ); bool bWindowPattern = false, bWindowReplaceOffset = false, bWindowReplace = false, bWindowMaxOffset = false; char *szPatchPattern; unsigned char byteReplace; long lReplaceOffset, lMaxOffset; for( pNode = pNodes->nextNode(); pNode.GetInterfacePtr() != NULL; pNode = pNodes->nextNode() ) { if( bWindowPattern && bWindowReplace && bWindowReplaceOffset && bWindowMaxOffset ) break; _variant_t vName = pNode->getAttribute( _bstr_t( "name" ) ); char *szName = OLE2A( vName.bstrVal ); if( stricmp( szName, "WindowedModePattern" ) == 0 ) { _variant_t vPatchPattern = pNode->getAttribute( _bstr_t( "value" ) ); szPatchPattern = OLE2A( vPatchPattern.bstrVal ); bWindowPattern = true; continue; } if( stricmp( szName, "WindowedModeReplace" ) == 0 ) { _variant_t vReplace = pNode->getAttribute( _bstr_t( "value" ) ); byteReplace = wcstoul( vReplace.bstrVal, NULL, 16 ); bWindowReplace = true; continue; } if( stricmp( szName, "WindowedModeReplaceOffset" ) == 0 ) { _variant_t vReplaceOffset = pNode->getAttribute( _bstr_t( "value" ) ); lReplaceOffset = wcstoul( vReplaceOffset.bstrVal, NULL, 16 ) - 1; // Arrays are 0 indexed in c++ bWindowReplaceOffset = true; continue; } if( stricmp( szName, "WindowedModeMaxOffset" ) == 0 ) { _variant_t vMaxOffset = pNode->getAttribute( _bstr_t( "value" ) ); lMaxOffset = wcstoul( vMaxOffset.bstrVal, NULL, 16 ); bWindowMaxOffset = true; continue; } } if( !(bWindowPattern && bWindowReplace && bWindowReplaceOffset && bWindowMaxOffset) ) return false; unsigned char *pAddy = reinterpret_cast< unsigned char * >( 0x400000 ); int iLen = strlen( szPatchPattern + 1 ) / 3 + 1; unsigned char *szPatternArray = new unsigned char[ iLen ]; memset( szPatternArray, 0, iLen ); int i; for( i = 0; i < iLen; ++i ) szPatternArray[ i ] = strtoul( szPatchPattern + (i*3), NULL, 16 ); bool bAbort = true; for( i = 0; i < lMaxOffset; ++i, ++pAddy ) { // peek at current byte if( *pAddy == szPatternArray[ 0 ] ) { // see if entire pattern matches. if( memcmp( reinterpret_cast< const char * >( szPatternArray ) + 1, reinterpret_cast< char * >( pAddy ) + 1, iLen - 1 ) == 0 ) { // yahoo? ... bAbort = false; pAddy += lReplaceOffset; break; } } } if( bAbort ) return false; DWORD dwOldProtect, dwNewProtect; VirtualProtect( reinterpret_cast< void * >( pAddy ), 1, PAGE_READWRITE, &dwOldProtect ); pAddy[ 0 ] = byteReplace; VirtualProtect( reinterpret_cast< void * >( pAddy ), 1, dwOldProtect, &dwNewProtect ); return true; } bool PatchNoSplash( MSXML::IXMLDOMDocument *pDoc ) { if( pDoc == NULL ) return false; USES_CONVERSION; MSXML::IXMLDOMElementPtr pNode; MSXML::IXMLDOMNodeListPtr pNodes = pDoc->selectNodes( _bstr_t( "/patches/patch" ) ); bool bNoSplashPattern1 = false, bNoSplashPattern2 = false, bNoSplashReplaceOffset1 = false, bNoSplashReplaceOffset2 = false, bNoSplashReplace1 = false, bNoSplashReplace2 = false, bNoSplashMaxOffset1 = false, bNoSplashMaxOffset2 = false; char *szPatchPattern1, *szPatchPattern2; unsigned char byteReplace1, byteReplace2; long lReplaceOffset1, lReplaceOffset2, lMaxOffset1, lMaxOffset2; for( pNode = pNodes->nextNode(); pNode.GetInterfacePtr() != NULL; pNode = pNodes->nextNode() ) { if( bNoSplashPattern1 && bNoSplashPattern2 && bNoSplashReplaceOffset1 && bNoSplashReplaceOffset2 && bNoSplashReplace1 && bNoSplashReplace2 && bNoSplashMaxOffset1 && bNoSplashMaxOffset2 ) break; _variant_t vName = pNode->getAttribute( _bstr_t( "name" ) ); char *szName = OLE2A( vName.bstrVal ); if( stricmp( szName, "SplashHax1Pattern" ) == 0 ) { _variant_t vPatchPattern = pNode->getAttribute( _bstr_t( "value" ) ); szPatchPattern1 = OLE2A( vPatchPattern.bstrVal ); bNoSplashPattern1 = true; continue; } if( stricmp( szName, "SplashHax2Pattern" ) == 0 ) { _variant_t vPatchPattern = pNode->getAttribute( _bstr_t( "value" ) ); szPatchPattern2 = OLE2A( vPatchPattern.bstrVal ); bNoSplashPattern2 = true; continue; } if( stricmp( szName, "SplashHax1Replace" ) == 0 ) { _variant_t vReplace = pNode->getAttribute( _bstr_t( "value" ) ); byteReplace1 = wcstoul( vReplace.bstrVal, NULL, 16 ); bNoSplashReplace1 = true; continue; } if( stricmp( szName, "SplashHax2Replace" ) == 0 ) { _variant_t vReplace = pNode->getAttribute( _bstr_t( "value" ) ); byteReplace2 = wcstoul( vReplace.bstrVal, NULL, 16 ); bNoSplashReplace2 = true; continue; } if( stricmp( szName, "SplashHax1ReplaceOffset" ) == 0 ) { _variant_t vReplaceOffset = pNode->getAttribute( _bstr_t( "value" ) ); lReplaceOffset1 = wcstoul( vReplaceOffset.bstrVal, NULL, 16 ) - 1; // Arrays are 0 indexed in c++ bNoSplashReplaceOffset1 = true; continue; } if( stricmp( szName, "SplashHax2ReplaceOffset" ) == 0 ) { _variant_t vReplaceOffset = pNode->getAttribute( _bstr_t( "value" ) ); lReplaceOffset2 = wcstoul( vReplaceOffset.bstrVal, NULL, 16 ) - 1; // Arrays are 0 indexed in c++ bNoSplashReplaceOffset2 = true; continue; } if( stricmp( szName, "SplashHax1MaxOffset" ) == 0 ) { _variant_t vMaxOffset = pNode->getAttribute( _bstr_t( "value" ) ); lMaxOffset1 = wcstoul( vMaxOffset.bstrVal, NULL, 16 ); bNoSplashMaxOffset1 = true; continue; } if( stricmp( szName, "SplashHax2MaxOffset" ) == 0 ) { _variant_t vMaxOffset = pNode->getAttribute( _bstr_t( "value" ) ); lMaxOffset2 = wcstoul( vMaxOffset.bstrVal, NULL, 16 ); bNoSplashMaxOffset2 = true; continue; } } if( !(bNoSplashPattern1 && bNoSplashPattern2 && bNoSplashReplaceOffset1 && bNoSplashReplaceOffset2 && bNoSplashReplace1 && bNoSplashReplace2 && bNoSplashMaxOffset1 && bNoSplashMaxOffset2) ) return false; unsigned char *pAddy = reinterpret_cast< unsigned char * >( 0x400000 ); int iLen = strlen( szPatchPattern1 + 1 ) / 3 + 1; unsigned char *szPatternArray1 = new unsigned char[ iLen ]; memset( szPatternArray1, 0, iLen ); int i; for( i = 0; i < iLen; ++i ) szPatternArray1[ i ] = strtoul( szPatchPattern1 + (i*3), NULL, 16 ); bool bAbort1 = true; for( i = 0; i < lMaxOffset1; ++i, ++pAddy ) { // peek at current byte if( *pAddy == szPatternArray1[ 0 ] ) { // see if entire pattern matches. if( memcmp( reinterpret_cast< const char * >( szPatternArray1 ) + 1, reinterpret_cast< char * >( pAddy ) + 1, iLen - 1 ) == 0 ) { // yahoo? ... bAbort1 = false; pAddy += lReplaceOffset1; break; } } } if( bAbort1 ) return false; DWORD dwOldProtect, dwNewProtect; VirtualProtect( reinterpret_cast< void * >( pAddy ), 1, PAGE_READWRITE, &dwOldProtect ); pAddy[ 0 ] = byteReplace1; VirtualProtect( reinterpret_cast< void * >( pAddy ), 1, dwOldProtect, &dwNewProtect ); pAddy = reinterpret_cast< unsigned char * >( 0x400000 ); iLen = strlen( szPatchPattern2 + 1 ) / 3 + 1; unsigned char *szPatternArray2 = new unsigned char[ iLen ]; memset( szPatternArray2, 0, iLen ); for( i = 0; i < iLen; ++i ) szPatternArray2[ i ] = strtoul( szPatchPattern2 + (i*3), NULL, 16 ); bool bAbort2 = true; for( i = 0; i < lMaxOffset2; ++i, ++pAddy ) { // peek at current byte if( *pAddy == szPatternArray2[ 0 ] ) { // see if entire pattern matches. if( memcmp( reinterpret_cast< const char * >( szPatternArray2 ) + 1, reinterpret_cast< char * >( pAddy ) + 1, iLen - 1 ) == 0 ) { // yahoo? ... bAbort2 = false; pAddy += lReplaceOffset2; break; } } } if( bAbort2 ) return false; VirtualProtect( reinterpret_cast< void * >( pAddy ), 1, PAGE_READWRITE, &dwOldProtect ); pAddy[ 0 ] = byteReplace2; VirtualProtect( reinterpret_cast< void * >( pAddy ), 1, dwOldProtect, &dwNewProtect ); return true; } PIMAGE_IMPORT_DESCRIPTOR getNamedImportDescriptor( HMODULE hModule, LPCSTR szImportMod ) { PIMAGE_DOS_HEADER pDOSHeader = reinterpret_cast< PIMAGE_DOS_HEADER >( hModule ); // Get the PE header. PIMAGE_NT_HEADERS pNTHeader = MakePtr( PIMAGE_NT_HEADERS, pDOSHeader, pDOSHeader->e_lfanew ); // If there is no imports section, leave now. if( pNTHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ].VirtualAddress == NULL ) return NULL; // Get the pointer to the imports section. PIMAGE_IMPORT_DESCRIPTOR pImportDesc = MakePtr ( PIMAGE_IMPORT_DESCRIPTOR, pDOSHeader, pNTHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ].VirtualAddress ); // Loop through the import module descriptors looking for the // module whose name matches szImportMod. while( pImportDesc->Name != NULL ) { PSTR szCurrMod = MakePtr( PSTR, pDOSHeader, pImportDesc->Name ); if( stricmp( szCurrMod, szImportMod ) == 0 ) // Found it. break; // Look at the next one. pImportDesc ++; } // If the name is NULL, then the module is not imported. if ( pImportDesc->Name == NULL ) return ( NULL ) ; // All OK, Jumpmaster! return pImportDesc; } bool hookFunctions( cHookDescriptor *pHook, DWORD nCount ) { HMODULE hProcess = ::GetModuleHandle( NULL ); for( cHookDescriptor *i = pHook; i != pHook + nCount; ++ i ) { // Get the specific import descriptor. PIMAGE_IMPORT_DESCRIPTOR pImportDesc = getNamedImportDescriptor( hProcess, i->m_szModule ); if ( pImportDesc == NULL ) continue; // Get the original thunk information for this DLL. I cannot use // the thunk information stored in the pImportDesc->FirstThunk // because the that is the array that the loader has already // bashed to fix up all the imports. This pointer gives us acess // to the function names. PIMAGE_THUNK_DATA pOrigThunk = MakePtr( PIMAGE_THUNK_DATA, hProcess, pImportDesc->OriginalFirstThunk ); // Get the array pointed to by the pImportDesc->FirstThunk. This is // where I will do the actual bash. PIMAGE_THUNK_DATA pRealThunk = MakePtr( PIMAGE_THUNK_DATA, hProcess, pImportDesc->FirstThunk ); // Loop through and look for the one that matches the name. for( ; pOrigThunk->u1.Function != NULL; ++ pOrigThunk, ++ pRealThunk ) { if( i->m_addr == eByName ) { if( pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG ) // Only look at those that are imported by name, not ordinal. continue; // Look get the name of this imported function. PIMAGE_IMPORT_BY_NAME pByName = MakePtr( PIMAGE_IMPORT_BY_NAME, hProcess, pOrigThunk->u1.AddressOfData ); // If the name starts with NULL, then just skip out now. if( pByName->Name[ 0 ] == '\0' ) continue; if ( ::_tcsicmp( reinterpret_cast< char * >( pByName->Name ), i->m_szFunction ) != 0 ) // This name dosen't match continue; } else { if( !( pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG ) ) // The import must be by ordinal continue; if( ( pOrigThunk->u1.Ordinal & ~IMAGE_ORDINAL_FLAG ) != i->m_dwOrdinal ) // Ordinal does not match continue; } // I found it. Now I need to change the protection to // writable before I do the blast. Note that I am now // blasting into the real thunk area! MEMORY_BASIC_INFORMATION mbi_thunk; ::VirtualQuery( pRealThunk, &mbi_thunk, sizeof ( MEMORY_BASIC_INFORMATION ) ); ::VirtualProtect( mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect ); // Save the original address if requested. i->m_pOldFunction = pRealThunk->u1.Function; pRealThunk->u1.Function = i->m_pNewFunction; DWORD dwOldProtect; ::VirtualProtect( mbi_thunk.BaseAddress, mbi_thunk.RegionSize, mbi_thunk.Protect, &dwOldProtect ); break; } } return true; } void DisplayErrorMessage( DWORD dwError, char* szFrom ) { char szBuffer[1024]; char *lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), reinterpret_cast< LPTSTR > ( &lpMsgBuf ), 0, NULL ); wsprintf( szBuffer, "%s %ld: %s", szFrom, dwError, lpMsgBuf ); MessageBox( 0, szBuffer, "Error", MB_OK | MB_ICONERROR ); LocalFree( lpMsgBuf ); } // Checksum - Added for my test container void Container_Initialize( HWND hWnd, IDirectDraw4* pDD4, IDirectDrawSurface4 *pDDS4 ) { if( cManager::_p ) return; HRESULT hRes = CComObject< cManager >::CreateInstance( &cManager::_p ); if( FAILED( hRes ) ) { DisplayErrorMessage( hRes, "Creating cManager Object" ); return; } cManager::_p->AddRef( ); cManager::_p->setWindow( hWnd ); cManager::_p->m_pD = pDD4; cManager::_p->m_pPrimarySurface = pDDS4; cManager::_p->m_pDecal->InitGraphics( pDD4, NULL ); cManager::_p->m_bContainer = true; cManager::_p->m_bXMLViewViewer = false; } void Container_StartPlugins( ) { if( !cManager::_p ) return; cManager::_p->m_pDecal->StartPlugins( ); } void Container_StopPlugins( ) { if( !cManager::_p ) return; cManager::_p->m_pDecal->StopPlugins( ); } void Container_Terminate( ) { if( !cManager::_p ) return; Container_StopPlugins( ); cManager::_p->Release( ); cManager::_p = NULL; } void Container_SetSurface( IDirectDrawSurface4 *pDDS4 ) { if( !cManager::_p ) return; cManager::_p->m_pPrimarySurface = pDDS4; } void Container_Draw( ) { if( !cManager::_p ) return; if( cManager::_p->m_bInitialized ) cManager::_p->m_pRootLayer->m_pBars->Reformat( ); cManager::_p->draw2D( ); } std::set< IView* > g_pViews; void XMLViewer_Initialize( HWND hWnd, IDirectDraw4* pDD4, IDirectDrawSurface4 *pDDS4 ) { if( cManager::_p ) return; HRESULT hRes = CComObject< cManager >::CreateInstance( &cManager::_p ); if( FAILED( hRes ) ) { DisplayErrorMessage( hRes, "Creating cManager Object" ); return; } cManager::_p->AddRef( ); cManager::_p->setWindow( hWnd ); cManager::_p->m_pD = pDD4; cManager::_p->m_pPrimarySurface = pDDS4; cManager::_p->m_pDecal->InitGraphics( pDD4, NULL ); cManager::_p->m_bContainer = true; cManager::_p->m_bXMLViewViewer = true; cManager::_p->init( ); } void XMLViewer_LoadView( BSTR bstrSchema ) { if( !cManager::_p ) return; static _bstr_t strSchema( _T( "" ) _T( "" ) _T( "" ) _T( "" ) _T( "" ) _T( "" ) _T( "" ) _T( "" ) _T( "" ) _T( "" ) _T( "" ) _T("") _T( "" ) _T( "" ) _T("") _T( "" ) _T( "" ) ); IView* pView; cManager::_p->LoadView( bstrSchema ? bstrSchema : strSchema, &pView ); g_pViews.insert( pView ); } void XMLViewer_RemoveView( IView* pView ) { try{ if( !pView ) return; g_pViews.erase( pView ); pView->Release( ); pView = NULL; } catch( ... ) { pView = NULL; } } void XMLViewer_Terminate( ) { if( !cManager::_p ) return; if( cManager::_p->m_bInitialized ) { for( std::set< IView* >::iterator it = g_pViews.begin( ); it != g_pViews.end( ); ++it ) XMLViewer_RemoveView( *it ); g_pViews.clear( ); cManager::_p->term( ); } cManager::_p->Release( ); cManager::_p = NULL; } void XMLViewer_Draw( ) { Container_Draw( ); }