hook杂记(一)
hook有很多种形式,这里分析inline hook api
这种形式的hook只针对被hook的进程,而不会影响到其他进程。
例如,某个进程用到了MessageBox这个API,程序希望改变原有MessageBox这个函数的行为,可以进行这种hook。
Inline hook有个几种方式
1.先执行hook函数,然后再执行原API。
2.先执行原API,再执行hook函数。
需要了解的几个知识点:
1.如果在Debug版下做实验,需要将Link Increase功能关掉
2.跳转指令 jmp XXXX;跳转到某个地址
对应机器码 e9 relative_add 其中 e9是一个字节 relative_add是四个字节,也就是这个指令共5个字节。跳转后的地址应为add_start+5+relative_add
其中add_start为e9开始的地址,relative_add为相对地址。
相反,relative_add=XXXX-5-add_start。
3.一般先分析被hook的原函数,分析头几个字节。如MessageBox
77D8050B 8B FF mov edi,edi
77D8050D 55 push ebp
77D8050E 8B EC mov ebp,esp
77D80510 83 3D 1C 04 DA 77 00 cmp dword ptr ds:[77DA041Ch],0
......
可以看出前5个指令可以被替换掉,而替换前6个字节肯定会出错。
原理:
Inline hook时候,需要准备一个PreHook函数和PostHook函数。
1.计算原API函数地址.
2.备份原APi函数的头5个字节。
3.用jmp PreHook 替换OrginApi头5个字节。
//一个是个字节,前面5个字节是备份的原API的头5个字节,后面5个字节是jmp OriginApi
__declspec(naked) int PostHook()
{
__asm
{
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
}
}
PreHook()
{
Call Hook;
goto PostHook;
}
测试代码:
#include <windows.h>
#include <stdio.h>
DWORD UpperFunctionReturnAdd;//保存API返回地址
int nRet;
BYTE orig_code[5] = {0x90, 0x90, 0x90, 0x90, 0x90};//存放原始的指令
BYTE hook_code[5] = {0xe9, 0, 0, 0, 0};//存放跳转到MyMessageBoxA的指令
BYTE jmp_org_code[5] = {0xe9, 0, 0, 0, 0};//存放跳转到原起始地址后5字节的指令
int MyMessageBoxA();
int MyMessageBoxAA(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
);
int MyFunc();
void Hook();
int jmp_back();
ULONG OldFuncAddr;
ULONG MyFuncAddr;
ULONG jmp_backAddr;
//在修改前几个字节时,注意:取出的指令为完整的
int main()
{
Hook();
int rt = MessageBoxA(NULL, "Hello World", "Title", MB_OK);
return 0;
}
void Hook()
{
DWORD dwOldProtect;
OldFuncAddr = (ULONG)MessageBoxA;
// MyFuncAddr = MyMessageBoxA的实际地址
MyFuncAddr = (ULONG)MyMessageBoxAA;
// jmp_backAddr = jmp_back的实际地址
jmp_backAddr = (ULONG)jmp_back;
//修改内存为PAGE_EXECUTE_READWRITE
VirtualProtect((LPVOID)jmp_backAddr, 10, PAGE_EXECUTE_READWRITE, &dwOldProtect);
VirtualProtect((LPVOID)OldFuncAddr, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
//计算跳转地址,由OldFuncAddr向MyFuncAddr跳转
*((ULONG*)(hook_code+1)) = (ULONG)MyFuncAddr - (ULONG)OldFuncAddr - 5;
memcpy(orig_code,(BYTE *)OldFuncAddr, 5);
memcpy((BYTE*)OldFuncAddr, hook_code, 5);
//计算返回地址,从jmp_backAddr向OldFuncAddr跳转
*((ULONG*)(jmp_org_code+1)) = (ULONG)OldFuncAddr - (ULONG)jmp_backAddr - 5;
memcpy((BYTE *)jmp_backAddr, orig_code, 5);
memcpy((BYTE *)jmp_backAddr + 5, jmp_org_code, 5);
}
__declspec(naked) int jmp_back()
{
__asm
{
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
}
}
//MyMessageBoxA:在函数执行前进行自己的处理
__declspec(naked) int MyMessageBoxA()
{
printf("MyMessageBoxA is called\r\n");
MyFunc();////可以加入函数过程
__asm
{
//跳回MessageBoxA入口点
jmp jmp_back;
}
}
//MyMessageBoxA:在函数执行后进行自己的处理
__declspec(naked) int MyMessageBoxAA(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
)
{
printf("MyMessageBoxAA is called\r\n");
__asm
{
pop UpperFunctionReturnAdd
push offset s1;//返回地址为S1:
//跳回MessageBoxA入口点
jmp jmp_back;
s1: nop
}
MyFunc();
__asm
{
;//将原返回地址压栈
mov eax, 0;////演示:将返回结果改为0,也可由MyFunc返回
push UpperFunctionReturnAdd
ret;
}
}
int MyFunc()
{
printf("Hello World\r\n");
return 1;
}
参考:
1.http://bbs.pediy.com/showthread.php?t=69666
这种形式的hook只针对被hook的进程,而不会影响到其他进程。
例如,某个进程用到了MessageBox这个API,程序希望改变原有MessageBox这个函数的行为,可以进行这种hook。
Inline hook有个几种方式
1.先执行hook函数,然后再执行原API。
2.先执行原API,再执行hook函数。
需要了解的几个知识点:
1.如果在Debug版下做实验,需要将Link Increase功能关掉
2.跳转指令 jmp XXXX;跳转到某个地址
对应机器码 e9 relative_add 其中 e9是一个字节 relative_add是四个字节,也就是这个指令共5个字节。跳转后的地址应为add_start+5+relative_add
其中add_start为e9开始的地址,relative_add为相对地址。
相反,relative_add=XXXX-5-add_start。
3.一般先分析被hook的原函数,分析头几个字节。如MessageBox
77D8050B 8B FF mov edi,edi
77D8050D 55 push ebp
77D8050E 8B EC mov ebp,esp
77D80510 83 3D 1C 04 DA 77 00 cmp dword ptr ds:[77DA041Ch],0
......
可以看出前5个指令可以被替换掉,而替换前6个字节肯定会出错。
原理:
Inline hook时候,需要准备一个PreHook函数和PostHook函数。
1.计算原API函数地址.
2.备份原APi函数的头5个字节。
3.用jmp PreHook 替换OrginApi头5个字节。
//一个是个字节,前面5个字节是备份的原API的头5个字节,后面5个字节是jmp OriginApi
__declspec(naked) int PostHook()
{
__asm
{
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
}
}
PreHook()
{
Call Hook;
goto PostHook;
}
测试代码:
#include <stdio.h>
DWORD UpperFunctionReturnAdd;//保存API返回地址
int nRet;
BYTE orig_code[5] = {0x90, 0x90, 0x90, 0x90, 0x90};//存放原始的指令
BYTE hook_code[5] = {0xe9, 0, 0, 0, 0};//存放跳转到MyMessageBoxA的指令
BYTE jmp_org_code[5] = {0xe9, 0, 0, 0, 0};//存放跳转到原起始地址后5字节的指令
int MyMessageBoxA();
int MyMessageBoxAA(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
);
int MyFunc();
void Hook();
int jmp_back();
ULONG OldFuncAddr;
ULONG MyFuncAddr;
ULONG jmp_backAddr;
//在修改前几个字节时,注意:取出的指令为完整的
int main()
{
Hook();
int rt = MessageBoxA(NULL, "Hello World", "Title", MB_OK);
return 0;
}
void Hook()
{
DWORD dwOldProtect;
OldFuncAddr = (ULONG)MessageBoxA;
// MyFuncAddr = MyMessageBoxA的实际地址
MyFuncAddr = (ULONG)MyMessageBoxAA;
// jmp_backAddr = jmp_back的实际地址
jmp_backAddr = (ULONG)jmp_back;
//修改内存为PAGE_EXECUTE_READWRITE
VirtualProtect((LPVOID)jmp_backAddr, 10, PAGE_EXECUTE_READWRITE, &dwOldProtect);
VirtualProtect((LPVOID)OldFuncAddr, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
//计算跳转地址,由OldFuncAddr向MyFuncAddr跳转
*((ULONG*)(hook_code+1)) = (ULONG)MyFuncAddr - (ULONG)OldFuncAddr - 5;
memcpy(orig_code,(BYTE *)OldFuncAddr, 5);
memcpy((BYTE*)OldFuncAddr, hook_code, 5);
//计算返回地址,从jmp_backAddr向OldFuncAddr跳转
*((ULONG*)(jmp_org_code+1)) = (ULONG)OldFuncAddr - (ULONG)jmp_backAddr - 5;
memcpy((BYTE *)jmp_backAddr, orig_code, 5);
memcpy((BYTE *)jmp_backAddr + 5, jmp_org_code, 5);
}
__declspec(naked) int jmp_back()
{
__asm
{
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
}
}
//MyMessageBoxA:在函数执行前进行自己的处理
__declspec(naked) int MyMessageBoxA()
{
printf("MyMessageBoxA is called\r\n");
MyFunc();////可以加入函数过程
__asm
{
//跳回MessageBoxA入口点
jmp jmp_back;
}
}
//MyMessageBoxA:在函数执行后进行自己的处理
__declspec(naked) int MyMessageBoxAA(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
)
{
printf("MyMessageBoxAA is called\r\n");
__asm
{
pop UpperFunctionReturnAdd
push offset s1;//返回地址为S1:
//跳回MessageBoxA入口点
jmp jmp_back;
s1: nop
}
MyFunc();
__asm
{
;//将原返回地址压栈
mov eax, 0;////演示:将返回结果改为0,也可由MyFunc返回
push UpperFunctionReturnAdd
ret;
}
}
int MyFunc()
{
printf("Hello World\r\n");
return 1;
}
参考:
1.http://bbs.pediy.com/showthread.php?t=69666