来源:互联网转载 | 更新日期:2023-09-09 11:17:54
文章目录
- 一、Hook概述
- 1.1 什么是Hook
- 1.2 什么是APIHook
- 1.3 Hook过程理解
- 二、Hook分类
- 2.0 分类概述
- 2.1 Address Hook
- 2.2 Inline Hook
- 2.3 基于异常处理的Hook
- 三、MinHook库介绍
- 3.1 MinHook代码
- 3.2 文件目录介绍
- 3.3 解决方案描述
- 3.4 头文件定义中文说明:
- 四、Hook实现过程
- 4.1 步骤介绍
- 4.2注入进程的dll代码
- 4.3效果
- 4.4 拓展
- 五、结尾
一、Hook概述
1.1 什么是Hook
“Hook” 翻译过来的意思是“挂钩” “钩子”,在程序执行的时候,在适当的位置对程序运行流程进行监控、拦截即为Hook技术。
1.2 什么是APIHook
API Hook就是Hook技术的一种具体形式,实际上API Hook是改变API运行结果的一种技术手段,在Windows下进行开发就会用到Windows系统提供的API,对系统API进行拦截,API Hook是想要改变其他进程运行流程的常用技术之一。
1.3 Hook过程理解
举个例子,如果我们要去Hook Windows的API “MessageBoxA”,Hook前后的程序流程可如下图所示:
可以看到,Hook之后原程序想去调用本来的MessageBoxA,却最后调用到我们的MyMessageBoxA,在调用我们自身的MyMessageBoxA以后,在MyMessageBox函数中,可以选择是否再去调用原MessageBoxA函数。
通过上述方式,我们需要做的就是让程序在Call MessageBoxA地址的时候,最终转到的是我们的函数地址,整个过程就是一个API Hook的过程。
二、Hook分类
2.0 分类概述
Hook主要可以分为三类:Address Hook,Inline Hook, 基于异常处理的Hook,当然有些病毒的异常行为与Hook的理念是一样的,只是方式不同而已。
2.1 Address Hook
Address Hook是指通过修改数据进行Hook的方法,修改的数据一般是函数的地址或者地址偏移量。一半这种数据是存储在各类表或者结构中,也可能是某个指定地址或者特殊寄存器中。但所有数据修改都是在某个时刻成为程序执行过程中的eip。因此只要把地址替换成我们的事先准备的替代函数,就可以实现Hook。
2.2 Inline Hook
Inline Hook是指直接修改指令的Hook方式,思想是转移程序的执行流程,使其进行函数调用逻辑的变化,一般是使用jmp,call,retn之类的转移指令去实现。
2.3 基于异常处理的Hook
当程序执行过程中发生异常,系统内核的异常处理程序nt!KiDispatchException就会开始工作。没有内核调试器存在的情况下,系统会把异常处理过程交给用户态的异常处理过程。
也就是说我们在程序中安装异常处理过程,在Hook位置写入一条会引起异常的指令,然后让程序在执行到这异常位置时,跳转到我们事先安装的异常处理过中程,进而实现该位置的Hook。
三、MinHook库介绍
经过以上对Hook方式的简单介绍我们可以理解Hook究竟是什么,以及实现Hook的几种方式,详细的原理介绍在《加密与解密》第13章都有解释。不再过多的原理解释,实际上Git上有很多已经封装好的Hook实现代码,学会如何使用库比自己基于原理实现整个Hook过程简单很多。。
3.1 MinHook代码
git地址:MinHook
官方文档说明:MinHook - The Minimalistic x86/x64 API Hooking Library
MinHook就是通过Inline Hook实现的,通过生成库文件,在我们的项目中包含头文件以及对应库文件就可以实现Hook。
3.2 文件目录介绍
build目录:包含各种版本解决方案,可以选择对应的版本进行生成
include目录:需要包含到我们项目的头文件目录
每一个对应版本的解决方案目录介绍:
lib目录:生成对应的静态库目录,包含x64与x86
bin目录:生成对应的动态库目录,包含x64与x86
3.3 解决方案描述
可以直接生成第一个项目libMinHook,生成库文件,把需要的头文件MinHook.h包含到自己的项目中,需要注意的是根据需要Hook的进程是64位还是32位而选择对应生成库文件版本,否则Hook会不生效
生成第二个项目是直接可以生成静态库对应的dll,可以用于动态Loadlibrary,但是还是建议直接生成静态库直接生成到我们自己的项目中,因为我们自己的项目是需要生成一个注入目标进程的dll。
3.4 头文件定义中文说明:
/** MinHook - The Minimalistic API Hooking Library for x64/x86* Copyright (C) 2009-2017 Tsuda Kageyu.* All rights reserved.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:** 1. Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.* 2. Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in the* documentation and/or other materials provided with the distribution.** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/#pragma once#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__)#error MinHook supports only x86 and x64 systems.
#endif#include <windows.h>// MinHook 错误码.
typedef enum MH_STATUS
{// 未知错误,不应退出MH_UNKNOWN = -1,// 成功MH_OK = 0,// 已经初始化过MH_ERROR_ALREADY_INITIALIZED,// 没有初始化或者已经退出初始化MH_ERROR_NOT_INITIALIZED,// 已创建指定目标函数的挂钩MH_ERROR_ALREADY_CREATED,// 尚未创建指定目标函数的挂钩MH_ERROR_NOT_CREATED,// 指定目标函数的挂钩已启用MH_ERROR_ENABLED,// 尚未启用或已禁用指定目标函数的挂钩MH_ERROR_DISABLED,// 指定的指针无效。它指向未分配和/或不可执行区域的地址MH_ERROR_NOT_EXECUTABLE,// 无法挂接指定的目标函数MH_ERROR_UNSUPPORTED_FUNCTION,// 内存分配失败MH_ERROR_MEMORY_ALLOC,// 更改内存保护失败MH_ERROR_MEMORY_PROTECT,// 未加载指定的模块MH_ERROR_MODULE_NOT_FOUND,// 找不到指定的函数MH_ERROR_FUNCTION_NOT_FOUND
}
MH_STATUS;// 可以作为参数传递
#define MH_ALL_HOOKS NULL#ifdef __cplusplus
extern "C" {
#endif// 初始化MinHook库,在程序调用开始前必须初始化MH_STATUS WINAPI MH_Initialize(VOID);// 卸载MinHook库,在程序调用结束后必须卸载MH_STATUS WINAPI MH_Uninitialize(VOID);// 创建一个目标函数的钩子// Parameters:// pTarget [in] 指向原函数(目标函数)的指针// pDetour [in] 指向用来替代目标函数的函数指针// ppOriginal [out] 指向调用原函数入口的指针,此参数可为NULL MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal);// 创建一个目标API函数的钩子// Parameters:// pszModule [in] 目标函数所在的模块名// pszProcName [in] 目标函数名// pDetour [in] 指向用来替代目标函数的函数指针// ppOriginal [out] 指向调用原函数入口的指针,此参数可为NULLMH_STATUS WINAPI MH_CreateHookApi(LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal);// 创建一个目标API函数的钩子// Parameters:// pszModule [in] 目标函数所在的模块名// pszProcName [in] 目标函数名// pDetour [in] 指向用来替代目标函数的函数指针// ppOriginal [out] 指向调用原函数入口的指针,此参数可为NULL// ppTarget [out] 指向目标函数的指针,它将与其他函数一起使用,此参数可为NULLMH_STATUS WINAPI MH_CreateHookApiEx(LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget);// 移除已创建的钩子// Parameters:// pTarget [in] 目标函数的指针MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget);// 启用已创建的钩子// Parameters:// pTarget [in] 指向目标函数的指针// 如果这个参数是 MH_ALL_HOOKS,那么所有创建的钩子都将一次性启用。MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget);// 禁用已创建的钩子// Parameters:// pTarget [in] 指向目标函数的指针// 如果这个参数是 MH_ALL_HOOKS,那么所有创建的钩子都将一次性禁用。MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget);// 添加启用创建的钩子放到队列中// Parameters:// pTarget [in] 指向目标函数的指针// 如果这个参数是 MH_ALL_HOOKS,那么所有创建的钩子都将排队等待启用。MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget);// 排队禁用已创建的钩子// Parameters:// pTarget [in] 指向目标函数的指针// 如果这个参数是 MH_ALL_HOOKS,那么所有创建的钩子都将排队等待禁用。MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget);// 一次性应用所有排队的更改MH_STATUS WINAPI MH_ApplyQueued(VOID);// 通过状态返回钩子名const char * WINAPI MH_StatusToString(MH_STATUS status);#ifdef __cplusplus
}
#endif
四、Hook实现过程
4.1 步骤介绍
我们要实现Hook,需要我们自己生成一个可以注入目标进程的独立的dll,有了可以注入目标进程的dll以后,我们就需要一个注射器来把我们的dll注入到目标进程中。
把dll注入目标进程的目的是为了获取目标进程中的目标函数的地址,进程空间都是相互独立的,想实现Hook就需要把dll注入到目标进程中,然后获取到目标进程中windows API的函数地址,或者是其他目标进程使用的其他开源库中的函数地址。
- 注入进程的dll:实现注入后的Hook操作
- 注射器:为了把dll注入到目标进程中
- 目标进程:含有目标Hook函数的被注入进程
4.2注入进程的dll代码
新建一个dll项目,把静态库libMinHook编译进去,添加头文件,然后进行Hook代码的编写,这里我们简单Hook一个MessageboxA,大体代码如下所示,还可以去看MinHook的头文件调用其他接口。
#include "../include/MinHook.h"
#if defined _M_X64
#pragma comment(lib, "libMinHook.x64.lib")
#elif defined _M_IX86
#pragma comment(lib, "libMinHook.x86.lib")
#endif
typedef int (WINAPI* MESSAGEBOXA)(HWND, LPCSTR, LPCSTR, UINT);MESSAGEBOXA fpMessageBoxA = NULL;//指向原MessageBoxA的指针
//用来替代原函数的MessageBox函数
int WINAPI DetourMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{//这里只做简单的修改参数,其实可以做很多事情,甚至不去调用原函数return fpMessageBoxA(hWnd, "Hooked!", lpCaption, uType);
}BOOL APIENTRY DllMain( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:do{//初始化if (MH_Initialize() != MH_OK){OutputDebugStringA("Initialize err");break;}//创建Hookif (MH_CreateHook(MessageBoxA, &DetourMessageBoxA,reinterpret_cast<LPVOID*>(&fpMessageBoxA)) != MH_OK){OutputDebugStringA("MH_CreateHook err");break;}//使目标函数Hook生效if (MH_EnableHook(MessageBoxA) != MH_OK){OutputDebugStringA("MH_EnableHook err");break;}//使目标函数Hook失效//if (MH_DisableHook(MessageBoxA) != MH_OK)//{// break;//}// 卸载MinHook//if (MH_Uninitialize() != MH_OK)//{// break;//}} while (false);case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;
}
注入工具就简单生成一个,用之前的远程线程注入方式就可以
简单的把注入的代码贴一下,我用MFC去写的一个工具Button控件响应代码
void CInjectProcessDlg::OnBnClickedHook1()
{DWORD hpid = 0; // 目标进程pidHANDLE htargetprocess = NULL; // 目标进程句柄LPVOID lpAddr; // 目标线程申请内存空间的指针BOOL bResult = FALSE; // 注入结果do{hpid = GetProcessIDByName(L"TargetProcess.exe");if (hpid == 0){break;}htargetprocess = OpenTargetProcess(hpid);if (htargetprocess == NULL){break;}if (TagetAlloc(htargetprocess, lpAddr) == FALSE){break;}if (WriteDLLToTarget(htargetprocess, lpAddr, L"Hookdll.dll") == FALSE){break;}PTHREAD_START_ROUTINE pnfStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"Kernel32"), "LoadLibraryW");if (pnfStartAddr == NULL){break;}if (CreateThreadInTarget(htargetprocess, pnfStartAddr, lpAddr) == FALSE){break;}bResult = TRUE;} while (false);
}
4.3效果
写个目标进程
#include <iostream>
#include <Windows.h>int main()
{MessageBoxA(NULL, "Orgin MessageBox", "tip", NULL); //注入前调用Sleep(10000); //简单Sleep一下MessageBoxA(NULL, "Orgin MessageBox", "tip", NULL); //注入后调用}
注入前
注入后:
通过PrecessExplorer可以清楚看到进程以及模块关系
4.4 拓展
以上例子只是一个简单的Hook了MessageBoxA,但实际上,我们可以通过一些工具看到一些进程中调用的其他API,或者使用的一些开源库的一些API接口,我们只要获取到想Hook的函数地址,就可以不限于仅仅Hook Windows提供的一些API。
Hook能做的事情很多,比如开发补丁,信息截获,安全防护等。
五、结尾
这个文章也是很简单入门的介绍Hook,实际上Hook技术还是有很强的拓展性,能做的事情很多,也可以做坏事,技术没有好坏,只是看使用者用来做什么了。