All 5 phases of the open-source Decal rebuild: Phase 1: 14 decompiled .NET projects (Interop.*, Adapter, FileService, DecalUtil) Phase 2: 10 native DLLs rewritten as C# COM servers with matching GUIDs - DecalDat, DHS, SpellFilter, DecalInput, DecalNet, DecalFilters - Decal.Core, DecalControls, DecalRender, D3DService Phase 3: C++ shims for Inject.DLL (D3D9 hooking) and LauncherHook.DLL Phase 4: DenAgent WinForms tray application Phase 5: WiX installer and build script 25 C# projects building with 0 errors. Native C++ projects require VS 2022 + Windows SDK (x86). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1029 lines
No EOL
29 KiB
C++
1029 lines
No EOL
29 KiB
C++
// 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 <initguid.h>
|
|
#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( "<?xml version=\"1.0\"?>" )
|
|
|
|
_T( "<view icon=\"4150\" title=\"TestView\" width=\"550\" height=\"550\">" )
|
|
|
|
_T( "<control name=\"NoteBookTab\" progid=\"DecalControls.Notebook\" left=\"5\">" )
|
|
|
|
_T( "<page label=\"Test List\">" )
|
|
_T( "<control left=\"0\" top=\"0\" width=\"180\" height=\"206\" progid=\"DecalControls.List\" name=\"sux\">" )
|
|
_T( "<column progid=\"DecalControls.IconColumn\"/>" )
|
|
_T( "<column progid=\"DecalControls.IconColumn\"/>" )
|
|
_T( "<column progid=\"DecalControls.TextColumn\" fixedwidth=\"80\"/>" )
|
|
_T( "<column progid=\"DecalControls.TextColumn\" fixedwidth=\"50\"/>" )
|
|
_T( "</control>" )
|
|
_T( "</page>" )
|
|
|
|
_T("<page label=\"whee!\">")
|
|
_T( "<control progid=\"DecalControls.FixedLayout\" width=\"550\">" )
|
|
_T( "</control>" )
|
|
_T("</page>")
|
|
|
|
_T( "</control>" )
|
|
_T( "</view>" )
|
|
);
|
|
|
|
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( );
|
|
} |