1. Hook的基本原理
Windows 钩子的本质是一段用以处理系统消息的程序,通过系统调用,将其挂入到系统。钩子的种类有很多,每一种钩子负责截获并处理相应的消息。钩子机制允许应用程序截获并处理发往指定窗口的消息或特定事件,其监视的窗口即可以是本进程内的也可以是由其他进程所创建的。在特定的消息发出,并在到达目的窗口之前,钩子程序先行截获此消息并得到对其的控制权。此时在钩子函数中就可以对截获的消息进行各种修改处理,甚至强行终止该消息的继续传递。
Detours库是Microsoft研究院的一个开发库,最新版本为3.0。它可以拦截任意的API调用,拦截代码是在动态运行时加载的,替换目标API最前面的几条指令,使其无条件的跳转到用户提供的拦截函数。
2. Hook神器Detours
2.1 Detours基本原理
2.1.1 Win32进程的内存管理
进程要执行的指令也放在虚存空间中
可以使用QueryProtectEx函数把存放指令的页面的权限更改为可读可写可执行,再改写其内容,从而修改正在运行的程序
可以使用VirtualAllocEx从一个进程为另一正运行的进程分配虚存,再使用 QueryProtectEx函数把页面的权限更改为可读可写可执行,并把要执行的指令以二进制机器码的形式写入,从而为一个正在运行的进程注入任意的代码
2.1.2 拦截WIN32 API的原理
Target函数:要拦截的函数,通常为Windows的API。
Trampoline函数:Target函数的复制品。因为Detours将会改写Target函数,所以先把Target函数复制保存好,一方面仍然保存Target函数的过程调用语义,另一方面便于以后的恢复。
Detour函数:用来替代Target函数的函数
2.1.3 为进程注入DLL
创建一个ThreadFuction,内容仅是调用LoadLibrary。
用VirtualAllocEx为一个已在运行的进程分配一片虚存,并把权限更改为可读可写可执行。
把ThreadFuction的二进制机器码写入这片虚存。
用CreateRemoteThread在该进程上创建一个线程,传入前面分配的虚存的起始地址作为线程函数的地址,即可为一个已在运行的进程装入一个DLL。通过DllMain 即可在一个已在运行的进程中运行自己的代码
2.2 下载地址
https://github.com/microsoft/detours
3. Hook实验
3.1 基本配置
x64 Native Tools Command Prompt for VS 2019 和 x86 Native Tools Command Prompt for VS 2019,这两个可以分别用来编译64位和32位的Detours
进入到/Detours-4.0.1/src目录:
然后使用nmake命令进行编辑:
nmake /f Makefile
编译之后生成下面的文件:
创建项目之后,设置项目属性,其中:
include添加包含目录
lib.X64添加到库目录
3.2 Hook代码
完整的C++代码如下所示:
#include <iostream>
#include "windows.h"
#include "detours.h"
#pragma comment(lib,"detours.lib")
int (WINAPI* Old_MessageBoxA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) = MessageBoxA;
int WINAPI New_MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
return Old_MessageBoxA(NULL, "Hooked MessageBoxA", "Hooked MessageBoxA", NULL);
}
void Hook()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)Old_MessageBoxA, New_MessageBoxA);//Hook 相关函数
DetourTransactionCommit();
}
void DeHook()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)Old_MessageBoxA, New_MessageBoxA);//Hook 相关函数
DetourTransactionCommit();
}
int main()
{
std::cout << "Hello World!\n";
MessageBoxA(NULL, "Hello World!", "MessageBoxA", NULL);
Hook();
MessageBoxA(NULL, "Hello World!", "MessageBoxA", NULL);
DeHook();
MessageBoxA(NULL, "Hello World!", "MessageBoxA", NULL);
}
3.3 代码拆解
首先先声明要hook的两个函数的函数指针,这里是MessageBoxA和MessageBoxW:
int (WINAPI* Old_MessageBoxA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) = MessageBoxA;
然后创建Hook后要对上述两个函数进行的操作
int WINAPI New_MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
return Old_MessageBoxA(NULL, "Hooked MessageBoxA", "Hooked MessageBoxA", NULL);
}
编写相应的Hook操作:
void Hook()
{
DetourTransactionBegin(); //开始Hook拦截
DetourUpdateThread(GetCurrentThread()); //刷新当前线程
DetourAttach(&(PVOID&)Old_MessageBoxA, New_MessageBoxA); //Hook 相关函数
DetourTransactionCommit(); //拦截生效
}
编写相应的Hook取消操作:
void DeHook()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)Old_MessageBoxA, New_MessageBoxA);//取消Hook 相关函数
DetourTransactionCommit();
}
最后,在main函数中进行测试:
int main()
{
std::cout << "Hello World!\n";
MessageBoxA(NULL, "Hello World!", "MessageBoxA", NULL); //未Hook输出
Hook();//开始Hook
MessageBoxA(NULL, "Hello World!", "MessageBoxA", NULL);
DeHook();//解除Hook
MessageBoxA(NULL, "Hello World!", "MessageBoxA", NULL);
}
为Hook弹出的弹窗分别为:
Hook后的弹窗分别为:
取消Hook的弹窗分别为:
4. referers
https://blog.csdn.net/allenq/article/details/8363363
https://blog.csdn.net/z971130192/article/details/100565010
https://www.bilibili.com/s/video/BV1ZA411t7Hy
https://www.cxyzjd.com/article/z971130192/100565398
https://blog.csdn.net/tutucoo/article/details/85235478