InlineHook 是一种计算机安全编程技术,其原理是在计算机程序执行期间进行拦截、修改、增强现有函数功能。它使用钩子函数(也可以称为回调函数)来截获程序执行的各种事件,并在事件发生前或后进行自定义处理,从而控制或增强程序行为。Hook技术常被用于系统加速、功能增强、开发等领域。本章将重点讲解Hook是如何实现的,并手动封装实现自己的Hook挂钩模板。

x32 Inline Hook

对于4.1中所提到的Hook方法还是过于复杂,我们可以将上述代码定义为MyHook类,构造函数用来初始化,析构函数用来恢复钩子,在Hook()成员函数中完成了3项工作,首先是获得了被HOOK函数的函数地址,接下来是保存了被HOOK函数的前5字节,最后是用构造好的跳转指令来修改被HOOK函数的前5字节的内容。

如下封装中实现了三个类内函数,其中Hook()用于开始Hook函数,此函数接收三个参数,参数1为需要Hook的动态链接库名,参数2为需要挂钩的函数名,参数3为自定以中转函数地址,其中UnHook()用于恢复函数挂钩,最后的ReHook()用于重新挂钩,以下是该类提供的功能的简要摘要:

  • m_pfnRig:成员变量,在挂接之前存储原始函数地址。
  • m_bOldBytes:成员变量,用于存储函数入口代码的原始5个字节。
  • m_bNewBytes:成员变量,用于存储将替换原始函数代码的内联钩子代码。
  • Hook():成员函数,通过将函数入口代码的前5个字节替换为JMP指令,将控制流重定向到指定的钩子函数,从而在指定的模块中钩子指定的函数。此函数返回一个BOOL,指示挂钩是否成功。
  • UnHook():成员函数,用于删除钩子并恢复原始函数代码。此函数返回一个BOOL,指示解除挂钩是否成功。
  • ReHook():成员函数,它使用之前存储的钩子代码重新钩子之前未钩子的函数。此函数返回一个BOOL,指示重新挂钩是否成功。
// 自己封装的Hook组件
class MyHook
{
public:
PROC m_pfnOrig; // 保存函数地址
BYTE m_bOldBytes[5]; // 保存函数入口代码
BYTE m_bNewBytes[5]; // 保存Inlie Hook代码
public: // 构造函数
MyHook()
{
m_pfnOrig = NULL;
ZeroMemory(m_bOldBytes, 5);
ZeroMemory(m_bNewBytes, 5);
} // 析构函数清理现场
~MyHook()
{
UnHook();
m_pfnOrig = NULL;
ZeroMemory(m_bOldBytes, 5);
ZeroMemory(m_bNewBytes, 5);
} // 开始挂钩
BOOL Hook(LPSTR pszModuleName, LPSTR pszFuncName, PROC pfnHookFunc)
{
BOOL bRet = FALSE; // 获取指定模块中函数的地址
m_pfnOrig = (PROC)GetProcAddress(GetModuleHandleA(pszModuleName), pszFuncName); if (m_pfnOrig != NULL)
{
// 保存该地址处 5 字节的内容
DWORD dwNum = 0;
ReadProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bOldBytes, 5, &dwNum); // 构造 JMP 指令
m_bNewBytes[0] = '\xe9'; // jmp Opcode // pfnHookFunc 是 HOOK 后的目标地址
// m_pfnOrig 是原来的地址
// 5 是指令长度
*(DWORD*)(m_bNewBytes + 1) = (DWORD)pfnHookFunc - (DWORD)m_pfnOrig - 5; // 将构造好的地址写入该地址处
WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bNewBytes, 5, &dwNum);
bRet = TRUE;
}
return bRet;
} // 取消挂钩
BOOL UnHook()
{
if (m_pfnOrig != 0)
{
DWORD dwNum = 0;
WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bOldBytes, 5, &dwNum);
}
return TRUE;
} // 重新挂钩
BOOL ReHook()
{
BOOL bRet = FALSE;
if (m_pfnOrig != 0)
{
DWORD dwNum = 0;
WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bNewBytes, 5, &dwNum);
bRet = TRUE;
}
return bRet;
}
};

