HooK原理简析

通过反汇编代码的跟进,了解Hook的工作原理(使用Detours库实现Hook)

示例代码

#include "stdafx.h"
#include <windows.h>
#include <detours.h>
#pragma  comment(lib,"detours.lib")

//1.拿到需要Hook的地址 操作系统里面MessageboxA里面的地址
static int (WINAPI* OldMessageBoxA)( HWND, LPCSTR, LPCSTR, UINT) = MessageBoxA;

//2.需要跳至的地方
int WINAPI NewMessageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
	if (strcmp(lpText, "This is zy") != 0)
	{
		return OldMessageBoxA(hWnd, "You are ugly!", "Sorry", MB_OK);
	}
	return OldMessageBoxA(hWnd, "This is zy", "Everyone", MB_OK);
}

//3.开始进行Hook地址
bool Hook() 
{
	// 相关的初始化信息
	DetourTransactionBegin();
	// 更新线程信息
	DetourUpdateThread(GetCurrentThread());
	// 挂载我们的hook函数(NeeMessagwBoxA)到MessageBoxA函数的地址上
	DetourAttach(&(PVOID&)OldMessageBoxA, NewMessageBoxA);
	return NO_ERROR == DetourTransactionCommit();
}

// 卸载Hook
bool UnHook()
{
	DetourTransactionBegin();
	DetourUpdateThread(GetCurrentThread());
	DetourDetach(&(PVOID&)OldMessageBoxA, NewMessageBoxA);
	return NO_ERROR == DetourTransactionCommit();
}

int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd )
{
	//跨进程改不了
	//因为我们当前的这个Hook就是我们本进程
	//如果想跨进程,在Dll中Hook,然后注入 全局Hook
	//Hook在哪个地方 
	//检测Hook CRC、
	//Hook后如何让保证系统的稳定,这个做的Hook实在R3层,R0也能Hook
	MessageBoxA(NULL, "This is Hook", "Hook", MB_OK);
	Hook();
	MessageBoxA(NULL, "This is Hook", "Hook", MB_OK);
	UnHook();
	MessageBoxA(NULL, "This is Hook", "Hook", MB_OK);

//  加载Hook的Dll文件
// 	HMODULE hModule = LoadLibraryA("HooKDll.dll");
// 	if (hModule == NULL)
// 	{
// 		printf("LoadLibraryA faild!\n");
// 	}
// 	MessageBoxA(NULL, "This is Hook", "Hook", MB_OK);
// 	FreeLibrary(hModule);
    return 0;
}

运行结果:
这里写图片描述


反汇编解析

接下来我们进入到反汇编格式查看下实现
第一句MessageBoxA(NULL, “This is Hook”, “Hook”, MB_OK);

// 压栈数据,调用MessageBoxA该API
0124280E  mov         esi,esp  
01242810  push        0  
01242812  push        offset string "Hook" (0124AC64h)  
01242817  push        offset string "This is Hook" (0124BD88h)  
0124281C  push        0  
0124281E  call        dword ptr [__imp__MessageBoxA@16 (0124F0C8h)] 

// 查看MessageBox函数的首地址
76EEFDAE  mov         edi,edi  
76EEFDB0  push        ebp  
76EEFDB1  mov         ebp,esp  
76EEFDB3  push        0  
76EEFDB5  push        dword ptr [ebp+14h]  
76EEFDB8  push        dword ptr [ebp+10h]  
76EEFDBB  push        dword ptr [ebp+0Ch]  
76EEFDBE  push        dword ptr [ebp+8]  
76EEFDC1  call        76EEFD66  
76EEFDC6  pop         ebp  
76EEFDC7  ret         10h  

具体指令的作用就不一一解释了,这不是重点。
可以看到MessageBox的函数首地址为76EEFDAE,该处的汇编指令为
mov edi,edi->不难看出,该指令的作用就是没有作用!只是为了方面扩展


经过Hook后
第二句MessageBoxA(NULL, “This is Hook”, “Hook”, MB_OK);

// 1、压栈数据,调用MessageBoxA该API
01242830  mov         esi,esp  
01242832  push        0  
01242834  push        offset string "Hook" (0124AC64h)  
01242839  push        offset string "This is Hook" (0124BD88h)  
0124283E  push        0  
01242840  call        dword ptr [__imp__MessageBoxA@16 (0124F0C8h)]  

// 2、跟进查看MessageBox函数的首地址,此处原本应该是 mov edi,edi
76EEFDAE  jmp         NewMessageBoxA (01241424h)  
76EEFDB3  push        0  
76EEFDB5  push        dword ptr [ebp+14h]  
76EEFDB8  push        dword ptr [ebp+10h]  
76EEFDBB  push        dword ptr [ebp+0Ch]  
76EEFDBE  push        dword ptr [ebp+8]  
76EEFDC1  call        76EEFD66  
76EEFDC6  pop         ebp  
76EEFDC7  ret         10h  

