// Returns: Original address of intercepted function (for chaining on)
PROC
WINAPI
HookImportedFunction(
HMODULE hFromModule, // Module to intercept calls from
PSTR pszFunctionModule, // Module to intercept calls to
PSTR pszFunctionName, // Function to intercept calls to
PROC pfnNewProc // New function (replaces old function)
);
// Macro for adding pointers/DWORDs together without C arithmetic interfering
#define MakePtr( cast, ptr, addValue ) (cast)( (DWORD)(ptr)+(DWORD)(addValue))
PROC WINAPI HookImportedFunction(
HMODULE hFromModule, // Module to intercept calls from
PSTR pszFunctionModule, // Module to intercept calls to
PSTR pszFunctionName, // Function to intercept calls to
PROC pfnNewProc // New function (replaces old function)
)
{
PROC pfnOriginalProc;
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNTHeader;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
PIMAGE_THUNK_DATA pThunk;
DWORD dwOldProtect;
DWORD dwOldProtect2;
if ( IsBadCodePtr(pfnNewProc) ) // Verify that a valid pfn was passed
return 0;
// First, verify the the module and function names passed to use are valid
pfnOriginalProc = GetProcAddress( GetModuleHandle(pszFunctionModule),
pszFunctionName );
if ( !pfnOriginalProc )
return 0;
if ( (GetVersion() & 0xC0000000) == 0x80000000 )
pDosHeader = // Win32s
(PIMAGE_DOS_HEADER)GetModuleBaseFromWin32sHMod(hFromModule);
else
pDosHeader = (PIMAGE_DOS_HEADER)hFromModule; // other
// Tests to make sure we're looking at a module image (the 'MZ' header)
if ( IsBadReadPtr(pDosHeader, sizeof(IMAGE_DOS_HEADER)) )
return 0;
if ( pDosHeader->e_magic != IMAGE_DOS_SIGNATURE )
return 0;
// The MZ header has a pointer to the PE header
pNTHeader = MakePtr(PIMAGE_NT_HEADERS, pDosHeader, pDosHeader->e_lfanew);
// More tests to make sure we're looking at a "PE" image
if ( IsBadReadPtr(pNTHeader, sizeof(IMAGE_NT_HEADERS)) )
return 0;
if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE )
return 0;
// We know have a valid pointer to the module's PE header. Now go
// get a pointer to its imports section
pImportDesc = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, pDosHeader,
pNTHeader->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].
VirtualAddress);
// Bail out if the RVA of the imports section is 0 (it doesn't exist)
if ( pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pNTHeader )
return 0;
// Iterate through the array of imported module descriptors, looking
// for the module whose name matches the pszFunctionModule parameter
while ( pImportDesc->Name )
{
PSTR pszModName = MakePtr(PSTR, pDosHeader, pImportDesc->Name);
if ( stricmp(pszModName, pszFunctionModule) == 0 )
break;
pImportDesc++; // Advance to next imported module descriptor
}
// Bail out if we didn't find the import module descriptor for the
// specified module. pImportDesc->Name will be non-zero if we found it.
if ( pImportDesc->Name == 0 )
return 0;
// Get a pointer to the found module's import address table (IAT)
pThunk = MakePtr(PIMAGE_THUNK_DATA, pDosHeader, pImportDesc->FirstThunk);
// Blast through the table of import addresses, looking for the one
// that matches the address we got back from GetProcAddress above.
while ( pThunk->u1.Function )
{
if ( pThunk->u1.Function == (DWORD)pfnOriginalProc )
//这里原来是(PDWORD)pfnOriginalProc,我改成了(DWORD)pfnOriginalProc,
//后来明白了,原来BCB5和VC6对PIMAGE_THUNK_DATA的定义不同,但BCB5应该是错了,这个定义在winnt.h里。
{
// We found it! Overwrite the original address with the
// address of the interception function. Return the original
// address to the caller so that they can chain on to it.
//这里的3句原来是一句pThunk->u1.Function = (PDWORD)pfnNewProc;但是运行时出错。
//另外,我看过《Windows核心编程》,他没有用VirtualProtect函数来改变权限,但我在Win2000下
//WriteProcessMemory函数有时失败。
VirtualProtect(ppfn , 4 , PAGE_EXECUTE_WRITECOPY , &dwOldProtect );
WriteProcessMemory(GetCurrentProcess(),pfnOriginalProc, &pfnNewProc,sizeof(pfnNewProc), NULL);
VirtualProtect(ppfn , 4 , dwOldProtect , &dwOldProtect2 );
return pfnOriginalProc;
}
pThunk++; // Advance to next imported function address
}
return 0; // Function not found
}
typedef DWORD (__stdcall *XPROC)(DWORD);
// Converts an HMODULE under Win32s to a base address in memory
DWORD GetModuleBaseFromWin32sHMod(HMODULE hMod)
{
XPROC ImteFromHModule, BaseAddrFromImte;
HMODULE hModule;
DWORD imte;
// Make a typedef for the WINAPI function we're going to intercept
typedef int (__stdcall *MESSAGEBOXPROC)(HWND, LPCSTR, LPCSTR, UINT);
MESSAGEBOXPROC PfnOriginalMessageBox; // for storing original address
//
// A special version of MessageBox that always prepends "Simon Sez: "
// to the text that will be displayed.
//
int WINAPI MyMessageBox( HWND hWnd, LPCSTR lpText,
LPCSTR lpCaption, UINT uType )
{
int retValue; // real MessageBox return value
PSTR lpszRevisedString; // pointer to our modified string
// Allocate space for our revised string - add 40 bytes for new stuff
lpszRevisedString =(char *)malloc( lstrlen(lpText) + 40 );
// Now modify the original string to first say "Simon Sez: "
if ( lpszRevisedString )
{
lstrcpy(lpszRevisedString, "Simon Sez: ");
lstrcat(lpszRevisedString, lpText);
}
else // If malloc() failed, just
lpszRevisedString = (PSTR)lpText; // use the original string.
// Chain on to the original function in USER32.DLL.
retValue = PfnOriginalMessageBox(hWnd,lpszRevisedString,lpCaption,uType);
if ( lpszRevisedString != lpText ) // If we sucessfully allocated string
free( lpszRevisedString ); // memory, then free it.
return retValue; // Return whatever the real MessageBox returned
}
int main( HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow )
{
MessageBox(0, "MessageBox Isn't Intercepted Yet", "Test", MB_OK);
// Intercept the calls that this module (TESTHOOK) makes to
// MessageBox() in USER32.DLL. The function that intercepts the
// calls will be MyMessageBox(), above.
PfnOriginalMessageBox = (MESSAGEBOXPROC) HookImportedFunction(
GetModuleHandle(0), // Hook our own module
"USER32.DLL", // MessageBox is in USE32.DLL
"MessageBoxA", // function to intercept
(PROC)MyMessageBox); // interception function
if ( !PfnOriginalMessageBox ) // Make sure the interception worked
{
MessageBox(0, "Couldn't hook function", 0, MB_OK);
return 0;
}
// !!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// When built with optimizations, the VC++ compiler loads a
// register with the address of MessageBoxA, and then makes all
// subsequent calls through it. This can cause the MessageBox call
// below to not go through the Import Address table that we just patched.
// For this reason, the .MAK file for this program does not use the
// /O2 or /O1 switches. This usually won't be a problem, but it
// was in this particularly simple program. ACCKK!!!!
// Call MessageBox again. However, since we've now intercepted
// MessageBox, control should first go to our own function
// (MyMessageBox), rather than the MessageBox() code in USER32.DLL.
MessageBox(0, "MessageBox Is Now Intercepted", "Test", MB_OK);