同样我们以替换自身弹窗为例子具体讲解一下该库如何使用,如下代码中首先我们自定义一个MyMessageBoxA函数,该函数的原型读者可通过微软官方文档获取,以下是MessageBoxA函数的原型(函数声明):

int MessageBoxA(
HWND hWnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType
);

其中,参数的含义如下:

  • hWnd: 指向包含消息框的窗口的句柄,通常为父窗口的句柄。可以设为NULL,表示没有父窗口。
  • lpText: 指向要显示的消息文本的字符串指针。
  • lpCaption: 指向要显示在消息框标题栏上的字符串指针,通常用于指定消息框的标题。
  • uType: 用于指定消息框的按钮和图标样式,可以使用预定义的常量值进行设置,如MB_OK、MB_YESNO等。

有了函数原型声明部分读者则可以自己实现一个MyMessageBoxA函数,需注意参数传递必须与原函数保持一致,在自定以函数内部我们首先通过MsgHook.UnHook();恢复之前的钩子,并调用原函数实现功能替换,当调用结束后记得使用MsgHook.ReHook();重新挂钩恢复钩子。

// 定义全局类
MyHook MsgHook; // 定义自定义Hook函数
int WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
// 先来恢复Hook 之所以要恢复是因为我们需要调用原始的MsgBox弹窗
MsgHook.UnHook(); MessageBoxA(hWnd, "hi hook api", lpCaption, uType); // 弹窗完成后重新Hook
MsgHook.ReHook();
return 0;
}

在主函数中我们通过调用MsgHook.Hook()函数,挂钩住user32.dll模块内的MessageBoxA函数,并将该函数请求转发到MyMessageBoxA上面做处理,当此时调用MessageBoxA时读者可观察弹出提示是否为我们所期望的,最后通过MsgHook.UnHook();用于解除钩子;

// 调用Hook组件
int main(int argc, char* argv[])
{
// 开始Hook
MsgHook.Hook((char *)"user32.dll", (char *)"MessageBoxA", (PROC)MyMessageBoxA); // 调用函数
MessageBoxA(NULL, "hello lyshark", "Msg", MB_OK); // 结束Hook
MsgHook.UnHook();
return 0;
}

读者可自行运行上述代码,当尝试调用MessageBox函数并传入hello lyshark参数时,输出的结果却变成了hi hook api如下图所示,则说明内联挂钩生效了。

x64 Inline Hook

32位钩子的封装实现详细读者已经能够理解了,接着我们来实现64位钩子的封装,64位与32位系统之间无论从寻址方式,还是语法规则都与x32架构有着本质的不同,由于64位编译器无法直接内嵌汇编代码,导致我们只能调用C库函数内嵌机器码来实现Hook的中转。

首先实现去MessageBox弹窗,由于64位编译器无法直接内嵌汇编代码,所以在我们需要Hook时只能将跳转机器码以二进制字节方式写死在程序里,如下代码是一段去弹窗演示案例。

