mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
339 lines
11 KiB
C++
339 lines
11 KiB
C++
/*
|
|
Module : HookImportFunction.cpp
|
|
Purpose: Defines the implementation for code to hook a call to any imported Win32 SDK
|
|
Created: PJN / 23-10-1999
|
|
History: PJN / 01-01-2001 1. Now includes copyright message in the source code and documentation.
|
|
2. Fixed an access violation in where I was getting the name of the import
|
|
function but not checking for failure.
|
|
3. Fixed a compiler error where I was incorrectly casting to a PDWORD instead
|
|
of a DWORD
|
|
PJN / 20-04-2002 1. Fixed a potential infinite loop in HookImportFunctionByName. Thanks to
|
|
David Defoort for spotting this problem.
|
|
|
|
Copyright (c) 1996 - 2002 by PJ Naughter. (Web: www.naughter.com, Email: pjna@naughter.com)
|
|
|
|
All rights reserved.
|
|
|
|
Copyright / Usage Details:
|
|
|
|
You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise)
|
|
when your product is released in binary form. You are allowed to modify the source code in any way you want
|
|
except you cannot modify the copyright details at the top of each module. If you want to distribute source
|
|
code with your application, then you are only allowed to distribute versions released by the author. This is
|
|
to maintain a single distribution point for the source code.
|
|
|
|
*/
|
|
|
|
|
|
////////////////// Includes ////////////////////////////////////
|
|
|
|
#include <windows.h>
|
|
#include "HookImportFunction.h"
|
|
|
|
#define ASSERT(e)
|
|
#define VERIFY(e) e
|
|
#define TRACE0(s) OutputDebugString(s)
|
|
#define _T(s) s
|
|
|
|
////////////////// Defines / Locals ////////////////////////////
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#define MakePtr(cast, ptr, AddValue) (cast)((DWORD)(ptr)+(DWORD)(AddValue))
|
|
|
|
BOOL IsNT();
|
|
|
|
|
|
|
|
////////////////// Implementation //////////////////////////////
|
|
|
|
BOOL HookImportFunctionsByName(HMODULE hModule, LPCSTR szImportMod, UINT uiCount,
|
|
LPHOOKFUNCDESC paHookArray, PROC* paOrigFuncs, UINT* puiHooked)
|
|
{
|
|
// Double check the parameters.
|
|
ASSERT(szImportMod);
|
|
ASSERT(uiCount);
|
|
ASSERT(!IsBadReadPtr(paHookArray, sizeof(HOOKFUNCDESC)*uiCount));
|
|
|
|
#ifdef _DEBUG
|
|
if (paOrigFuncs)
|
|
ASSERT(!IsBadWritePtr(paOrigFuncs, sizeof(PROC)*uiCount));
|
|
if (puiHooked)
|
|
ASSERT(!IsBadWritePtr(puiHooked, sizeof(UINT)));
|
|
|
|
//Check each function name in the hook array.
|
|
for (UINT i = 0; i<uiCount; i++)
|
|
{
|
|
ASSERT(paHookArray[i].szFunc);
|
|
ASSERT(*paHookArray[i].szFunc != _T('\0'));
|
|
|
|
//If the proc is not NULL, then it is checked.
|
|
if (paHookArray[i].pProc)
|
|
ASSERT(!IsBadCodePtr(paHookArray[i].pProc));
|
|
}
|
|
#endif
|
|
|
|
//Do the parameter validation for real.
|
|
if (uiCount == 0 || szImportMod == NULL || IsBadReadPtr(paHookArray, sizeof(HOOKFUNCDESC)* uiCount))
|
|
{
|
|
ASSERT(FALSE);
|
|
SetLastErrorEx(ERROR_INVALID_ADDRESS, SLE_ERROR);
|
|
return FALSE;
|
|
}
|
|
|
|
if (paOrigFuncs && IsBadWritePtr(paOrigFuncs, sizeof(PROC)*uiCount))
|
|
{
|
|
ASSERT(FALSE);
|
|
SetLastErrorEx(ERROR_INVALID_ADDRESS, SLE_ERROR);
|
|
return FALSE;
|
|
}
|
|
|
|
if (puiHooked && IsBadWritePtr(puiHooked, sizeof(UINT)))
|
|
{
|
|
ASSERT(FALSE);
|
|
SetLastErrorEx(ERROR_INVALID_ADDRESS, SLE_ERROR );
|
|
return FALSE;
|
|
}
|
|
|
|
//Is this a system DLL, which Windows95 will not let you patch
|
|
//since it is above the 2GB line?
|
|
if (!IsNT() && ((DWORD)hModule >= 0x80000000))
|
|
{
|
|
#ifdef _DEBUG
|
|
CString sMsg;
|
|
sMsg.Format(_T("Could not hook module %x because we are on Win9x and it is in shared memory\n"), hModule);
|
|
OutputDebugString(sMsg);
|
|
#endif
|
|
SetLastErrorEx(ERROR_INVALID_HANDLE, SLE_ERROR);
|
|
return FALSE;
|
|
}
|
|
|
|
//TODO TODO
|
|
// Should each item in the hook array be checked in release builds?
|
|
|
|
if (puiHooked)
|
|
*puiHooked = 0; //Set the number of functions hooked to zero.
|
|
|
|
//Get the specific import descriptor.
|
|
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = GetNamedImportDescriptor(hModule, szImportMod);
|
|
if (NULL == pImportDesc)
|
|
return FALSE; // The requested module was not imported.
|
|
|
|
HINSTANCE hImportMod = GetModuleHandle(szImportMod);
|
|
if (NULL == hImportMod)
|
|
{
|
|
ASSERT(FALSE);
|
|
SetLastErrorEx(ERROR_HOOK_NEEDS_HMOD, SLE_ERROR);
|
|
return FALSE; // The requested module was not available.
|
|
}
|
|
|
|
//Set all the values in paOrigFuncs to NULL.
|
|
if (NULL != paOrigFuncs)
|
|
memset(paOrigFuncs, NULL, sizeof(PROC)*uiCount);
|
|
|
|
//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, hModule, 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, hModule, pImportDesc->FirstThunk);
|
|
|
|
//Loop through and look for the one that matches the name.
|
|
for (; NULL != pOrigThunk->u1.Function;
|
|
// Increment both tables.
|
|
pOrigThunk++, pRealThunk++)
|
|
{
|
|
//Only look at those that are imported by name, not ordinal.
|
|
if (IMAGE_ORDINAL_FLAG == (IMAGE_ORDINAL_FLAG & pOrigThunk->u1.Ordinal))
|
|
continue;
|
|
|
|
//Look get the name of this imported function.
|
|
PIMAGE_IMPORT_BY_NAME pByName = MakePtr(PIMAGE_IMPORT_BY_NAME, hModule, pOrigThunk->u1.AddressOfData);
|
|
|
|
if (IsBadReadPtr(pByName, MAX_PATH+4))
|
|
{
|
|
SetLastErrorEx(ERROR_INVALID_ADDRESS, SLE_ERROR);
|
|
continue;
|
|
}
|
|
|
|
//If the name starts with NULL, then just skip to next.
|
|
if (_T('\0') == pByName->Name[0])
|
|
continue;
|
|
|
|
//Determines if we do the hook.
|
|
BOOL bDoHook = FALSE;
|
|
|
|
//TODO {
|
|
// Might want to consider bsearch here.
|
|
//TODO }
|
|
//See if the particular function name is in the import
|
|
// list. It might be good to consider requiring the
|
|
// paHookArray to be in sorted order so bsearch could be
|
|
// used so the lookup will be faster. However, the size of
|
|
// uiCount coming into this function should be rather small
|
|
// but it is called for each function imported by szImportMod.
|
|
for (UINT i = 0; i<uiCount; i++)
|
|
{
|
|
if ((paHookArray[i].szFunc[0] == pByName->Name[0]) &&
|
|
(strcmpi(paHookArray[i].szFunc, (char*)pByName->Name) == 0))
|
|
{
|
|
//If the proc is NULL, kick out, otherwise
|
|
// go ahead and hook it.
|
|
if (paHookArray[i].pProc)
|
|
bDoHook = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (FALSE == bDoHook)
|
|
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));
|
|
VERIFY(VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect));
|
|
|
|
// Get fast/simple pointer
|
|
PROC* pFunction = (PROC*) &(pRealThunk->u1.Function);
|
|
if (*pFunction == paHookArray[i].pProc)
|
|
{
|
|
SetLastErrorEx(ERROR_ALREADY_INITIALIZED, SLE_ERROR);
|
|
return FALSE;
|
|
}
|
|
if (IsBadCodePtr(*pFunction))
|
|
{
|
|
ASSERT(FALSE);
|
|
SetLastErrorEx(ERROR_INVALID_ADDRESS, SLE_ERROR);
|
|
return FALSE;
|
|
}
|
|
//Save the original address if requested.
|
|
if (NULL != paOrigFuncs)
|
|
{
|
|
if ((DWORD)(*pFunction) < (DWORD)hImportMod && ((DWORD)(0x80000000) > (DWORD)hImportMod))
|
|
{
|
|
ASSERT(FALSE);
|
|
SetLastErrorEx(ERROR_INVALID_ADDRESS, SLE_ERROR);
|
|
return FALSE;
|
|
}
|
|
if (*pFunction != paOrigFuncs[i])
|
|
{
|
|
if (NULL != paOrigFuncs[i])
|
|
{
|
|
if (paHookArray[i].pProc != paOrigFuncs[i])
|
|
{
|
|
ASSERT(FALSE);
|
|
SetLastErrorEx(ERROR_INVALID_ADDRESS, SLE_ERROR);
|
|
return FALSE;
|
|
}
|
|
}
|
|
paOrigFuncs[i] = * pFunction;
|
|
}
|
|
}
|
|
//Do the actual hook.
|
|
*pFunction = paHookArray[i].pProc;
|
|
|
|
//Increment the total number hooked.
|
|
if (puiHooked)
|
|
*puiHooked += 1;
|
|
|
|
//Change the protection back to what it was before I blasted.
|
|
DWORD dwOldProtect;
|
|
VERIFY(VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, mbi_thunk.Protect, &dwOldProtect));
|
|
}
|
|
//All OK, JumpMaster!
|
|
SetLastError(ERROR_SUCCESS);
|
|
return TRUE;
|
|
}
|
|
|
|
PIMAGE_IMPORT_DESCRIPTOR GetNamedImportDescriptor(HMODULE hModule, LPCSTR szImportMod)
|
|
{
|
|
//Always check parameters.
|
|
ASSERT(szImportMod);
|
|
ASSERT(hModule);
|
|
if ((szImportMod == NULL) || (hModule == NULL))
|
|
{
|
|
ASSERT(FALSE);
|
|
SetLastErrorEx(ERROR_INVALID_PARAMETER, SLE_ERROR);
|
|
return NULL;
|
|
}
|
|
|
|
//Get the Dos header.
|
|
PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER) hModule;
|
|
|
|
// Is this the MZ header?
|
|
if (IsBadReadPtr(pDOSHeader, sizeof(IMAGE_DOS_HEADER)) || (pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE))
|
|
{
|
|
#ifdef _DEBUG
|
|
CString sMsg;
|
|
sMsg.Format(_T("Could not find the MZ Header for %x\n"), hModule);
|
|
OutputDebugString(sMsg);
|
|
#endif
|
|
SetLastErrorEx( ERROR_BAD_EXE_FORMAT, SLE_ERROR);
|
|
return NULL;
|
|
}
|
|
|
|
// Get the PE header.
|
|
PIMAGE_NT_HEADERS pNTHeader = MakePtr(PIMAGE_NT_HEADERS, pDOSHeader, pDOSHeader->e_lfanew);
|
|
|
|
//Is this a real PE image?
|
|
if (IsBadReadPtr(pNTHeader, sizeof(IMAGE_NT_HEADERS)) || (pNTHeader->Signature != IMAGE_NT_SIGNATURE))
|
|
{
|
|
ASSERT(FALSE);
|
|
SetLastErrorEx( ERROR_INVALID_EXE_SIGNATURE, SLE_ERROR);
|
|
return NULL;
|
|
}
|
|
|
|
//If there is no imports section, leave now.
|
|
if (pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress == 0)
|
|
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)
|
|
{
|
|
PSTR szCurrMod = MakePtr(PSTR, pDOSHeader, pImportDesc->Name);
|
|
if (stricmp(szCurrMod, szImportMod) == 0)
|
|
break; // Found it.
|
|
|
|
//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 IsNT()
|
|
{
|
|
OSVERSIONINFO stOSVI;
|
|
memset(&stOSVI, NULL, sizeof(OSVERSIONINFO));
|
|
stOSVI.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
|
|
BOOL bRet = GetVersionEx(&stOSVI);
|
|
ASSERT(TRUE == bRet);
|
|
if (FALSE == bRet)
|
|
{
|
|
TRACE0("GetVersionEx failed!\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//Check the version and call the appropriate thing.
|
|
return (VER_PLATFORM_WIN32_NT == stOSVI.dwPlatformId);
|
|
}
|