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