【加密与解密(第四版)】第十三章笔记

第十三章 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的检测、恢复与对抗

上一篇:【Qt】初识


下一篇:替换 postman?试试这款 32.5k star 的项目吧-项目简介