第十三章 HOOK技术
13.1 Hook概述
IAT HOOK(改地址)
BOOL IAT_InstallHook(){BOOL bResult = FALSE ;HMODULE hCurExe = GetModuleHandle(NULL);PULONG_PTR pt ;ULONG_PTR OrginalAddr;bResult = InstallModuleIATHook(hCurExe,"user32.dll","MessageBoxA",(PVOID)My_MessageBoxA,&pt,&OrginalAddr);if (bResult){printf("[*]Hook安装完毕! pThunk=0x%p OriginalAddr = 0x%p\n",pt,OrginalAddr);g_PointerToIATThunk = pt ;OldMessageBox = (PFN_MessageBoxA)OrginalAddr ;}return bResult;}//************************************// FullName: InstallModuleIATHook// Description: 为指定模块安装IAT Hook// Access: public// Returns: BOOL// Parameter: HMODULE hModToHook , 待Hook的模块基址// Parameter: char * szModuleName , 目标函数所在模块的名字// Parameter: char * szFuncName , 目标函数的名字// Parameter: PVOID DetourFunc , Detour函数地址// Parameter: PULONG * pThunkPointer , 用以接收指向修改的位置的指针// Parameter: ULONG * pOriginalFuncAddr , 用以接收原始函数地址//************************************BOOL InstallModuleIATHook(HMODULE hModToHook,// 要钩取的模块的句柄char *szModuleName,// 要钩取的模块的名称char *szFuncName,// 要钩取的函数的名称PVOID DetourFunc,// 替换原始函数的钩子函数的地址PULONG_PTR *pThunkPointer,// 用于存储指向导入地址的指针的指针ULONG_PTR *pOriginalFuncAddr// 用于存储原始函数地址的指针){PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor; // 指向导入描述符表的指针PIMAGE_THUNK_DATA pThunkData; // 指向导入函数表的指针ULONG ulSize; // 导入描述符表的大小HMODULE hModule=0; // 加载模块的句柄ULONG_PTR TargetFunAddr; // 目标函数的地址PULONG_PTR lpAddr; // 导入地址的指针char *szModName; // 当前模块的名称BOOL result = FALSE ; // 返回值,默认为失败BOOL bRetn = FALSE; // 操作结果,默认为失败hModule = LoadLibrary(szModuleName); // 加载要钩取的模块TargetFunAddr = (ULONG_PTR)GetProcAddress(hModule,szFuncName); // 获取要钩取的函数的地址printf("[*]Address of %s:0x%p\n",szFuncName,TargetFunAddr); // 输出目标函数的地址printf("[*]Module To Hook at Base:0x%p\n",hModToHook); // 输出要钩取的模块的基地址pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModToHook, TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize); // 获取导入描述符表的地址printf("[*]Find ImportTable,Address:0x%p\n",pImportDescriptor); // 输出导入描述符表的地址while (pImportDescriptor->FirstThunk) // 遍历导入描述符表,直到遇到空描述符{szModName = (char*)((PBYTE)hModToHook+pImportDescriptor->Name) ; // 获取当前模块的名称printf("[*]Cur Module Name:%s\n",szModName); // 输出当前模块的名称if (stricmp(szModName,szModuleName) != 0) // 若当前模块名称与要钩取的模块名称不匹配{printf("[*]Module Name does not match, search next...\n"); // 输出模块名称不匹配的信息pImportDescriptor++; // 继续下一个导入描述符表continue; // 继续下一次循环}// 程序的导入表处理完毕后OriginalFirstThunk可能是无效的,不能再根据名称来查找,而是遍历FirstThunk直接根据地址判断pThunkData = (PIMAGE_THUNK_DATA)((BYTE *)hModToHook + pImportDescriptor->FirstThunk); // 获取导入函数表的地址while(pThunkData->u1.Function) // 遍历导入函数表,直到遇到空条目{lpAddr = (ULONG_PTR*)pThunkData; // 获取导入地址的指针// 找到了地址if((*lpAddr) == TargetFunAddr) // 如果导入地址与目标函数地址相等{printf("[*]Find target address!\n"); // 输出找到目标地址的信息// 通常情况下导入表所在内存页都是只读的,因此需要先修改内存页的属性为可写DWORD dwOldProtect; // 旧的保护属性MEMORY_BASIC_INFORMATION mbi; // 内存页信息结构体VirtualQuery(lpAddr,&mbi,sizeof(mbi)); // 获取内存页的信息bRetn = VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dwOldProtect); // 修改内存页的保护属性为可写if (bRetn) // 如果修改成功{// 内存页属性修改成功,继续下一步操作,先保存原始数据if (pThunkPointer != NULL) // 如果指针不为空{*pThunkPointer = lpAddr ; // 将导入地址的指针赋值给pThunkPointer}if (pOriginalFuncAddr != NULL) // 如果指针不为空{*pOriginalFuncAddr = *lpAddr ; // 将导入地址的值赋值给pOriginalFuncAddr}// 修改地址*lpAddr = (ULONG_PTR)DetourFunc; // 将导入地址的值修改为钩子函数的地址result = TRUE ; // 操作成功// 恢复内存页的属性VirtualProtect(mbi.BaseAddress,mbi.RegionSize,dwOldProtect,0); // 恢复内存页的保护属性printf("[*]Hook ok.\n"); // 输出钩子成功的信息}break; // 跳出循环}//---------pThunkData++; // 继续下一个导入函数表条目}pImportDescriptor++; // 继续下一个导入描述符表}FreeLibrary(hModule); // 释放加载的模块return result; // 返回操作结果}
InLine Hook(改内容)
13.2 HOOK的分类
名目繁多的 Hook,总结起来其实只有两种:Address Hook和Inline Hook。
Address Hook:IAT、EAT、user32.dll的回调函数表、IDT(中断描述符表)、SSDT(系统服务描述符表)、C++类的虚函数表、COM接口的功能函数表、处理例程地址、特殊寄存器中的地址、特定的函数指针
Inline Hook:
基于异常处理的HOOK
13.3 HOOK位置的挑选
影响最小的 Hook:应用程序中的call Hook,可精确到特定位置对特定 API的调用。
影响最大的Hook:在系统内核中,大部分Hook的位置都会影响整个系统的调用过程,越往下就越明显。
在内核中,KiFastCallEnlry和KeServiceDescriptorTable(含 Shadow)是两个绝佳的Hook位置。
13.4 HOOK的典型过程
Address Hook 的实施过程:定义Detour()函数、定义函数指针、查表(遍历匹配)替换原函数地址、关闭写保护、写入Detour()函数的地址
Inline Hook 的实施过程:确定 Hook方式及需要在Trampoline 中执行的指令、准备TrampolineFun函数、准备jmp指令并写入、CALLL HOOK
二次HOOK
13.5 Detour函数的典型用法
检查参数、检查结果、拦截调用或下发
13.6 HOOK中的注意事项
多线程安全、保存和恢复现场、注意返回值、避免重入
13.7 HOOK在X64平台上的新问题
指针的定义与操作、内存地址对齐、PE格式、调用约定的变化、跳转指令的问题、PatchGuard问题
13.8 HOOK技术的应用
实现增强的二次开发或补丁、信息截获、安全防护
13.9 HOOK的检测、恢复与对抗