windows-nt/Source/XPSP1/NT/windows/appcompat/shims/specific/settlers3.cpp
2020-09-26 16:20:57 +08:00

377 lines
7.8 KiB
C++

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
Settlers3.cpp
Abstract:
The app has a protection system that sets the CPU direction flag and
expects it to be maintained through calls to WaitForSingleObject, SetEvent
and ResetEvent.
We have to patch the import tables manually and pretend to be kernel32 so
the protection system doesn't fail elsewhere.
Notes:
This is an app specific shim.
History:
07/05/2001 linstev Created
--*/
#include "precomp.h"
#include "LegalStr.h"
IMPLEMENT_SHIM_BEGIN(Settlers3)
#include "ShimHookMacro.h"
APIHOOK_ENUM_BEGIN
APIHOOK_ENUM_END
//
// The real kernel32 handle
//
HINSTANCE g_hinstKernel;
// Functions to hook imports
BOOL HookImports(HMODULE hModule);
//
// List of hooks we'll be patching
//
FARPROC _GetProcAddress(HMODULE hModule, LPCSTR lpProcName);
HINSTANCE _LoadLibraryA(LPCSTR lpLibFileName);
HMODULE _GetModuleHandleA(LPCSTR lpModuleName);
BOOL _ResetEvent(HANDLE hEvent);
BOOL _SetEvent(HANDLE hEvent);
DWORD _WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
struct AHOOK {
LPSTR szName;
PVOID pfnOld;
PVOID pfnNew;
};
AHOOK g_HookArray[] = {
{ "LoadLibraryA" , 0, _LoadLibraryA },
{ "GetProcAddress" , 0, _GetProcAddress },
{ "GetModuleHandleA" , 0, _GetModuleHandleA },
{ "ResetEvent" , 0, _ResetEvent },
{ "SetEvent" , 0, _SetEvent },
{ "WaitForSingleObject" , 0, _WaitForSingleObject }
};
DWORD g_dwHookCount = sizeof(g_HookArray) / sizeof(AHOOK);
/*++
Hooks section: each designed to spoof the app into thinking this shim is
kernel32.dll.
--*/
HINSTANCE
_LoadLibraryA(
LPCSTR lpLibFileName
)
{
if (lpLibFileName && stristr(lpLibFileName, "kernel32")) {
return g_hinstDll;
} else {
HINSTANCE hRet = LoadLibraryA(lpLibFileName);
HookImports(GetModuleHandleW(0));
return hRet;
}
}
FARPROC
_GetProcAddress(
HMODULE hModule,
LPCSTR lpProcName
)
{
if (hModule == g_hinstDll) {
hModule = g_hinstKernel;
}
FARPROC lpRet = GetProcAddress(hModule, lpProcName);
//
// Run the list of our hooks to see if we need to spoof them
//
if (lpRet) {
for (UINT i=0; i<g_dwHookCount; i++) {
if (lpRet == g_HookArray[i].pfnOld) {
lpRet = (FARPROC) g_HookArray[i].pfnNew;
break;
}
}
}
return lpRet;
}
HMODULE
_GetModuleHandleA(
LPCSTR lpModuleName
)
{
if (lpModuleName && stristr(lpModuleName, "kernel32")) {
return g_hinstDll;
} else {
return GetModuleHandleA(lpModuleName);
}
}
/*++
These save and restore the state of the direction flag (actually all flags),
before and after each call.
--*/
BOOL
_ResetEvent(
HANDLE hEvent
)
{
DWORD dwFlags;
__asm {
pushfd
pop dwFlags
}
BOOL bRet = ResetEvent(hEvent);
__asm {
push dwFlags
popfd
}
return bRet;
}
BOOL
_SetEvent(
HANDLE hEvent
)
{
DWORD dwFlags;
__asm {
pushfd
pop dwFlags
}
BOOL bRet = SetEvent(hEvent);
__asm {
push dwFlags
popfd
}
return bRet;
}
DWORD
_WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
)
{
DWORD dwFlags;
__asm {
pushfd
pop dwFlags
}
DWORD dwRet = WaitForSingleObject(hHandle, dwMilliseconds);
__asm {
push dwFlags
popfd
}
return dwRet;
}
/*++
Patch everyone to point to this dll
--*/
BOOL
HookImports(HMODULE hModule)
{
NTSTATUS status;
BOOL bAnyHooked = FALSE;
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER) hModule;
PIMAGE_NT_HEADERS pINTH;
PIMAGE_IMPORT_DESCRIPTOR pIID;
DWORD dwImportTableOffset;
DWORD dwOldProtect, dwOldProtect2;
SIZE_T dwProtectSize;
DWORD i, j;
PVOID pfnOld;
LPBYTE pDllBase = (LPBYTE) pIDH;
if (!hModule || (hModule == g_hinstDll) || (hModule == g_hinstKernel)) {
return FALSE;
}
//
// Get the import table.
//
pINTH = (PIMAGE_NT_HEADERS)(pDllBase + pIDH->e_lfanew);
dwImportTableOffset = pINTH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
if (dwImportTableOffset == 0) {
//
// No import table found. This is probably ntdll.dll
//
return TRUE;
}
pIID = (PIMAGE_IMPORT_DESCRIPTOR)(pDllBase + dwImportTableOffset);
//
// Loop through the import table and search for the APIs that we want to patch
//
while (TRUE) {
LPSTR pszImportEntryModule;
PIMAGE_THUNK_DATA pITDA;
//
// Return if no first thunk (terminating condition).
//
if (pIID->FirstThunk == 0) {
break;
}
pszImportEntryModule = (LPSTR)(pDllBase + pIID->Name);
//
// We have APIs to hook for this module!
//
pITDA = (PIMAGE_THUNK_DATA)(pDllBase + (DWORD)pIID->FirstThunk);
while (TRUE) {
SIZE_T dwFuncAddr;
AHOOK *pHook = NULL;
pfnOld = (PVOID)pITDA->u1.Function;
//
// Done with all the imports from this module? (terminating condition)
//
if (pITDA->u1.Ordinal == 0) {
break;
}
for (i=0; i<g_dwHookCount; i++) {
if (pfnOld == g_HookArray[i].pfnOld) {
pHook = &g_HookArray[i];
break;
}
}
//
// Check if we've found a hook
//
if (!pHook) {
pITDA++;
continue;
}
//
// Make the code page writable and overwrite new function pointer
// in the import table.
//
dwProtectSize = sizeof(DWORD);
dwFuncAddr = (SIZE_T)&pITDA->u1.Function;
status = VirtualProtect((PVOID)dwFuncAddr,
dwProtectSize,
PAGE_READWRITE,
&dwOldProtect);
if (NT_SUCCESS(status)) {
pITDA->u1.Function = (SIZE_T)pHook->pfnNew;
dwProtectSize = sizeof(DWORD);
status = VirtualProtect((PVOID)dwFuncAddr,
dwProtectSize,
dwOldProtect,
&dwOldProtect2);
if (!NT_SUCCESS(status)) {
DPFN(eDbgLevelError, "[HookImports] Failed to change back the protection");
}
} else {
DPFN(eDbgLevelError,
"[HookImports] Failed 0x%X to change protection to PAGE_READWRITE."
" Addr 0x%p\n",
status,
&pITDA->u1.Function);
}
pITDA++;
}
pIID++;
}
return TRUE;
}
/*++
Register hooked functions
--*/
BOOL
NOTIFY_FUNCTION(
DWORD fdwReason
)
{
if (fdwReason == DLL_PROCESS_ATTACH) {
//
// Patch all the import tables of everyone with this module
//
g_hinstKernel = GetModuleHandleW(L"kernel32");
for (UINT i=0; i<g_dwHookCount; i++) {
g_HookArray[i].pfnOld = GetProcAddress(g_hinstKernel, g_HookArray[i].szName);
}
HookImports(GetModuleHandleW(0));
}
return TRUE;
}
HOOK_BEGIN
CALL_NOTIFY_FUNCTION
HOOK_END
IMPLEMENT_SHIM_END