34

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.

AstroCB
  • 12,337
  • 20
  • 57
  • 73
Clark Gaebel
  • 17,280
  • 20
  • 66
  • 93

5 Answers5

39

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!

Max Truxa
  • 3,308
  • 25
  • 38
xian
  • 4,657
  • 5
  • 34
  • 38
  • I'm not sure what you mean by that, could you clarify? Also, I updated my post with some code. – xian May 17 '09 at 01:07
  • Mmm... I'm looking for more of a generic solution to the problem. So that EVERY process (not just foo) that calls the windows function get's true instead of false. – Clark Gaebel May 17 '09 at 01:10
  • Then you should clarify that in your question. You asked how to hook into "foo.exe" and this does that. – xian May 17 '09 at 01:16
  • 1
    Then it's not possible in user mode – Valentin Galea May 17 '09 at 01:16
  • 1
    Then you need to inject the dll into every process or write a kernel mode driver todo this. I'd consider this malware – newgre May 17 '09 at 01:17
  • Detours has this functionality built in, simply use setdll. Injecting into every process on the system is frequently nessissary, RPC debugging or some other such system wide message pump/sink would be helplessly difficult to debug if you were not able to watch system wide events. setdll will reset the import table of a PE, you may have to un-set windows file protection to actually update a system dll though. Regardless, use EASYHOOK it's much supiour to detours (detours has not been updated in years) – RandomNickName42 May 17 '09 at 01:28
  • I clarified the question... look again. – Clark Gaebel May 17 '09 at 01:29
  • The article has been removed; could you provide a working link? – phimuemue Oct 05 '11 at 19:44
  • I believe you need to assign the address of `send` to `Real_Send` before calling `DetourAttach` (`Real_Send = send; DetourAttach(....)`) – René Nyffenegger Feb 12 '21 at 12:16
14

EASYHOOK https://github.com/EasyHook/EasyHook

Dominate's all aformentioned techniques in simpleicty, flexability and functionality.

It was not discussed previously on Hook processes either. I've read all leaf's of this thread and with absolute certanty, EASYHOOK is vastly superiour. No matter if your using C, C++, CLR, whatever.

I'll paste a bit from the codeplex homepage, to ensure sufficient omage being paid.

The following is an incomplete list of features:

  1. A so called "Thread Deadlock Barrier" will get rid of many core problems when hooking unknown APIs; this technology is unique to EasyHook
  2. You can write managed hook handlers for unmanaged APIs
  3. You can use all the convenience managed code provides, like NET Remoting, WPF and WCF for example
  4. A documented, pure unmanaged hooking API
  5. Support for 32- and 64-bit kernel mode hooking (also check out my PatchGuard 3 bypass driver which can be found in the release list)
  6. No resource or memory leaks are left in the target
  7. Experimental stealth injection mechanism that won't raise the attention of any current AV Software
  8. EasyHook32.dll and EasyHook64.dll are pure unmanaged modules and can be used without any NET framework installed!
  9. All hooks are installed and automatically removed in a stable manner
  10. Support for Windows Vista SP1 x64 and Windows Server 2008 SP1 x64 by utilizing totally undocumented APIs, to still allow hooking into any terminal session.
  11. Managed/Unmanaged module stack trace inside a hook handler
  12. Get calling managed/unmanaged module inside a hook handler
  13. Create custom stack traces inside a hook handler
  14. You will be able to write injection libraries and host processes compiled for AnyCPU, which will allow you to inject your code into 32- and 64-Bit processes from 64- and 32-Bit processes by using the very same assembly in all cases.
  15. EasyHook supports RIP-relative addressing relocation for 64-Bit targets.
  16. No unpacking/installation is necessary.
  17. The Visual Studio Redistributable is not required.

I'm happy that my hooker's still know a few tricks in comparison that makes me keep them around. But to be sure, when you need a HOOK, 99 times of 100, EASYHOOK'r will get you there faster. And it's quite actively maintained.

Nate Zaugg
  • 4,202
  • 2
  • 36
  • 53
RandomNickName42
  • 5,923
  • 1
  • 36
  • 35
  • wowus: I allready told you, easyhook, has a built in "Stealth.c" code file, which does exactally what your asking. It's in the download. – RandomNickName42 May 17 '09 at 01:54
  • Codeplex is now Microsoft and Microsoft has stopped the service. The links don't work any more. I don't know the original product, but https://easyhook.github.io/ might be the new location. – Thomas Weller Jan 18 '22 at 15:51
8

Please give more details of the function you want to hook! There are several ways to get your own code called in such a case, for instance:

  • You can build a fake DLL with the same name as the DLL that contains the function you want to hook (and copy it in the folder of foo.exe). This library would expose exactly the same functions as the original DLL. Each exposed function just bypasses the call to the original DLL, with the exception of the function you want to hook.

  • You can change the function pointer table during run-time, for instance with the (commercial) Detour package that has been mentioned by "kitchen". However, doing such hooking can be done easily by your own, see this article to learn how.

  • You can find out where the specific function is called in foo.exe and just replace the assembly code that calls the function with a code that "returns true". Basically, you're patching "foo.exe"..

  • For specific functions, Windows offers automatic hooking, e.g. for keys and mouse events. Check the function SetWindowsHook for this.

beef2k
  • 2,233
  • 2
  • 19
  • 20
3

This depends somewhat on the version of Windows you're wanting to target. Nonetheless, if you're playing on Pre-Vista, you can simply use SetWindowsHookEx to inject your DLL into every running process. Your DLL would then need to hook the appropriate function using Detours or similar.

mrduclaw
  • 3,965
  • 4
  • 36
  • 37
-2

If you are writing your hook in assembly and not using Detours (for whatever reason), then you need some key information about returing FALSE:

  • Win32, set EAX to 0
  • Win64, set RAX to 0

You need to set EAX or RAX (depending upon platform) to zero as the last thing the function you are hooking does. That will result in the calling code receiving 0 as the return value (assuming they are returning an int or pointer type value).

Stephen Kellett
  • 3,078
  • 1
  • 22
  • 25