Click here to Skip to main content
15,903,030 members
Articles / Programming Languages / C++
Article

APIHijack - A Library for easy DLL function hooking.

Rate me:
Please Sign up or sign in to vote.
4.79/5 (30 votes)
15 Sep 2000CPOL 805.2K   10.5K   173   156
This library allows you to replace functions in other DLLs with functions from your own DLL.
  • Download source files and demo project - 102 Kb

    Introduction

    Based on DelayLoadProfileDLL.CPP, by Matt Pietrek for MSJ February 2000. This code is intended to be included in a DLL inserted through a global Windows Hook (CBT hook for example). It will replace functions from other DLLs (e.g. DDRAW.DLL) with functions from your DLL.

    Functions are hooked by passing a parameter structure to the HookAPICalls() function as follows:

    SDLLHook D3DHook = 
    {
        "DDRAW.DLL",
        false, NULL,    // Default hook disabled, NULL function pointer.
        {
            { "DirectDrawCreate", MyDirectDrawCreate },
            { NULL, NULL }
        }
    };
    
    BOOL APIENTRY DllMain( HINSTANCE hModule, DWORD fdwReason, LPVOID lpReserved)
    {
        if ( fdwReason == DLL_PROCESS_ATTACH )  // When initializing....
        {
            hDLL = hModule;
    
            // We don't need thread notifications for what we're doing.  Thus, 
            // get rid of them, thereby eliminating some of the overhead of 
            // this DLL
            DisableThreadLibraryCalls( hModule );
    
            // Only hook the APIs if this is the right process.
            GetModuleFileName( GetModuleHandle( NULL ), Work, sizeof(Work) );
            PathStripPath( Work );
    
            if ( stricmp( Work, "myhooktarget.exe" ) == 0 )
                HookAPICalls( &D3DHook );
        }
    
        return TRUE;
    }

    Now all that remains is to get your DLL loaded into the target process.

  • License

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


    Written By
    United States United States
    This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

    Comments and Discussions

     
    GeneralRe: Creating toolbar Pin
    dorutzu9-Jun-04 6:26
    dorutzu9-Jun-04 6:26 
    GeneralRe: Creating toolbar Pin
    ThatsAlok3-Dec-04 19:09
    ThatsAlok3-Dec-04 19:09 
    GeneralRe: Creating toolbar Pin
    Anonymous28-Dec-04 23:35
    Anonymous28-Dec-04 23:35 
    GeneralCapture Text in RichEdit and HTML Pin
    minhvc7-Mar-04 16:39
    minhvc7-Mar-04 16:39 
    QuestionHow to remove hook Pin
    minhvc19-Feb-04 15:53
    minhvc19-Feb-04 15:53 
    AnswerRe: How to remove hook Pin
    minhvc7-Mar-04 16:36
    minhvc7-Mar-04 16:36 
    GeneralRe: How to remove hook Pin
    Roey C29-Mar-08 19:01
    Roey C29-Mar-08 19:01 
    AnswerResetIAT & UnhookAPICalls Pin
    deodiaus26-May-05 7:03
    deodiaus26-May-05 7:03 
    I had some problems with this code, and needed to impletment this unhook feature.
    bool ResetIAT( SDLLHook* DLLHook, PIMAGE_IMPORT_DESCRIPTOR pImportDesc, PVOID pBaseLoadAddr )
    {
    try{
    PIMAGE_THUNK_DATA pIAT; // Ptr to import address table
    PIMAGE_THUNK_DATA pINT; // Ptr to import names table
    PIMAGE_THUNK_DATA pIteratingIAT;

    // Figure out which OS platform we're on
    OSVERSIONINFO osvi;
    osvi.dwOSVersionInfoSize = sizeof(osvi);
    GetVersionEx( &osvi );

    // If no import names table, we can't redirect this, so bail
    if ( pImportDesc->OriginalFirstThunk == 0 )
    return false;

    pIAT = MakePtr( PIMAGE_THUNK_DATA, pBaseLoadAddr, pImportDesc->FirstThunk );
    pINT = MakePtr( PIMAGE_THUNK_DATA, pBaseLoadAddr, pImportDesc->OriginalFirstThunk );

    // Count how many entries there are in this IAT. Array is 0 terminated
    pIteratingIAT = pIAT;
    unsigned cFuncs = 0;
    while ( pIteratingIAT->u1.Function )
    {
    cFuncs++;
    pIteratingIAT++;
    }

    if ( cFuncs == 0 ) // If no imported functions, we're done!
    return false;

    // These next few lines ensure that we'll be able to modify the IAT,
    // which is often in a read-only section in the EXE.
    DWORD flOldProtect, flNewProtect, flDontCare;
    MEMORY_BASIC_INFORMATION mbi;

    // Get the current protection attributes
    VirtualQuery( pIAT, &mbi, sizeof(mbi) );

    // remove ReadOnly and ExecuteRead attributes, add on ReadWrite flag
    flNewProtect = mbi.Protect;
    flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ);
    flNewProtect |= (PAGE_READWRITE);

    if ( !VirtualProtect( pIAT, sizeof(PVOID) * cFuncs,
    flNewProtect, &flOldProtect) )
    {
    return false;
    }

    // If the Default hook is enabled, build an array of redirection stubs in the processes memory.
    DLPD_IAT_STUB * pStubs = 0;
    if ( DLLHook->UseDefault )
    {
    // Allocate memory for the redirection stubs. Make one extra stub at the
    // end to be a sentinel
    pStubs = new DLPD_IAT_STUB[ cFuncs + 1];
    if ( !pStubs )
    return false;
    }

    // Scan through the IAT, completing the stubs and redirecting the IAT
    // entries to point to the stubs
    pIteratingIAT = pIAT;

    while ( pIteratingIAT->u1.Function )
    {
    void* HookFn = 0; // Set to either the SFunctionHook or pStubs.

    if ( !IMAGE_SNAP_BY_ORDINAL( pINT->u1.Ordinal ) ) // import by name
    {
    PIMAGE_IMPORT_BY_NAME pImportName = MakePtr( PIMAGE_IMPORT_BY_NAME, pBaseLoadAddr, pINT->u1.AddressOfData );

    // Iterate through the hook functions, searching for this import.
    SFunctionHook* FHook = DLLHook->Functions;
    while ( FHook->Name )
    {
    if ( lstrcmpi( FHook->Name, (char*)pImportName->Name ) == 0 )
    {
    OutputDebugString( "unhooked function: " );
    OutputDebugString( (char*)pImportName->Name );
    OutputDebugString( "\n" );

    // Save the old function in the SFunctionHook structure and get the new one.
    if (FHook->OrigFn != pIteratingIAT->u1.Function){
    char szBuf[1025];
    sprintf( szBuf, "FHook->OrigFn (0x%p) != (0x%p) pIteratingIAT->u1.Function in %s\n", FHook->OrigFn, pIteratingIAT->u1.Function, FHook->Name);
    OutputDebugString(szBuf);
    } //else
    HookFn = FHook->OrigFn;// wheger

    break;
    }
    FHook++;
    }

    // If the default function is enabled, store the name for the user.
    if ( DLLHook->UseDefault )
    pStubs->pszNameOrOrdinal = (DWORD)&pImportName->Name;
    }
    else
    {
    // If the default function is enabled, store the ordinal for the user.
    if ( DLLHook->UseDefault )
    pStubs->pszNameOrOrdinal = pINT->u1.Ordinal;
    }

    // If the default function is enabled, fill in the fields to the stub code.
    if ( DLLHook->UseDefault )
    {
    pStubs->data_call = (DWORD)(PDWORD)DLLHook->DefaultFn
    - (DWORD)(PDWORD)&pStubs->instr_JMP;
    pStubs->data_JMP = *(PDWORD)pIteratingIAT - (DWORD)(PDWORD)&pStubs->count;

    // If it wasn't manually hooked, use the Stub function.
    if ( !HookFn )
    HookFn = (void*)pStubs;
    }

    // Replace the IAT function pointer if we have a hook.
    if ( HookFn )
    {
    // Cheez-o hack to see if what we're importing is code or data.
    // If it's code, we shouldn't be able to write to it
    if ( IsBadWritePtr( (PVOID)pIteratingIAT->u1.Function, 1 ) )
    {
    pIteratingIAT->u1.Function = (PDWORD)HookFn;
    }
    else if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
    {
    // Special hack for Win9X, which builds stubs for imported
    // functions in system DLLs (Loaded above 2GB). These stubs are
    // writeable, so we have to explicitly check for this case
    if ( pIteratingIAT->u1.Function > (PDWORD)0x80000000 )
    pIteratingIAT->u1.Function = (PDWORD)HookFn;
    }
    }

    if ( DLLHook->UseDefault )
    pStubs++; // Advance to next stub

    pIteratingIAT++; // Advance to next IAT entry
    pINT++; // Advance to next INT entry
    }

    if ( DLLHook->UseDefault )
    pStubs->pszNameOrOrdinal = 0; // Final stub is a sentinel

    // Put the page attributes back the way they were.
    VirtualProtect( pIAT, sizeof(PVOID) * cFuncs, flOldProtect, &flDontCare);
    } catch(...){
    OutputDebugString("Exception caught in ResetIAT\n");
    throw;
    }

    return true;
    }


    // need to reset the pointers back to their old values
    bool UnhookAPICalls( SDLLHook* Hook, HMODULE hModule, int iDepth )
    {
    try{
    iDepth++;
    if (iDepth>100){
    OutputDebugString("Stuck in infinite recursion");
    return false;
    }
    char *fName = new char[100];
    GetModuleFileName(hModule, fName, 100);
    SETMODULES::iterator it = setModules.find(fName);
    if (it==setModules.end())
    return false;
    setModules.erase( it);

    OutputDebugString("unhooking=");
    OutputDebugString(fName);
    OutputDebugString("\n");
    delete[] fName;

    if ( !Hook )
    return false;

    PIMAGE_NT_HEADERS pExeNTHdr = PEHeaderFromHModule( hModule );

    if ( !pExeNTHdr )
    return false;

    DWORD importRVA = pExeNTHdr->OptionalHeader.DataDirectory
    [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
    if ( !importRVA )
    return false;

    // Convert imports RVA to a usable pointer
    PIMAGE_IMPORT_DESCRIPTOR pImportDesc = MakePtr( PIMAGE_IMPORT_DESCRIPTOR,
    hModule, importRVA );

    // Save off imports address in a global for later use
    g_pFirstImportDesc = pImportDesc;

    // Iterate through each import descriptor, and redirect if appropriate
    while ( pImportDesc->FirstThunk )
    {
    PSTR pszImportModuleName = MakePtr( PSTR, hModule, pImportDesc->Name);

    for (int iNameIndex=0; iNameIndex<max_dll_number; inameindex++)
    ="" {
    ="" char="" *dllname="Hook-">Name[iNameIndex];
    if (!dllName)
    break;
    if ( lstrcmpi( pszImportModuleName, dllName ) == 0 )
    {
    OutputDebugString( "Restoring " );
    OutputDebugString( dllName );
    OutputDebugString( "...\n" );

    ResetIAT( Hook, pImportDesc, (PVOID)hModule );
    break;
    }
    }
    UnhookAPICalls(Hook, GetModuleHandle(pszImportModuleName), iDepth);

    pImportDesc++; // Advance to next import descriptor
    }
    } catch(...){
    OutputDebugString("Exception caught in UnhookAPICalls\n");
    throw;
    }
    return true;
    }
    GeneralRe: ResetIAT &amp; UnhookAPICalls Pin
    Roey C29-Mar-08 19:17
    Roey C29-Mar-08 19:17 
    AnswerRe: How to remove hook [modified] Pin
    Roey C29-Mar-08 18:22
    Roey C29-Mar-08 18:22 
    Questionhow to know some button are pressed use hook Pin
    tang1000716-Feb-04 5:31
    tang1000716-Feb-04 5:31 
    AnswerRe: how to know some button are pressed use hook Pin
    Rupom/RUET, Bangladesh7-Jun-04 20:04
    sussRupom/RUET, Bangladesh7-Jun-04 20:04 
    GeneralUnHook!!!!! Pin
    minhvc15-Feb-04 15:04
    minhvc15-Feb-04 15:04 
    GeneralEncrypted Pin
    khossy14-Feb-04 23:50
    susskhossy14-Feb-04 23:50 
    GeneralError compile Pin
    bigwizard28-Jan-04 5:55
    bigwizard28-Jan-04 5:55 
    GeneralRe: Error compile Pin
    Rupom / RUET,Bangladesh21-Jul-04 19:37
    sussRupom / RUET,Bangladesh21-Jul-04 19:37 
    GeneralProblem hooking TextOut Pin
    BobAshforth19-Nov-03 6:07
    BobAshforth19-Nov-03 6:07 
    GeneralHookApi SourceCode Pin
    xiamy8-Nov-03 6:31
    xiamy8-Nov-03 6:31 
    QuestionWhy can not hook the function that its return is pointer? Pin
    doxuanhuyen4-Nov-03 3:50
    doxuanhuyen4-Nov-03 3:50 
    AnswerRe: Why can not hook the function that its return is pointer? Pin
    autodebug12-Nov-03 17:16
    autodebug12-Nov-03 17:16 
    GeneralNice article Pin
    Kentamanos6-Oct-03 16:16
    Kentamanos6-Oct-03 16:16 
    GeneralDon't work for win98/95!! Pin
    chn1-Sep-03 17:48
    chn1-Sep-03 17:48 
    GeneralWarning: VIRUS FOUND!!! Pin
    andrewgs7324-Aug-03 1:05
    andrewgs7324-Aug-03 1:05 
    GeneralRe: Warning: VIRUS FOUND!!! Pin
    Kentamanos6-Oct-03 16:18
    Kentamanos6-Oct-03 16:18 
    GeneralRe: Warning: VIRUS FOUND!!! Pin
    Miguel Lopes11-May-04 2:36
    Miguel Lopes11-May-04 2:36 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.