钩子编程

钩子编程（hooking），也称作“挂钩”，是计算机程序设计术语，指通过拦截软件模块间的函数调用、消息传递、事件传递来修改或扩展操作系统、应用程序或其他软件组件的行为的各种技术。处理被拦截的函数调用、事件、消息的代码，被称为钩子（hook）。
钩子编程有多种用途，如调试、扩展功能。例如在键盘或鼠标事件到达应用程序之前拦截这些事件；拦截应用程序或其他模块的操作系统调用以监督行为、修改功能。也广泛用于benchmarking程序，如度量3D游戏的帧率。 
钩子编程也被用于恶意代码，如rootkit是各种通过假扮系统API调用输出来隐藏自身进程的可见性的工具与技术；游戏外挂是另一类例子。
方法.
物理修改.
在应用程序执行之前，物理修改可执行程序，这典型通过找到函数调用入口点，修改入口点使之在函数被执行前先执行其他的代码。另一种挂钩的方法是修改可执行程序的输入表（import table）。还有一种挂钩方法是采用包装库，使得应用程序不需修改即可调用该包装库完成其功能，而在包装库中插入钩子然后再调用原有的库。 
运行时修改.
操作系统与软件可提供方法，在运行时插入事件钩子。Microsoft Windows允许插入钩子以处理或修改对话框、滚动条、菜单等的系统事件、应用程序事件；插入、删除、处理或修改键盘鼠标事件。Linux允许类似的钩子通过NetFilter以处理网络事件。
如果没有上述机制或权限，也可拦截进程的库函数调用，在函数调用开始处注入代码。这可通过修改内存中的进程的中断向量表或输入表（import table）实现。
示例代码.
虚函数表挂钩.
C++使用虚函数，因此可在运行时直接修改虚函数表的内容来挂钩。 Window上很多软件库以COM方式提供的（如DirectX）， 所以有需求拦截COM调用的COM Hook。COM里的接口是C++虚表的形式提供的，所以COM的Hook其实就是虚表（vtable）的Hook。ATL就是用接口替代的方式来调试和记录COM接口引用计数的次数
<syntaxhighlight lang="cpp">
class VirtualTable { // example class 
};
void VirtualTable::VirtualFunction01( ticket ) { 
typedef void ( __thiscall* VirtualFunction01_t )( ticket* thisptr ); 
VirtualFunction01_t g_org_VirtualFunction01;
void __fastcall hk_VirtualFunction01( ticket* thisptr, int edx ) { 
int _tmain(int argc, _TCHAR* argv[]) {
</syntaxhighlight>
C#键盘事件钩子.
<syntaxhighlight lang="csharp">
using System.Runtime.InteropServices;
namespace Hooks
</syntaxhighlight>
使用跳转指令的API/函数钩子拦截.
下述例子使用指令，修改windows API中的MessageBoxW函数的前6个字节执行其他代码。这段代码编译为DLL文件，采用DLL注入技术让应用程序使用。微软提供了封装好的Detours库用于此目的。
<syntaxhighlight lang="c">
</syntaxhighlight>
Netfilter钩子.
使用Netfilter钩子修改Linux内核的网络交通。
<syntaxhighlight lang="c">
/* Port we want to drop packets on */
static const uint16_t port = 25;
/* This is the hook function itself */
static unsigned int hook_func(unsigned int hooknum,
/* Used to register our hook function */
static struct nf_hook_ops nfho = {
static __init int my_init(void)
static __exit void my_exit(void)
module_init(my_init);
module_exit(my_exit);
</syntaxhighlight>
内部IAT钩子.
下例展示通过修改可执行程序的输入地址表（IAT），把钩子函数替代原函数。 
<syntaxhighlight lang="c">
typedef int(__stdcall *pMessageBoxA) (HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType); //This is the 'type' of the MessageBoxA call.
pMessageBoxA RealMessageBoxA; //This will store a pointer to the original function.
void DetourIATptr(const char* function, void* newfunction, HMODULE module);
int __stdcall NewMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) { //Our fake function
int main(int argc, CHAR *argv[]) {
void **IATfind(const char *function, HMODULE module) { //Find the IAT (Import Address Table) entry specific to the given function.
 int ip = 0;
 if (module == 0)
 module = GetModuleHandle(0);
 PIMAGE_DOS_HEADER pImgDosHeaders = (PIMAGE_DOS_HEADER)module;
 PIMAGE_NT_HEADERS pImgNTHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)pImgDosHeaders + pImgDosHeaders->e_lfanew);
 PIMAGE_IMPORT_DESCRIPTOR pImgImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((LPBYTE)pImgDosHeaders + pImgNTHeaders->OptionalHeader.DataDirectory.VirtualAddress);
 if (pImgDosHeaders->e_magic != IMAGE_DOS_SIGNATURE)
 printf("libPE Error : e_magic is no valid DOS signature\n");
 for (IMAGE_IMPORT_DESCRIPTOR *iid = pImgImportDesc; iid->Name != NULL; iid++) {
 for (int funcIdx = 0; *(funcIdx + (LPVOID*)(iid->FirstThunk + (SIZE_T)module)) != NULL; funcIdx++) {
 char *modFuncName = (char*)(*(funcIdx + (SIZE_T*)(iid->OriginalFirstThunk + (SIZE_T)module)) + (SIZE_T)module + 2);
 const uintptr_t nModFuncName = (uintptr_t)modFuncName;
 bool isString = !(nModFuncName & (sizeof(nModFuncName) == 4 ? 0x80000000 : 0x8000000000000000));
 if (isString) {
 if (!_stricmp(function, modFuncName))
 return funcIdx + (LPVOID*)(iid->FirstThunk + (SIZE_T)module);
 return 0;
void DetourIATptr(const char *function, void *newfunction, HMODULE module) {
 void **funcptr = IATfind(function, module);
 if (*funcptr == newfunction)
 return;
 DWORD oldrights, newrights = PAGE_READWRITE;
 //Update the protection to READWRITE
 VirtualProtect(funcptr, sizeof(LPVOID), newrights, &oldrights);
 RealMessageBoxA = (pMessageBoxA)*funcptr; //Some compilers require the cast like "MinGW" not sure about MSVC
 *funcptr = newfunction;
 //Restore the old memory protection flags.
 VirtualProtect(funcptr, sizeof(LPVOID), oldrights, &newrights);
</syntaxhighlight>