#include <stdio.h>
#include <Windows.h> BYTE OldCode[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };
DWORD_PTR base; // 自己实现中转函数
int WINAPI MyMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
return 1;
} int main(int argc,char * argv[])
{
HMODULE hwnd = GetModuleHandle(TEXT("user32.dll"));
DWORD_PTR base = (DWORD_PTR)GetProcAddress(hwnd, "MessageBoxW");
DWORD OldProtect; if (VirtualProtect((LPVOID)base, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
{
memcpy(OldCode, (LPVOID)base, 12); // 拷贝原始机器码指令
*(PINT64)(HookCode + 2) = (INT64)&MyMessageBoxW; // 填充90为指定跳转地址
}
memcpy((LPVOID)base, &HookCode, sizeof(HookCode)); // 拷贝Hook机器指令 MessageBoxW(NULL, L"hello lyshark", NULL, NULL); return 0;
}

接着我们在上面代码的基础上继续进行完善,添加恢复钩子的功能,该功能时必须要有的,因为我们还是需要调用原始的弹窗代码,所以要在调用时进行暂时恢复,调用结束后再继续Hook挂钩。

#include <stdio.h>
#include <Windows.h> void Hook();
void UnHook(); BYTE Ori_Code[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 }; /* Hook 机器码的原理如下所示
MOV RAX, 0x9090909090909090
JMP RAX
*/ // 定义函数指针
static int (WINAPI *OldMessageBoxW)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) = MessageBoxW; // 执行自己的弹窗
int WINAPI MyMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
UnHook(); // 恢复Hook
int ret = OldMessageBoxW(hWnd, TEXT("Hook Inject"), lpCaption, uType); // 调用原函数
Hook(); // 继续hook
return ret;
} // 开始Hook
void Hook()
{
DWORD OldProtect;
if (VirtualProtect(OldMessageBoxW, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
{
memcpy(Ori_Code, OldMessageBoxW, 12); // 拷贝原始机器码指令
*(PINT64)(HookCode + 2) = (INT64)&MyMessageBoxW; // 填充90为指定跳转地址
}
memcpy(OldMessageBoxW, &HookCode, sizeof(HookCode)); // 拷贝Hook机器指令
} // 结束Hook
void UnHook()
{
memcpy(OldMessageBoxW, &Ori_Code, sizeof(Ori_Code)); // 恢复hook原始代码
} int main(int argc, char *argv [])
{
Hook();
MessageBoxW(NULL, TEXT("hello lyshark"), TEXT("MsgBox"), MB_OK);
UnHook();
MessageBoxW(NULL, TEXT("hello lyshark"), TEXT("MsgBox"), MB_OK); return 0;
}

将上面所写的代码进行函数化封装,实现一个完整的钩子处理程序。代码如下所示。

#include <stdio.h>
#include <Windows.h> BYTE OldCode[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 }; void Hook(LPCWSTR lpModule, LPCSTR lpFuncName, LPVOID lpFunction)
{
DWORD_PTR FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
DWORD OldProtect = 0; if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
{
memcpy(OldCode, (LPVOID)FuncAddress, 12); // 拷贝原始机器码指令
*(PINT64)(HookCode + 2) = (UINT64)lpFunction; // 填充90为指定跳转地址
}
memcpy((LPVOID)FuncAddress, &HookCode, sizeof(HookCode)); // 拷贝Hook机器指令
VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
} void UnHook(LPCWSTR lpModule, LPCSTR lpFuncName)
{
DWORD OldProtect = 0;
UINT64 FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
{
memcpy((LPVOID)FuncAddress, OldCode, sizeof(OldCode));
}
VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
} int WINAPI MyMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
// 首先恢复Hook代码
UnHook(L"user32.dll", "MessageBoxW");
int ret = MessageBoxW(0, L"hello lyshark", lpCaption, uType); // 调用结束后,再次挂钩
Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
return ret;
} bool APIENTRY DllMain(HANDLE handle, DWORD dword, LPVOID lpvoid)
{
switch (dword)
{
case DLL_PROCESS_ATTACH:
Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
break;
case DLL_PROCESS_DETACH:
UnHook(L"user32.dll", "MessageBoxW");
break;
}
return true;
}

上方的代码还是基于过程化的案例,为了能更加通用,我们将其封装成MyHook类,这样后期可以直接引入项目调用了。

#include <iostream>
#include <Windows.h> class MyHook
{
public:
FARPROC m_pfnOrig; // 保存函数地址
BYTE m_bOldBytes[12]; // 保存函数入口代码
BYTE m_bNewBytes[12]; // 保存Inlie Hook代码
public: // 构造函数
MyHook()
{
BYTE OldCode[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE NewCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 }; RtlMoveMemory(MyHook::m_bNewBytes, NewCode, 12);
memset(MyHook::m_bOldBytes, 0, 12);
m_pfnOrig = NULL;
} // 析构函数
~MyHook()
{
m_pfnOrig = NULL;
ZeroMemory(MyHook::m_bNewBytes, 12);
ZeroMemory(MyHook::m_bOldBytes, 12);
} // 开始挂钩
BOOL Hook(LPCWSTR lpModule, LPCSTR lpFuncName, LPVOID lpFunction)
{
DWORD_PTR FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
DWORD OldProtect = 0; if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
{
memcpy(m_bOldBytes, (LPVOID)FuncAddress, 12); // 拷贝原始机器码指令
*(PINT64)(MyHook::m_bNewBytes + 2) = (UINT64)lpFunction; // 填充90为指定跳转地址
}
memcpy((LPVOID)FuncAddress, &m_bNewBytes, sizeof(m_bNewBytes)); // 拷贝Hook机器指令
VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
return TRUE;
} // 结束挂钩
BOOL UnHook(LPCWSTR lpModule, LPCSTR lpFuncName)
{
DWORD OldProtect = 0;
UINT64 FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
{
memcpy((LPVOID)FuncAddress, m_bOldBytes, sizeof(m_bOldBytes));
}
VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
return TRUE;
}
}; // 定义全局类
MyHook MsgHook; // 自己实现中转函数
int WINAPI MyMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
// 首先恢复Hook代码
MsgHook.UnHook(L"user32.dll", "MessageBoxW"); int ret = MessageBoxW(0, L"hi hook api", lpCaption, uType); // 调用结束后,再次挂钩
MsgHook.Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
return ret;
} int main(int argc, char* argv[])
{
// 开始Hook
MsgHook.Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW); MessageBoxW(NULL, L"hello lyshark", L"Msg", MB_OK); // 结束Hook
MsgHook.UnHook(L"user32.dll", "MessageBoxW"); return 0;
}

