参考文章:http://www.codeproject.com/Articles/21414/Powerful-x-x-Mini-Hook-Engine
Mini-Hook-Engine下载:http://download.csdn.net/detail/friendan/9182677
-----------------------------------------------------------------------------------------------------
我使用 Detours和Mini-Hook-Engine来HOOK GetMessageTime都没有成功,
Detours直接返回失败,Mini-Hook-Engine虽然成功写了JMP,但在HOOK后,调用原GetMessageTime时出错了,原因是其Bridge写错了。。。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
先Copy下Mini-Hook-Engine的原理,后面我们写自己的Bridge时用到:
Let's make a real world example. If the first instructions of the function/API we want to hook are:
mov edi, edi
push ebp
mov ebp, esp
xor ecx, ecx
They will be replaced by our:
00400000 jmp our_code
00400005 xor ecx, ecx
Our bride will look like this:
mov edi, edi
push ebp
mov ebp, esp
jmp 00400005
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
我们来看看GetMessageTime的汇编代码:
// GetMessageTime入口原始汇编指令
7608F600 6A 0A push 0Ah
7608F602 E8 C7 17 FE FF call 76070DCE
7608F607 C3 ret
7608F608 81 C2 58 07 00 00 add edx,758h
7608F60E E9 B0 27 FE FF jmp 76071DC3
7608F613 50 push eax
7608F614 6A 00 push 0
...
// 使用Mini Hook-Engine后,GetMessageTime入口汇编指令被修改如下
7608F600 FF 25 06 F6 08 76 jmp dword ptr ds:[7608F606h]
7608F606 18 AB 5B 0F 58 07 sbb byte ptr [ebx+7580F5Bh],ch
7608F60C 00 00 add byte ptr [eax],al
7608F60E E9 B0 27 FE FF jmp 76071DC3
7608F613 50 push eax
7608F614 6A 00 push 0
...
// Mini Hook-Engine对GetMessageTime的Bridge如下
001E0000 EB 05 jmp 001E0007
001E0002 90 nop
001E0003 90 nop
001E0004 90 nop
001E0005 90 nop
001E0006 90 nop
001E0007 FF 25 40 0C E6 74 jmp dword ptr ds:[74E60C40h]
001E000D FF 25 13 00 1E 00 jmp dword ptr ds:[1E0013h]
001E0013 19 11 sbb dword ptr [ecx],edx
001E0015 E6 74 out 74h,al
001E0017 6A 0A push 0Ah // GetMessageTime原入口第一条指令
001E0019 E8 C7 17 FE FF call 001C17E5 // 这里错了。。。,原函数是:call 76070DCE
001E001E C3 ret
001E001F 81 C2 58 07 00 00 add edx,758h
001E0025 FF 25 2B 00 1E 00 jmp dword ptr ds:[1E002Bh]
001E002B 0E push cs
001E002C ?? db f6h
001E002D 08 76 00 or byte ptr [esi],dh
001E0030 00 00 add byte ptr [eax],al
001E0032 00 00 add byte ptr [eax],al
001E0034 00 00 add byte ptr [eax],al
001E0036 00 00 add byte ptr [eax],al
----------------------------------------------------------------------------------------------------------------------
知道了错误原因,我继续使用Mini Hook-Engine来HOOK GetMessageTime,不过Bridge我们得自己写:
我的Bridge代码如下:
VOID __declspec(naked) GetMessageTime_Bridge(VOID)
{
_asm
{
push 0xA
//call g_iGetMessageTimeCallAddr // 这里动态写入,先用5个nop来占位
nop
nop
nop
nop
nop
ret
//jmp iJmpAddr // 这里动态写入,先用5个nop来占位
nop
nop
nop
nop
nop
}
}
LONG WINAPI MyGetMessageTime(VOID)
{
// 调用原函数
LONG liRet = 0;
LONG (WINAPI *pGetMessageTime)(VOID) = NULL;
//pGetMessageTime = (LONG (__stdcall *)(void))NtHookEngine_GetOriginalFunction((ULONG_PTR)MyGetTickCount);
pGetMessageTime = (LONG (__stdcall *)(void))GetMessageTime_Bridge;
if (pGetMessageTime != NULL)
{
liRet = pGetMessageTime();
}
return liRet;
}
最重要的HOOK GetMessageTime函数:
VOID Hook_GetMessageTime(VOID)
{
// E8 C7 17 FE FF call 76070DCE
DWORD dwOldProtect = 0;
VirtualProtect(TrueGetMessageTime, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 这里得到C7 17 FE FF
int iCallAddr = 0;
memcpy((VOID*)&iCallAddr, (VOID*)((INT)TrueGetMessageTime + 3), 4);
// 将C7 17 FE FF还原成76070DCE
// ((int)TrueGetMessageTime + 7) == CALL的下一行地址
iCallAddr = iCallAddr + ((int)TrueGetMessageTime + 7);
VirtualProtect(TrueGetMessageTime, 5, dwOldProtect, NULL);
// 修正我们的GetMessageTime_Bridge
int iAddr = (int)GetMessageTime_Bridge;
byte bData[5] = {0};
byte bKey[5] = {0x90, 0x90, 0x90, 0x90, 0x90};
while(true)
{
// 查找那5个nop指令的首地址
VirtualProtect((VOID*)iAddr, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy(bData, (VOID*)iAddr, 5);
VirtualProtect((VOID*)iAddr, 5, dwOldProtect, NULL);
if (memcmp(bData, bKey, 5) != 0)
{
iAddr ++;
continue;
}
VirtualProtect((VOID*)iAddr, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 写入 call 76070DCE
// 目标地址 - 当前指令地址的下个指令地址 = 距离
*((byte*)iAddr) = 0xE8;
*((int*)(iAddr + 1)) = iCallAddr - (iAddr + 5);
// 写入 jmp 7608F60C
// 目标地址 - JMP指令所在地址 - 5 = JMP地址
*((byte*)(iAddr + 6)) = 0xE9;
*((int*)(iAddr + 7)) = ((int)TrueGetMessageTime + 0xC) - (iAddr + 6) - 5;
VirtualProtect((VOID*)iAddr, 5, dwOldProtect, NULL);
break;
}
// HOOK GetMessageTime
NtHookEngine_Hook((ULONG_PTR)TrueGetMessageTime, (ULONG_PTR)&MyGetMessageTime);
}
---------------------------------------------------------------------------------------------------
此次收获:
1. 懂得了Bridge的写法和用法,原来函数只是一个地址,我们强制转换就可以了:
LONG (WINAPI *pGetMessageTime)(VOID) = NULL;
pGetMessageTime = (LONG (__stdcall *)(void))GetMessageTime_Bridge;
2. 懂得了CALL 后面的地址的计算方法:
// 目标地址(76070DCE) - 当前CALL指令的下个地址 = CALL后面的4个字节数据:E8 C7 17 FE FF
*((byte*)iAddr) = 0xE8;
*((int*)(iAddr + 1)) = iCallAddr - (iAddr + 5);