C++ – How to hook Windows functions in C/C++

chookwindows

If I have a function foo() that windows has implemented in kernel32.dll and it always returns true, can I have my program: "bar.exe" hook/detour that Windows function and make it return false for all processes instead?

So, if my svchost, for example, calls foo(), it will return false instead of true. The same action should be expected for all other processes currently running.

If so, how? I guess I'm looking for a system-wide hook or something.

Best Answer

Take a look at Detours, it's perfect for this sort of stuff.


For system-wide hooking, read this article from MSDN.


First, create a DLL which handles hooking the functions. This example below hooks the socket send and receive functions.

#include <windows.h>
#include <detours.h>

#pragma comment( lib, "Ws2_32.lib" )
#pragma comment( lib, "detours.lib" )
#pragma comment( lib, "detoured.lib" )

int ( WINAPI *Real_Send )( SOCKET s, const char *buf, int len, int flags ) = send;
int ( WINAPI *Real_Recv )( SOCKET s, char *buf, int len, int flags ) = recv;  
int WINAPI Mine_Send( SOCKET s, const char* buf, int len, int flags );
int WINAPI Mine_Recv( SOCKET s, char *buf, int len, int flags );

int WINAPI Mine_Send( SOCKET s, const char *buf, int len, int flags ) {
    // .. do stuff ..

    return Real_Send( s, buf, len, flags );
}

int WINAPI Mine_Recv( SOCKET s, char *buf, int len, int flags ) {
    // .. do stuff ..

    return Real_Recv( s, buf, len, flags );
}

BOOL WINAPI DllMain( HINSTANCE, DWORD dwReason, LPVOID ) {
    switch ( dwReason ) {
        case DLL_PROCESS_ATTACH:       
            DetourTransactionBegin();
            DetourUpdateThread( GetCurrentThread() );
            DetourAttach( &(PVOID &)Real_Send, Mine_Send );
            DetourAttach( &(PVOID &)Real_Recv, Mine_Recv );
            DetourTransactionCommit();
            break;

        case DLL_PROCESS_DETACH:
            DetourTransactionBegin();
            DetourUpdateThread( GetCurrentThread() );
            DetourDetach( &(PVOID &)Real_Send, Mine_Send );
            DetourDetach( &(PVOID &)Real_Recv, Mine_Recv );
            DetourTransactionCommit(); 
        break;
    }

    return TRUE;
}

Then, create a program to inject the DLL into the target application.

#include <cstdio>
#include <windows.h>
#include <tlhelp32.h>

void EnableDebugPriv() {
    HANDLE hToken;
    LUID luid;
    TOKEN_PRIVILEGES tkp;

    OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken );

    LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &luid );

    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = luid;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    AdjustTokenPrivileges( hToken, false, &tkp, sizeof( tkp ), NULL, NULL );

    CloseHandle( hToken ); 
}

int main( int, char *[] ) {
    PROCESSENTRY32 entry;
    entry.dwSize = sizeof( PROCESSENTRY32 );

    HANDLE snapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, NULL );

    if ( Process32First( snapshot, &entry ) == TRUE ) {
        while ( Process32Next( snapshot, &entry ) == TRUE ) {
            if ( stricmp( entry.szExeFile, "target.exe" ) == 0 ) {
                EnableDebugPriv();

                char dirPath[MAX_PATH];
                char fullPath[MAX_PATH];

                GetCurrentDirectory( MAX_PATH, dirPath );

                sprintf_s( fullPath, MAX_PATH, "%s\\DllToInject.dll", dirPath );

                HANDLE hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, entry.th32ProcessID );
                LPVOID libAddr = (LPVOID)GetProcAddress( GetModuleHandle( "kernel32.dll" ), "LoadLibraryA" );
                LPVOID llParam = (LPVOID)VirtualAllocEx( hProcess, NULL, strlen( fullPath ), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE );

                WriteProcessMemory( hProcess, llParam, fullPath, strlen( fullPath ), NULL );
                CreateRemoteThread( hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)libAddr, llParam, NULL, NULL );
                CloseHandle( hProcess );
            }
        }
    }

    CloseHandle( snapshot );

    return 0;
}

This should be more than enough to get you started!