如何在 C/C++ 中挂钩 Windows 函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/873658/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:42:21  来源:igfitidea点击:

How can I hook Windows functions in C/C++?

c++cwindowshook

提问by Clark Gaebel

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?

如果我有一个foo()Windows 已经在 kernel32.dll 中实现的函数并且它总是返回 true,我可以让我的程序:“bar.exe”挂钩/绕道该 Windows 函数并让它为所有进程返回 false 吗?

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.

因此,例如,如果我的 svchost 调用foo(),它将返回 false 而不是 true。对于当前正在运行的所有其他进程,应该期望相同的操作。

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

如果是这样,如何?我想我正在寻找一个系统范围的钩子或其他东西。

回答by xian

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

看看Detours,它非常适合这类东西。



For system-wide hooking, read this articlefrom MSDN.

对于系统范围的挂钩,请阅读MSDN 上的这篇文章



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

首先,创建一个处理挂钩函数的 DLL。下面的这个例子钩住了套接字发送和接收函数。

#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.

然后,创建一个程序将 DLL 注入目标应用程序。

#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!

这应该足以让你开始!

回答by RandomNickName42

EASYHOOKhttp://www.codeplex.com/easyhook

EASYHOOK http://www.codeplex.com/easyhook

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

在简单性、灵活性和功能性方面支配所有上述技术。

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

之前也没有在Hook 进程中讨论过。我已经阅读了这个线程的所有叶子,并且绝对确定,EASYHOOK非常优越。无论您使用 C、C++、CLR,还是什么。

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

我将从 codeplex 主页粘贴一些内容,以确保支付足够的费用。

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 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 necessary.
  17. The Visual Studio Redistributable is not required.
  1. 所谓的“线程死锁屏障”会在hook未知API时摆脱许多核心问题;这项技术是 EasyHook 独有的
  2. 您可以为非托管 API 编写托管挂钩处理程序
  3. 您可以使用所有提供的便利托管代码,例如 NET Remoting、WPF 和 WCF
  4. 一个记录在案的纯非托管挂钩 API
  5. 支持 32 位和 64 位内核模式挂钩(另请查看我的 PatchGuard 3 旁路驱动程序,该驱动程序可在发布列表中找到)
  6. 目标中没有留下任何资源或内存泄漏
  7. 不会引起任何当前 AV 软件关注的实验性隐形注入机制
  8. EasyHook32.dll 和 EasyHook64.dll 是纯非托管模块,无需安装任何 NET 框架即可使用!
  9. 所有挂钩均以稳定的方式安装和自动移除
  10. 通过使用完全未公开的 API 支持 Windows Vista SP1 x64 和 Windows Server 2008 SP1 x64,仍然允许连接到任何终端会话。
  11. 钩子处理程序内的托管/非托管模块堆栈跟踪
  12. 在钩子处理程序中调用托管/非托管模块
  13. 在钩子处理程序中创建自定义堆栈跟踪
  14. 您将能够编写为 AnyCPU 编译的注入库和主机进程,这将允许您在所有情况下使用完全相同的程序集将代码从 64 位和 32 位进程注入到 32 位和 64 位进程中。
  15. EasyHook 支持 64 位目标的 RIP 相对寻址重定位。
  16. 无需开箱/安装。
  17. 不需要 Visual Studio Redistributable。

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.

我很高兴我的妓女仍然知道一些比较让我保留他们的技巧。但可以肯定的是,当您需要 HOOK 时,100 次的 99 次,EASYHOOK'r 会让您更快地到达那里。它得到了相当积极的维护。

回答by beef2k

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 articleto learn how.

  • You can find out where the specific function is called in foo.exeand 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 SetWindowsHookfor this.

  • 您可以构建一个与包含要挂钩的函数的 DLL 同名的假 DLL(并将其复制到 的文件夹中foo.exe)。该库将公开与原始 DLL 完全相同的函数。每个公开的函数只是绕过对原始 DLL 的调用,但要挂钩的函数除外。

  • 您可以在运行时更改函数指针表,例如使用“厨房”提到的(商业)Detour 包。但是,您自己可以轻松完成此类挂钩,请参阅本文以了解如何操作。

  • 您可以找出调用特定函数的位置foo.exe,只需将调用该函数的汇编代码替换为“返回true”代码即可。基本上,您正在修补“ foo.exe”..

  • 对于特定功能,Windows 提供了自动挂钩,例如用于按键和鼠标事件。为此检查函数SetWindowsHook

回答by mrduclaw

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.

这在某种程度上取决于您要定位的 Windows 版本。尽管如此,如果您在 Pre-Vista 上玩游戏,您可以简单地使用 SetWindowsHookEx 将您的 DLL 注入每个正在运行的进程中。然后,您的 DLL 需要使用 Detours 或类似方法来挂钩适当的函数。

回答by Stephen Kellett

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

如果您在汇编中编写钩子而不使用 Detours(无论出于何种原因),那么您需要一些有关返回 FALSE 的关键信息:

  • Win32, set EAX to 0
  • Win64, set RAX to 0
  • Win32,将 EAX 设置为 0
  • Win64,将 RAX 设置为 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).

您需要将 EAX 或 RAX(取决于平台)设置为零,这是您挂钩的函数所做的最后一件事。这将导致调用代码接收 0 作为返回值(假设它们返回的是 int 或指针类型值)。