Inline Hook研究
Inline Hook是在程序调用某个API函数之前,在其上方设置指令跳转到自己写的的函数
实验
Hoo kMessageBox测试
x86思路如下:
1.调用 GetModuleHandle 来获取到模块的基址(user32.dll)
2.调用 GetProcAddress 获取到MessageBoxA弹窗的基址
3.调用 VirtualProtect 来修改MsgBox前5个字节内存属性
4.计算 Dest - MsgBox - 5 重定位跳转地址,并Jmp跳转
5.计算 Dest + Offset + 5 = MsgBox +5 跳转回来
疑问:为什么是5个字节?,jmp指令的机器码刚好5字节
E9 87 FE FF FF
而在调用MessageBoxA API之前的保存栈堆新开堆栈时刚好够5字节(机器码共5个字节,这种情况适用于大部分API但并非全部API都适用)
8B FF mov edi,pdi
55 push ebp //新栈顶
8B EC mov ebp,esp //保留堆栈
Hook后这些指令就变成了一条jmp指令
最终在OD类似如下
x86内嵌asm hook实现如下:
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
typedef int (WINAPI
*MessageBox_type) (
__in_opt HWND hWnd,
__in_opt LPCSTR lpText,
__in_opt LPCSTR lpCaption,
__in UINT uType);
MessageBox_type RealMessageBox = MessageBoxA;
//我们自己的MessageBox,每调用MessageBox都要跳到myMessageBox来处理
_declspec(naked) void WINAPI
myMessageBox(
__in_opt HWND hWnd,
__in_opt LPCSTR lpText,
__in_opt LPCSTR lpCaption,
__in UINT uType)
{
__asm
{
PUSH ebp
mov ebp, esp
/*
vs2010 debug 编译后的代码由于要cmp esi esp来比较堆栈。
所以这里在调用非__asm函数前push一下esi
*/
push esi
}
//下面打印MessageBox参数
printf("hwnd:%8X lpText:%s lpCaption:%s,uType:%8X", hWnd, lpText, lpCaption, uType);
__asm
{
/*
vs2010 debug 编译后的代码由于要cmp esi esp来比较堆栈。
所以这里在调用非__asm函数前push一下esi
*/
pop esi
mov ebx, RealMessageBox
add ebx, 5 //地址恢复避免重复hook
jmp ebx
}
}
#pragma pack(1)
typedef struct _JMPCODE
{
BYTE jmp;
DWORD addr;
}JMPCODE, *PJMPCODE;
VOID HookMessageBoxA()
{
JMPCODE jcode;
jcode.jmp = 0xe9;//jmp
jcode.addr = (DWORD)myMessageBox - (DWORD)RealMessageBox - 5; //自己的函数地址-MessageBox基址-jmp指令大小
RealMessageBox = MessageBoxA;
::WriteProcessMemory(GetCurrentProcess(), MessageBoxA, &jcode, sizeof(JMPCODE), NULL);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
HookMessageBoxA(); //hook操作
MessageBoxA(NULL, "HOOK", "HOOKKKKKK", MB_OK);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
x64如下
1.备份原来的指令
2.8个0x90替换为要跳转的函数地址
3.自己的函数中要执行的内容
4.恢复原来的指令
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
BYTE oldcode[12] = { 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };
void HookFunction(LPVOID lpFunction) {
DWORD_PTR FuncAddress = (UINT64)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
DWORD offset = 0;
if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &offset)) {
memcpy(oldcode, (LPVOID)FuncAddress, 12); // 拷贝原始机器码指令到oldcode
*(PINT64)(HookCode + 2) = (UINT64)lpFunction; //填充90为指定跳转地址 (0x90修改为要跳转的地址)
}
memcpy((LPVOID)FuncAddress, &HookCode, sizeof(HookCode)); //拷贝hook指令
VirtualProtect((LPVOID)FuncAddress, 12, offset, &offset); //还原页面保护属性
}
void UnHookFunction() {
DWORD offset = 0;
DWORD_PTR FuncAddress = (UINT64)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &offset)) {
memcpy((LPVOID)FuncAddress, oldcode, sizeof(oldcode)); //恢复原来的机器指令
}
VirtualProtect((LPVOID)FuncAddress, 12, offset, &offset);//还原页面保护属性
}
int WINAPI TestMessageBoxA(HWND hwnd, LPCSTR lpText, LPCSTR lpcaption, UINT utype) {
UnHookFunction(); //取消hook
int ret = MessageBoxA(0, "HOOK", "title", MB_OK);
HookFunction((PROC)TestMessageBoxA); //继续hook
return ret;
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
HookFunction((PROC)TestMessageBoxA);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
结果如下:
HookCode刨析:
x86话的HookCode为E9 00 00 00 00 (另外个执行例子)还需要减去5字节的便宜地址
x86非内嵌asm,x86要将自己函数处理的地址-被Hook函数+5得出的偏移地址 (至于为什么我也不知道,原文写的不清不楚)
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
BYTE oldcode[5] = { 0x00 };
BYTE HookCode[5] = { 0xe9, 0x0, 0x0, 0x0, 0x0 };
void HookFunction(LPVOID lpFunction) {
DWORD_PTR FuncAddress = (UINT32)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
DWORD offset = 0;
DWORD dfset = 0;
dfset = (DWORD)lpFunction - ((DWORD)FuncAddress + 5); //偏移地址计算
if (VirtualProtect((LPVOID)FuncAddress, 5, PAGE_EXECUTE_READWRITE, &offset)) {
memcpy(oldcode, (LPVOID)FuncAddress, 5); // 拷贝原始机器码指令到oldcode
*(PINT32)(HookCode + 1) = (UINT32)dfset; //填充90为指定跳转地址 (0x90修改为要跳转的地址)
}
memcpy((LPVOID)FuncAddress, &HookCode, sizeof(HookCode)); //拷贝hook指令
VirtualProtect((LPVOID)FuncAddress, 5, offset, &offset); //还原页面保护属性
}
void UnHookFunction() {
DWORD offset = 0;
DWORD_PTR FuncAddress = (UINT32)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
if (VirtualProtect((LPVOID)FuncAddress, 5, PAGE_EXECUTE_READWRITE, &offset)) {
memcpy((LPVOID)FuncAddress, oldcode, sizeof(oldcode)); //恢复原来的机器指令
}
VirtualProtect((LPVOID)FuncAddress, 5, offset, &offset);//还原页面保护属性
}
int WINAPI TestMessageBoxA(HWND hwnd, LPCSTR lpText, LPCSTR lpcaption, UINT utype) {
UnHookFunction(); //取消hook
int ret = MessageBoxA(0, "HOOK", "title", MB_OK);
HookFunction((PROC)TestMessageBoxA); //继续hook
return ret;
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
HookFunction((PROC)TestMessageBoxA);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
其他例子SetConsoleTitleW Hook
Hook如下,一执行命令cmd的标题就会被更改
总的来说x86用内嵌asm,x64用memcpy插机器码
DetOurs库的使用
这玩意x64是商业版(
github地址:https://github.com/Microsoft/Detours
下完进入到目录,用vs命令行的nmake编译。然后将lib和include添加到项目
Hook MessageBoxA
#include "stdafx.h"
#include <detours.h>
#pragma comment(lib,"detours.lib")
static INT(WINAPI *OldMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT) = MessageBoxA; //定义被Hook的API函数原型
INT WINAPI MyMessAgeBoxA(HWND, LPCSTR, LPCSTR, UINT) {
OldMessageBoxA(NULL, "Hook", "Title", MB_OK); //注意调用的被hook的函数时要使用自己定义的,否则会崩溃
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
int ts;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
ts = DetourTransactionBegin();
if (ts == NO_ERROR) {
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)OldMessageBoxA, MyMessAgeBoxA);
DetourTransactionCommit();
}
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
参考链接
https://www.cnblogs.com/LyShark/p/11692436.html
https://developer.aliyun.com/article/568432
https://www.write-bug.com/article/1851.html
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。
文章标题:Inline Hook研究
本文作者:九世
发布时间:2021-08-19, 01:31:25
最后更新:2021-08-19, 01:44:49
原始链接:http://jiushill.github.io/posts/3d0703bd.html版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。