[e]
#include <iostream>
#include <windows.h>
#pragma warning( disable : 4311 )
#pragma warning( disable : 4312 )
#define HOOK_BYTES 5
typedef unsigned int uint;
uint hookAddr = 0;
char old_code[ HOOK_BYTES ];
char new_code[ HOOK_BYTES ];
void printRegisters( void );
bool hook( void )
{
DWORD dwFlag;
if ( VirtualProtect( ( void* )hookAddr, HOOK_BYTES, PAGE_EXECUTE_READWRITE, &dwFlag ) )
{
memcpy( old_code, ( void* )hookAddr, HOOK_BYTES );
memcpy( ( void* )hookAddr, new_code, HOOK_BYTES );
VirtualProtect( ( void* )hookAddr, HOOK_BYTES, dwFlag, &dwFlag );
return true;
}
return false;
}
void unhook( void )
{
DWORD dwFlag;
if ( VirtualProtect( ( void* )hookAddr, HOOK_BYTES, PAGE_EXECUTE_READWRITE, &dwFlag ) )
{
memcpy( ( void* )hookAddr, old_code, HOOK_BYTES );
VirtualProtect( ( void* )hookAddr, HOOK_BYTES, dwFlag, &dwFlag );
}
}
namespace global
{
uint gEAX = 0;
uint gEBX = 0;
uint gECX = 0;
uint gEDX = 0;
uint gESP = 0;
uint gEBP = 0;
uint gESI = 0;
uint gEDI = 0;
uint gRet = 0; // 临时的返回地址
uint gTmp = 0; // 一些临时的值保存
uint gPar = 0; // 被hook函数的正常返回地址
uint gCnt = 1; // 当前hook的次数
uint gMax = 0; // 最大hook次数,为0表示一直hook
bool bEnt = 0; // 是否为第一次进入hook函数
}
void __declspec( naked ) hook_jmp( void )
{
__asm
{
__entry:
pushad
{
cmp global::bEnt, 0 // 如果没有进入则表示需要unhook
je __first
cmp global::gMax, 0 // 如果为0,则一直启用hook逻辑
je __second
mov eax, global::gCnt
cmp eax, global::gMax // 如果当前hook次数没有达到最大次数,则继续
jl __second
mov global::gCnt, 1 // reset state
mov global::bEnt, 0 // reset state
mov global::gMax, 0 // reset state
mov eax, global::gPar // 被hook函数的正常返回地址
mov global::gRet, eax // 准备跳转到被hook函数的上层调用,结束hook
popad
jmp __ret
}
__first:
// 保存相关重要寄存器值
{
popad
mov global::gEAX, eax
mov global::gEBX, ebx
mov global::gECX, ecx
mov global::gEDX, edx
mov global::gESP, esp
mov global::gEBP, ebp
mov global::gESI, esi
mov global::gEDI, edi
}
// 第一次进入,unhook并监视相关状态
pushad
{
mov global::bEnt, 1 // 记录状态
mov edi, global::gEBP // 被hook函数的ebp
mov eax, [ edi + 4 ] // 被hook函数的返回地址(其上层调用地址)
mov global::gPar, eax // 保存返回地址
mov esi, __entry // 将被hook函数的返回地址修改为
mov [ edi + 4 ], esi // 本函数的首地址,以便执行完被hook函数的
// 剩余逻辑之后能够返回到本函数,决定是否
// 还需要hook。
call printRegisters // 打印寄存器值[测试],或者其他
call unhook // unhook
mov eax, hookAddr // 获得被hook的内存地址
mov global::gRet, eax
}
popad
pop global::gTmp // 移除本函数的返回地址,并将hook的地址设置
jmp __ret // 为本函数的返回地址,从而实现跳转
__second:
// 第二次进入, 继续hook, 这次进入是被hook函数ret返回的,没有新的ret地址被压栈
{
mov global::bEnt, 0 // 设置状态
add global::gCnt, 1 // 增加hook计数
call hook // hook
mov eax, global::gPar // 将被hook函数的返回地址设置为本函数的
mov global::gRet, eax // 返回地址,从而实现正常的函数流程
}
popad
__ret:
push global::gRet // 修改本函数的返回地址
ret
}
}
void setHookBytes( uint addr )
{
hookAddr = addr;
new_code[ 0 ] = ( char )0xe8; // call 指令机器码
( uint& )new_code[ 1 ] = ( uint )hook_jmp - addr - 5; // 计算跳转偏移
}
void printRegisters( void )
{
printf( "EAX = 0x%08x/n", global::gEAX );
printf( "EBX = 0x%08x/n", global::gEBX );
printf( "ECX = 0x%08x/n", global::gECX );
printf( "EDX = 0x%08x/n", global::gEDX );
printf( "ESP = 0x%08x/n", global::gESP );
printf( "EBP = 0x%08x/n", global::gEBP );
printf( "ESI = 0x%08x/n", global::gESI );
printf( "EDI = 0x%08x/n", global::gEDI );
}
[/e] 如上,hook_jmp函数即为我们自定义的hook函数,当被hook函数被hook之后,就会跳转到这个函数里,执行相关逻辑,上面我加了很详细的注释。应该很容易看懂。还是先看怎么使用这套方法,再来细说,代码如下:
[e]
void testHook( void )
{
printf( "This is a hook test 1./n" );
printf( "This is a hook test 2./n" );
printf( "This is a hook test 3./n" );
printf( "This is a hook test 4./n" );
printf( "______________________/n" );
}
int main( void )
{
uint hook_addr = 0x0042ec7b;
setHookBytes( hook_addr );
global::gMax = 2;
if ( hook() )
{
testHook();
testHook();
testHook();
}
system( "pause" );
return 0;
}
[/e]
原文地址
http://blog.csdn.net/masefee/article/details/6326634