openDecal/Native/Inject/Inject.cpp
erik d1442e3747 Initial commit: Complete open-source Decal rebuild
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>
2026-02-08 18:27:56 +01:00

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( );
}