读者可自行运行上述代码,当尝试调用MessageBox函数并传入hello lyshark参数时,输出的结果却变成了hi hook api如下图所示,则说明内联挂钩生效了。

4.2 Inline Hook 挂钩技术的更多相关文章

  1. Inline Hook

    @author: dlive IAT Hook时如果要钩取的API不在IAT中(LoadLibrary后调用),则无法使用该技术.而Inline Hook不存在这个限制. 0x01 Inline Ho ...

  2. 在已有软件加壳保护 下实现 Inline hook

    如写的不好请见谅,本人水平有限. 个人简历及水平:. http://www.cnblogs.com/hackdragon/p/3662599.html 正常情况: 接到一个项目实现对屏幕输出内容的获取 ...

  3. Inline Hook 钩子编写技巧

    Hook 技术通常被称为钩子技术,Hook技术是Windows系统用于替代中断机制的具体实现,钩子的含义就是在程序还没有调用系统函数之前,钩子捕获调用消息并获得控制权,在执行系统调用之前执行自身程序, ...

  4. x86平台inline hook原理和实现

    概念 inline hook是一种通过修改机器码的方式来实现hook的技术. 原理 对于正常执行的程序,它的函数调用流程大概是这样的: 0x1000地址的call指令执行后跳转到0x3000地址处执行 ...

  5. 转移指令原理和Inline Hook

    目录 转移指令原理和Inline Hook 转移指令 操作符offset jmp指令 根据位移进行转移的jmp指令 插播HOOK知识 Inline Hook Inline Hook 原理 Hook代码 ...

  6. inline hook原理和实现

    inline hook是通过修改函数执行指令来达到挂钩的.比如A要调用B,但人为地修改执行流程导致A调用了C,C在完成了自己的功能后,返回B再执行. 修改这段指令前首先要获取修改权限 由于要修改的代码 ...

  7. Windows下x86和x64平台的Inline Hook介绍

    前言 我在之前研究文明6的联网机制并试图用Hook技术来拦截socket函数的时候,熟悉了简单的Inline Hook方法,但是由于之前的方法存在缺陷,所以进行了深入的研究,总结出了一些有关Windo ...

  8. 对付ring0 inline hook

    对付ring0 inline hook的基本思路是这样的,自己写一个替换的内核函数,以NtOpenProcess为例,就是MyNtOpenProcess.然后修改SSDT表,让系统服务进入自己的函数M ...

  9. Inline Hook NtQueryDirectoryFile

    Inline Hook NtQueryDirectoryFile 首先声明这个是菜鸟—我的学习日记,不是什么高深文章,高手们慎看. 都总是发一些已经过时的文章真不好意思,几个月以来沉迷于游戏也是时候反 ...

  10. android inline hook

    最近终于沉下心来对着书把hook跟注入方面的代码敲了一遍,打算写几个博客把它们记录下来. 第一次介绍一下我感觉难度最大的inline hook,实现代码参考了腾讯GAD的游戏安全入门. inline ...

