static BOOL HookImportFunction(HMODULE hModule, LPCSTR szImportModule, LPCSTR szFunc, PROC paHookFuncs, PROC* paOrigFuncs) { PIMAGE_IMPORT_DESCRIPTOR pImportDesc; PIMAGE_THUNK_DATA pOrigThunk; PIMAGE_THUNK_DATA pRealThunk; if (!IsNT() && ((size_t)hModule >= 0x80000000)) return FALSE; pImportDesc = GetNamedImportDescriptor(hModule, szImportModule); if (pImportDesc == NULL) return FALSE; pOrigThunk = MakePtr(PIMAGE_THUNK_DATA, hModule, pImportDesc->OriginalFirstThunk); pRealThunk = MakePtr(PIMAGE_THUNK_DATA, hModule, pImportDesc->FirstThunk); while (pOrigThunk->u1.Function) { if (IMAGE_ORDINAL_FLAG != (pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)) { PIMAGE_IMPORT_BY_NAME pByName = MakePtr(PIMAGE_IMPORT_BY_NAME, hModule, pOrigThunk->u1.AddressOfData); BOOL bDoHook; // When hook EditPlus, read pByName->Name[0] will case this dll terminate, so call IsBadReadPtr() here. if (IsBadReadPtr(pByName, sizeof(IMAGE_IMPORT_BY_NAME))) { pOrigThunk++; pRealThunk++; continue; } if ('\0' == pByName->Name[0]) { pOrigThunk++; pRealThunk++; continue; } bDoHook = FALSE; if ((szFunc[0] == pByName->Name[0]) && (_strcmpi(szFunc, (char*)pByName->Name) == 0)) { if (paHookFuncs) bDoHook = TRUE; } if (bDoHook) { MEMORY_BASIC_INFORMATION mbi_thunk; DWORD dwOldProtect; VirtualQuery(pRealThunk, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION)); VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect); if (paOrigFuncs) *paOrigFuncs = (PROC)pRealThunk->u1.Function; pRealThunk->u1.Function = (DWORD)paHookFuncs; VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, mbi_thunk.Protect, &dwOldProtect); return TRUE; } } pOrigThunk++; pRealThunk++; } return FALSE; }
BOOL BUGSUTIL_DLLINTERFACE __stdcall HookImportedFunctionsByName ( HMODULE hModule , LPCSTR szImportMod , UINT uiCount , LPHOOKFUNCDESCA paHookArray , PROC * paOrigFuncs , LPDWORD pdwHooked ) { // Double check the parameters. ASSERT ( NULL != szImportMod ) ; ASSERT ( 0 != uiCount ) ; ASSERT ( FALSE == IsBadReadPtr ( paHookArray , sizeof (HOOKFUNCDESC) * uiCount )); #ifdef _DEBUG if ( NULL != paOrigFuncs ) { ASSERT ( FALSE == IsBadWritePtr ( paOrigFuncs , sizeof ( PROC ) * uiCount ) ); } if ( NULL != pdwHooked ) { ASSERT ( FALSE == IsBadWritePtr ( pdwHooked , sizeof ( UINT ))); } // Check each function name in the hook array. { for ( UINT i = 0 ; i < uiCount ; i++ ) { ASSERT ( NULL != paHookArray[ i ].szFunc ) ; ASSERT ( '\0' != *paHookArray[ i ].szFunc ) ; // If the proc is not NULL, then it is checked. if ( NULL != paHookArray[ i ].pProc ) { ASSERT ( FALSE == IsBadCodePtr ( paHookArray[i].pProc)); } } } #endif // Do the parameter validation for real. if ( ( 0 == uiCount ) || ( NULL == szImportMod ) || ( TRUE == IsBadReadPtr ( paHookArray , sizeof (HOOKFUNCDESC) * uiCount ) ) ) { SetLastErrorEx ( ERROR_INVALID_PARAMETER , SLE_ERROR ) ; return ( FALSE ) ; } if ( ( NULL != paOrigFuncs ) && ( TRUE == IsBadWritePtr ( paOrigFuncs , sizeof ( PROC ) * uiCount ) ) ) { SetLastErrorEx ( ERROR_INVALID_PARAMETER , SLE_ERROR ) ; return ( FALSE ) ; } if ( ( NULL != pdwHooked ) && ( TRUE == IsBadWritePtr ( pdwHooked , sizeof ( UINT ) ) ) ) { SetLastErrorEx ( ERROR_INVALID_PARAMETER , SLE_ERROR ) ; return ( FALSE ) ; } // Is this a system DLL, which Windows95 will not let you patch // since it is above the 2GB line? if ( ( FALSE == IsNT ( ) ) && ( (DWORD)hModule >= 0x80000000 ) ) { SetLastErrorEx ( ERROR_INVALID_HANDLE , SLE_ERROR ) ; return ( FALSE ) ; } // TODO TODO // Should each item in the hook array be checked in release builds? if ( NULL != paOrigFuncs ) { // Set all the values in paOrigFuncs to NULL. memset ( paOrigFuncs , NULL , sizeof ( PROC ) * uiCount ) ; } if ( NULL != pdwHooked ) { // Set the number of functions hooked to zero. *pdwHooked = 0 ; } // Get the specific import descriptor. PIMAGE_IMPORT_DESCRIPTOR pImportDesc = GetNamedImportDescriptor ( hModule , szImportMod ); if ( NULL == pImportDesc ) { // The requested module was not imported. This is not an error. return ( TRUE ) ; } // 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. while ( NULL != pOrigThunk->u1.Function ) { // Only look at those that are imported by name, not ordinal. if ( IMAGE_ORDINAL_FLAG != ( pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG )) { // Look get the name of this imported function. PIMAGE_IMPORT_BY_NAME pByName ; pByName = MakePtr ( PIMAGE_IMPORT_BY_NAME , hModule , pOrigThunk->u1.AddressOfData ) ; // If the name starts with NULL, then just skip out now. if ( '\0' == pByName->Name[ 0 ] ) { continue ; } // Determines if we do the hook. BOOL bDoHook = FALSE ; // TODO TODO // Might want to consider bsearch here. // 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] ) && ( 0 == _strcmpi ( paHookArray[i].szFunc , (char*)pByName->Name ) ) ) { // If the proc is NULL, kick out, otherwise go // ahead and hook it. if ( NULL != paHookArray[ i ].pProc ) { bDoHook = TRUE ; } break ; } } if ( TRUE == bDoHook ) { // 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 ) ) ; // Save the original address if requested. if ( NULL != paOrigFuncs ) { paOrigFuncs[i] = (PROC)pRealThunk->u1.Function ; } // Do the actual hook. pRealThunk->u1.Function = (DWORD)paHookArray[i].pProc ; DWORD dwOldProtect ; // Change the protection back to what it was before I // blasted. VERIFY ( VirtualProtect ( mbi_thunk.BaseAddress , mbi_thunk.RegionSize , mbi_thunk.Protect , &dwOldProtect ) ) ; if ( NULL != pdwHooked ) { // Increment the total number hooked. *pdwHooked += 1 ; } } } // Increment both tables. pOrigThunk++ ; pRealThunk++ ; } // All OK, JumpMaster! SetLastError ( ERROR_SUCCESS ) ; return ( TRUE ) ; }
BOOL __stdcall HookOrdinalExport ( HMODULE hModule , LPCTSTR szImportMod , DWORD dwOrdinal , PROC pHookFunc , PROC * ppOrigAddr ) { // Assert the parameters. ASSERT ( NULL != hModule ) ; ASSERT ( FALSE == IsBadStringPtr ( szImportMod , MAX_PATH ) ) ; ASSERT ( 0 != dwOrdinal ) ; ASSERT ( FALSE == IsBadCodePtr ( pHookFunc ) ) ; // Perform the error checking for the parameters. if ( ( NULL == hModule ) || ( TRUE == IsBadStringPtr ( szImportMod , MAX_PATH ) ) || ( 0 == dwOrdinal ) || ( TRUE == IsBadCodePtr ( pHookFunc ) ) ) { SetLastErrorEx ( ERROR_INVALID_PARAMETER , SLE_ERROR ) ; return ( FALSE ) ; } if ( NULL != ppOrigAddr ) { ASSERT ( FALSE == IsBadWritePtr ( ppOrigAddr , sizeof ( PROC ) ) ) ; if ( TRUE == IsBadWritePtr ( ppOrigAddr , sizeof ( PROC ) ) ) { SetLastErrorEx ( ERROR_INVALID_PARAMETER , SLE_ERROR ) ; return ( FALSE ) ; } } // Get the specific import descriptor. PIMAGE_IMPORT_DESCRIPTOR pImportDesc = GetNamedImportDescriptor ( hModule , szImportMod ) ; if ( NULL == pImportDesc ) { // The requested module wasn't imported. Don't return an error. return ( TRUE ) ; } // Get the original thunk information for this DLL. I can't use // the thunk information stored in pImportDesc->FirstThunk // because the loader has already changed that array to fix up // all the imports. The original thunk gives me access to the // function names. PIMAGE_THUNK_DATA pOrigThunk = MakePtr ( PIMAGE_THUNK_DATA , hModule , pImportDesc->OriginalFirstThunk ) ; // Get the array that pImportDesc->FirstThunk points to because I'll // do the actual hooking there. PIMAGE_THUNK_DATA pRealThunk = MakePtr ( PIMAGE_THUNK_DATA , hModule , pImportDesc->FirstThunk ); // The flag is going to be set from the thunk, so make it // easier to look up. DWORD dwCompareOrdinal = IMAGE_ORDINAL_FLAG | dwOrdinal ; // Loop through and find the function to hook. while ( NULL != pOrigThunk->u1.Function ) { // Look only at functions that are imported by ordinal value, // not those that are imported by name. if ( IMAGE_ORDINAL_FLAG == ( pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG )) { // Did I find the function to hook? if ( dwCompareOrdinal == pOrigThunk->u1.Ordinal ) { // I found the function to hook. Now I need to change // the memory protection to writable before I overwrite // the function pointer. Note that I'm now writing into // the real thunk area! MEMORY_BASIC_INFORMATION mbi_thunk ; VirtualQuery ( pRealThunk , &mbi_thunk , sizeof ( MEMORY_BASIC_INFORMATION ) ) ; if ( FALSE == VirtualProtect ( mbi_thunk.BaseAddress , mbi_thunk.RegionSize , PAGE_READWRITE , &mbi_thunk.Protect )) { ASSERT ( !"VirtualProtect failed!" ) ; // There's nothing I can do but fail the function. SetLastErrorEx ( ERROR_INVALID_PARAMETER , SLE_ERROR ) ; return ( FALSE ) ; } // Save the original address if requested. if ( NULL != ppOrigAddr ) { *ppOrigAddr = (PROC)pRealThunk->u1.Function ; } // Microsoft has two different definitions of the // PIMAGE_THUNK_DATA fields as they are moving to // support Win64. The W2K RC2 Platform SDK is the // latest header, so I'll use that one and force the // Visual C++ 6 Service Pack 3 headers to deal with it. // Hook the function. DWORD * pTemp = (DWORD*)&pRealThunk->u1.Function ; *pTemp = (DWORD)(pHookFunc) ; DWORD dwOldProtect ; // Change the protection back to what it was before I // overwrote the function pointer. VERIFY ( VirtualProtect ( mbi_thunk.BaseAddress , mbi_thunk.RegionSize , mbi_thunk.Protect , &dwOldProtect ) ) ; // Life is good. SetLastError ( ERROR_SUCCESS ) ; return ( TRUE ) ; } } // Increment both tables. pOrigThunk++ ; pRealThunk++ ; } // Nothing was hooked. Technically, this isn't an error. It just // means that the module is imported but the function isn't. SetLastError ( ERROR_SUCCESS ) ; return ( FALSE ) ; }