// 3、在01241424h地址处,进行跳转到012426C0h(我们真正的函数首地址)
01241424h  jmp         NewMwssageBoxA (012426C0h) 
int WINAPI NewMwssageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
012426C0  push        ebp  
012426C1  mov         ebp,esp  
012426C3  sub         esp,0C0h  
012426C9  push        ebx  
012426CA  push        esi  
012426CB  push        edi  
012426CC  lea         edi,[ebp-0C0h]  
012426D2  mov         ecx,30h  
012426D7  mov         eax,0CCCCCCCCh  
012426DC  rep stos    dword ptr es:[edi]  
	if (strcmp(lpText, "This is zy") != 0)
012426DE  push        offset string "This is zy" (0124AC30h)  
012426E3  mov         eax,dword ptr [lpText]  
012426E6  push        eax  
012426E7  call        _strcmp (01241136h)  
012426EC  add         esp,8  
012426EF  test        eax,eax  
012426F1  je          NewMwssageBoxA+54h (01242714h)  
	{
		return OldMwssageBoxA(hWnd, "You are ugly!", "Sorry", MB_OK);
		
//进行数据的压栈
012426F3  mov         esi,esp  
012426F5  push        0  
012426F7  push        offset string "Sorry" (0124AC40h)  
012426FC  push        offset string "You are ugly!" (0124AC48h)  
01242701  mov         eax,dword ptr [hWnd]  
01242704  push        eax  

// 我们的程序将从此处跳转
01242705  call        dword ptr [OldMwssageBoxA (0124E2D0h)] 

// 进行 RTC 运行时检错
0124270B  cmp         esi,esp  
0124270D  call        __RTC_CheckEsp (012411AEh)  
01242712  jmp         NewMwssageBoxA+73h (01242733h)  
	}
	return OldMwssageBoxA(hWnd, "This is zy", "Everyone", MB_OK);
01242714  mov         esi,esp  
01242716  push        0  
01242718  push        offset string "Everyone" (0124AC58h)  
0124271D  push        offset string "This is zy" (0124AC30h)  
01242722  mov         eax,dword ptr [hWnd]  
01242725  push        eax  
01242726  call        dword ptr [OldMwssageBoxA (0124E2D0h)]  
0124272C  cmp         esi,esp  
0124272E  call        __RTC_CheckEsp (012411AEh)  
}
01242733  pop         edi  
01242734  pop         esi  
01242735  pop         ebx  
01242736  add         esp,0C0h  
0124273C  cmp         ebp,esp  
0124273E  call        __RTC_CheckEsp (012411AEh)  
01242743  mov         esp,ebp  
01242745  pop         ebp  
01242746  ret         10h

// 4、经过在NewMwssageBoxA中的跳转,进入到36ED00D8地址处
36ED00D8  mov         edi,edi  
36ED00DA  push        ebp  
36ED00DB  mov         ebp,esp  
36ED00DD  jmp         76EEFDB3 

// 5、很明显,此时我们又跳转回来了(此处和第二点是同一位置)
76EEFDB3  push        0  
76EEFDB5  push        dword ptr [ebp+14h]  
76EEFDB8  push        dword ptr [ebp+10h]  
76EEFDBB  push        dword ptr [ebp+0Ch]  
76EEFDBE  push        dword ptr [ebp+8]  
76EEFDC1  call        76EEFD66  
76EEFDC6  pop         ebp  
76EEFDC7  ret         10h  

通过该汇编指令的理解,可以发现:经过Hook后,改变了原来的函数首地址的指令mov edi,edi,使得该地址先跳转到我们Hook后的地址上,先实现我们的函数,完成之后再跳转到原来的地址上执行(当然,你也可以不用再跳回到原来的地址中执行),这也是Hook的原理

注意的是,该Hook仅仅是在本进程进行的,而且是Hook的ring3层


经过UnHook后,将会将我们的地址改变为原来的mov edi,edi,和第三句
MessageBoxA(NULL, “This is Hook”, “Hook”, MB_OK);一样

// 压栈数据,调用MessageBoxA该API
01242852  mov         esi,esp  
01242854  push        0  
01242858  push        offset string "Hook" (0124AC64h)  
0124285D  push        offset string "This is Hook" (0124BD88h)  
01242863  push        0  
01242865  call        dword ptr [__imp__MessageBoxA@16 (0124F0C8h)] 

// 查看MessageBox函数的首地址
76EEFDAE  mov         edi,edi  
76EEFDB0  push        ebp  
76EEFDB1  mov         ebp,esp  
76EEFDB3  push        0  
76EEFDB5  push        dword ptr [ebp+14h]  
76EEFDB8  push        dword ptr [ebp+10h]  
76EEFDBB  push        dword ptr [ebp+0Ch]  
76EEFDBE  push        dword ptr [ebp+8]  
76EEFDC1  call        76EEFD66  
76EEFDC6  pop         ebp  
76EEFDC7  ret         10h  

总结下(Hook前后的对比):
这里写图片描述

示例代码需要Detours库的支持,Detours下载地址
本文难免有所错误,如有问题欢迎留言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值