随机推荐

  1. 数据工程师必备的8项技能,不要只知道Python!

    欢迎关注公众号:机器学习算法与Python实战(ID:tjxj666) 原作:Mohammed M Jubapu 译者:机器学习算法与Python实战(公众号ID:tjxj666) 英文:https: ...

  2. RPC 框架性能测试,注意这 8 点就够了

    某天,二狗子写了一个 RPC 框架后,简单测了一下性能,发现超出 grpc 一大截.二狗子一高兴,忍不住找同事吹了一波.结果,同事亲测后对二狗子说框架性能也就这样.二狗子表示不服,跟同事一番唇枪舌剑后 ...

  3. 如何向已有的项目中添加C/C++代码?

    第一步: 我们需要在src/main下面建立一个cpp目录,然后在其中写一个CMakeLists.txt文件和一个cpp文件,直接给出代码: #CMakeLists.txt文件# For more i ...

  4. Codeforces Round #674 (Div. 3) (A - F题题解)

    A. Floor Number https://codeforces.com/contest/1426/problem/A 题意: 一个楼房房间号由 \(1\) 递增,一楼仅2个房间.给定一位用户的房 ...

  5. Centos7 kubeadm安装k8s

    安装环境准备 关闭防火墙 systemctl stop firewalld systemctl disable firewalld 关闭selinux sed -i 's/enforcing/disa ...

  6. 【收藏】Stable Diffusion 制作光影文字效果

    大家对于最近 Stable Diffusion 不断出新的视觉"整活"印象都很深刻,很多人对最近比较流行的制作光影文字很感兴趣,制作光影文字可以作为进阶 Stable Diffus ...

  7. vue tabBar导航栏设计实现5-最终版本

    系列导航 一.vue tabBar导航栏设计实现1-初步设计 二.vue tabBar导航栏设计实现2-抽取tab-bar 三.vue tabBar导航栏设计实现3-进一步抽取tab-item 四.v ...

  8. <vue 基础知识 2、插值语法> v-once,v-html,v-text,v-pre,v-cloak

    代码结构 一.     Mustache 1.效果 展示如何将数据展示在页面上 2.代码 01-Mustache.html <!DOCTYPE html> <html lang=&q ...

  9. Zookeeper 的 ZAB 协议 以及 zookeeper 与 nacos 注册中心比对

    本文为博主原创,未经允许不得转载: 目录: 1. ZAB 协议 2. zookeer 节点状态 3. zookeeper 注册中心与 nacos 注册中心比较 4. zookeeper 配置注册中心 ...

  10. Blazor的技术优点

    Blazor是一种使用.NET和C#构建客户端Web应用程序的新兴技术.它允许开发者在浏览器中直接运行.NET代码,而无需依赖JavaScript.Blazor的技术优点主要表现在以下几个方面: 单一 ...