一、什么是HOOK
Hook是用来获取或者更改程序执行时的某些数据,或者更改程序执行流程的一种技术
Hook还有一种说法叫做“挂钩子”
二、HOOK的两种形式
修改函数代码
- Inline Hook
修改函数地址
- IAT HOOK
- SSDT HOOK
- IDT HOOK
- EAT HOOK
- IRP HOOK
三、IAT HOOK
简介
IAT表:全称Import Address Table,中文名为导入地址表
每一个进程都有这样一个IAT表,而这个IAT表存储着当前这个模块所用到的所有api函数地址
将程序拖入OD里,随便找一个调用系统api的函数,然后数据跟随内存地址,会发现这些api函数在IAT表里的位置都是挨在一起的
只要将这些函数的地址修改成指向我们自行设置的函数地址,就能实现所谓的IAT Hook
DLL代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。"
#include <windows.h>
//创建与Messagebox相同的函数指针,注意要设置相同的函数参数
typedef int (WINAPI *PfnMsgA)(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);
PfnMsgA g_OldPfnMsgA = nullptr; //定义一个指向原先messagebox函数的空指针(nullptr)
//解析PE文件结构
HMODULE hModImageBase = GetModuleHandle(NULL); //获取当前的ImagBase(基址)
PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)(DWORD)hModImageBase; //获取DOS头
DWORD dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew;
PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)dwTemp; //获取NT头
PIMAGE_FILE_HEADER pFileHead = (PIMAGE_FILE_HEADER)& pNtHead->FileHeader; //获取标准PE头
PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)& pNtHead->OptionalHeader; //获取扩展PE头
DWORD dwExportLocal = pOptHead->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; //找到导入表的偏移(RVA)
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)GetModuleHandle(NULL) + dwExportLocal); //获取导入表
//定义自己设置的Messagebox函数
int WINAPI MyMessageBox(_In_opt_ HWND hWnd, _In_opt_ LPCSTR lpText, _In_opt_ LPCSTR lpCaption, _In_ UINT uType)
{
char szHookText[] = "Hook成功";
if (g_OldPfnMsgA != nullptr)
{
return g_OldPfnMsgA(hWnd, szHookText, lpCaption, uType);//调用以前的
}
return 0;
}
//设置IATHook
void SetIatHook()
{
MessageBoxA(NULL, "开始进行HOOK", NULL, NULL);
PVOID pHookAddress = nullptr; //定义一个指向hook地址的空指针
pHookAddress = GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA"); //将要hook的地址指向Messagebox函数
if (nullptr == pHookAddress)
{
OutputDebugString(TEXT("获取函数地址失败"));
MessageBoxA(NULL, "获取函数地址失败HOOK", NULL, NULL);
return;
}
g_OldPfnMsgA = (PfnMsgA)pHookAddress; //指向旧函数的指针.
//寻找IAT表的位置.
PIMAGE_IMPORT_DESCRIPTOR pCurrent = pImport;
DWORD *pFirstThunk; //导入表子表,也就是IAT存储函数地址的表.
//遍历导入表
while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL)
{
dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL);//找到IAT表的偏移地址
pFirstThunk = (DWORD *)dwTemp; //指向IAT表的指针
while (*pFirstThunk != NULL)
{
//遍历IAT表里的子表,若指针指向的是就函数的地址,则将其修改成我们的函数地址
if (*pFirstThunk == (DWORD)g_OldPfnMsgA)
{
DWORD oldProtected;
VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected); //设置该内存区域属性为可写可读可执行
dwTemp = (DWORD)MyMessageBox;
memcpy(pFirstThunk, (DWORD *)&dwTemp, 4); //将旧函数地址修改成自己的函数地址
VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected);
}
pFirstThunk++; //遍历IAT表
}
pCurrent++; //遍历导入表
}
}
//恢复导入表
void UnIatHook()
{
MessageBoxA(NULL, "开始进行HOOK", NULL, NULL);
PVOID pHookAddress = nullptr;
pHookAddress = MyMessageBox;
if (nullptr == pHookAddress)
{
OutputDebugString(TEXT("获取函数地址失败"));
MessageBoxA(NULL, "恢复函数地址失败HOOK", NULL, NULL);
return;
}
PIMAGE_IMPORT_DESCRIPTOR pCurrent = pImport;
DWORD* pFirstThunk; //指向
//遍历导入表
while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL)
{
dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL);
pFirstThunk = (DWORD*)dwTemp;
while (*pFirstThunk != NULL)
{
//遍历子表
if (*pFirstThunk == (DWORD)MyMessageBox) //如果是我们的函数地址.则
{
//找到要修改的导入表了,修改内存保护属性.写入我们新的函数地址.
DWORD oldProtected;
VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected);
dwTemp = (DWORD)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
memcpy(pFirstThunk, (DWORD*)& dwTemp, 4); //将变量中保存的函数地址拷贝到导入表中.
VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected);
}
pFirstThunk++; //继续遍历.
}
pCurrent++; //每次是加一个导入表结构.
}
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
SetIatHook();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
四、Inline Hook
简介
Inline Hook又称为超级Hook, 比起IAT Hook, 它的实用性更强以及更加容易过检测
Inline Hook为什么被称为是修改函数代码的Hook呢? 简单来说Hook就是jmp, 之所以InlineHook能够实现任意地址的hook, 是因为可以通过jmp指令实现来回的跳转
例如, 你要在某个地址进行Hook, 那就将此地址的汇编指令修改成jmp, 跳转到你要执行的函数, 然后再jmp回到hook地址的下一行地址
下面有一个简单的实例给给大伙演示
一个简单的实例
首先用C语言创建一个简单的加法程序
#include<Windows.h>
#include<cstdio>
int ADD(int a,int b) {
return a + b;
}
int main() {
int a, b;
scanf("%d,%d",&a, &b);
printf("结果是:%d", ADD(a, b));
}
将程序拖入OD中,并且找到Add函数的call的地址
再此call处下个断点,然后运行程序输入数据,这里我们输入1,2,然后F7进入此call
进入call后查看右下角的堆栈窗口可以查看到我们输入的函数实参
如果将此call堆栈里的参数值给修改了,那么就可以实现修改函数返回值
首先在函数头部地址添加一个JMP汇编指令,跳转的地址是任意的,只要这个地址周围没有指令,这里以跳转到401045为例
红色部分的汇编指令就是我们自行创建的,以此来实现修改堆栈中的函数参数值
这里也要注意平衡堆栈,原先的call是有push ebp
和mov ebp,esp
指令的,由于添加了jmp指令导致它们被覆盖,所以这里要添上
最后的jmp指令再跳转回到401003
最后程序运行结果为9,因为我们把参数从原先的1和2, 修改成了4和5
任意地址Hook
代码下载地址
链接:https://pan.baidu.com/s/1M2rPk2aNs74vIOyRl44n8A
提取码:c5ff
C++代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include <Windows.h>
#include <stdio.h>
#define BEA_ENGINE_STATIC
#define BEA_USE_STDCALL
#include "BeaEngine.h" //反汇编引擎
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")
#pragma comment(lib,"BeaEngine.lib")
#pragma comment(lib, "legacy_stdio_definitions.lib")
int gJmpRetAddress; //跳转返回到的地址
int gHookAddress; //要被HOOK的地址
int gLen; //HOOK代码写入后被覆盖掉的字节数
byte gOldArray[256] = { 0 }; //用于存储HOOK代码写入后被覆盖掉的字节数组
void __declspec(naked) MyFunction()
{
//打开浏览器
ShellExecute(NULL, "open", "https://www.baidu.com", NULL, NULL, SW_SHOWNORMAL);
//恢复原始字节
for (size_t i = 0; i < gLen; i++)
{
*((byte*)(gHookAddress + i)) = gOldArray[i];
}
_asm jmp gHookAddress
}
int CalcJmpAddress(int targetAddress, int hookAddress)
{
return targetAddress - hookAddress - 5;
}
//任意地址HOOK函数
void SupperHook(int hookAddress)
{
DISASM disam; //定义一个反汇编引擎
disam.EIP = hookAddress; //将当前EIP设置成hook的地址
gLen = Disasm(&disam); //当前EIP的硬编码字节数
while (gLen < 5)
{
disam.EIP += gLen;
gLen += Disasm(&disam);
}
memcpy_s(gOldArray, gLen, (void*)hookAddress, gLen); //
//计算HOOK执行完毕后jmp返回的地址
gJmpRetAddress = hookAddress + gLen;
//得到HOOK需要跳转到的函数地址
int targetAddress = (int)&MyFunction;
//计算jmp xxxxxxxx这个指令中的xxxxxxxx的值
//计算公式:目标地址-源地址-5
int value = CalcJmpAddress(targetAddress, hookAddress);
//修改内存页属性为可读可写可执行
DWORD oldProtect;
VirtualProtect((LPVOID)hookAddress, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtect);
//将要HOOK的地址第一个字节改成Jmp
*((byte*)hookAddress) = 0xE9;
//将跳转目标地址写入
*((int*)(hookAddress + 1)) = value;
}
DWORD WINAPI Thread(_In_ LPVOID lpParameter) {
int hookAddress;
AllocConsole(); //创建控制台
freopen("CONIN$", "r+t", stdin); //将stdin设备输入的数据重定向至控制台输入
freopen("CONOUT$", "w+t", stdout); //将stdin设备输出的数据重定向至控制台输出
while (true)
{
printf("请输入要HOOK的地址:");
scanf("%08X", &hookAddress);
//执行HOOK
SupperHook(hookAddress);
printf("HOOK执行完毕\n\n");
}
return true;
SupperHook(hookAddress);
return true;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: {
HANDLE hTread = CreateThread(NULL, 0, Thread, (LPVOID)NULL, 0, NULL);
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
易语言代码
操作演示
这里就用C++代码的hook来作演示
首先新建一个测试dll注入的易语言程序, , 并将此程序放到InlineHook.dll的所处目录
将testdll.exe拖入OD中, 跳转到按钮时间特征码处, 这里就用按钮时间特征码的起始地址来作Hook的地址
运行testdll.exe程序, 输入要hook的地址后, 再返回OD查看hook地址的汇编指令变化,由原先的push ebp
变成了 jmp
指令
返回testdll.exe程序界面, 点击按钮后触发Hook并执行我们自己的函数, 这里我的函数是打开浏览器
标签:逆向,函数,HOOK,int,学习,Hook,地址,DWORD,NULL From: https://www.cnblogs.com/henry666/p/16621344.html