首页
论坛
课程
招聘
[原创]扫盲之Api Hook 细析(一)
2008-6-6 16:16 32318

[原创]扫盲之Api Hook 细析(一)

2008-6-6 16:16
32318
//Author:Alex(Yock.W)
//转载请署名出处

//针对一些初学者扫盲,牛人就不用看了。希望别笑话……

前言 基础知识

本系列文章会对常用的几种API HOOK方法进行全面的分析。

Hook是什么?

钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。

钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。

Hook原理

每个Hook都有一个关联的链表,由系统维护,链表指针指向被Hook子程调用的回调函数:

LRESULT WINAPI HookCallBack(
int nCode,
WPARAM wParam,
LPARAM lParam);
nCode 事件代码
wParam 消息的类型
lParam 包含的消息

当被Hook的类型关联的消息发生时,系统会把这个消息传递到链表指针指向的Hook子程,由子程内部对消息进行处理,。

钩子安装与卸载

使用Windows提供的API SetWindowsHookEx来将Hook子程指针安装到Hook链表,安装的Hook子程始终在Hook链表头部。

HHOOK SetWindowsHookEx(
int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId);
idHook 安装的钩子类型
lpfn 子程函数指针
hMod 应用程序实例的句柄
dwThreadId Hook关联的线程标识符

当被Hook的消息类型发生时,系统会调用与这个Hook关联的链表头的Hook子程。由子程序决定是否要把这个事件传递给下个子程。Hook子程传递事件给下一个Hook子程需要调用使用Windows提供的API CallNextHookEx。

LRESULT CallNextHookEx(
HHOOK hhk,
int nCode,
WPARAM wParam,
LPARAM lParam);
hhk 当前钩子的句柄,由SetWindowsHookEx返回
nCode 事件代码
wParam 消息的类型
lParam 包含的消息

钩子在使用完,需调用UnHookWindowsHookEx来卸载。

UnHookWindowsHookEx(
HHOOK hhk);
Api Hook 的几种方法

一.修改函数过程映射的地址空间

Api Hook:

我们都知道,在调用函数的时候,会先Call 0xFFFFFF,来到函数映射在内存的地址空间里,这时候,传入的参数已入栈,那么,我们只需要修改函数头部,跳转到我们自定义的过程,这样,我们就达到了对API函数监视的目的。那么,我们需要多大的空间来写入代码让其跳转到我们自定义的过程里呢?

聪明的你肯定已经想到了。没错,只需5字节即可。Why? 看一下代码:

jmp 0xFFFFFFFF
jmp 指令占1字节,地址是4字节,所以,只需5字节即可。

有的看官可能会问了,为什么不用Call呢?

因为Call指令在实现跳转以后会通过ret返回到Call下面的指令继续执行,而Jmp指令实现跳转以后则全有子程来控制了。

我们知道了通过以上方法来修改函数头部数据来达到我们的目的,那么,我们先要得到指向函数头部的地址,通过LoadLibrary和GetProcAddress即可获取到我们要监视的函数在内存中映射的地址指针。我们以MessageBoxA为例:

typedef int (__stdcall *TMessageBoxA)(HWND hWnd,LPSTR lpText,LPSTR lpCaption,int uType);

extern "C" TMessageBoxA __fastcall GetPFuncAddr(LPSTR DllName,const char* FullName);

TMessageBoxA __fastcall GetPFuncAddr(LPSTR DllName,const char* FullName)
{
void *DllModule;
DllModule = LoadLibrary(DllName);
TMessageBoxA PAddr = (TMessageBoxA)GetProcAddress(DllModule,FullName);
return PAddr;
}
在得到了指向内存映射里函数头部的指针以后,我们下一步,来读出函数头部的5字节。为什么要读出函数头部的5字节呢?呵呵,当然是留作恢复时写会数据了。ReadProcessMemory:

BOOL ReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead);
hProcess 进程句柄
lpBaseAddress 读内存开始地址
lpBuffer 保存数据的指针
nSize 数据大小
lpNumberOfBytesRead //address of number of bytes read

BYTE *pFuncData = new BYTE[5];
LPDWORD iRead;

ReadProcessMemory((void *)GetCurrentProcess(),PMessageBoxA,pFuncData,5,iRead);

保存原函数入口点的数据以后,嘿嘿,当然是写入指向我们自己的函数地址了。

BYTE AsmCode[5];

AsmCode[0] = 0xE9;
__asm
{
lea eax, MyMessageBoxA
mov ebx,PMessageBoxA
sub eax,ebx
sub eax,5
mov dword ptr[AsmCode+1],eax
}
WriteProcessMemory((void *)GetCurrentProcess(),PMessageBoxA,AsmCode,5,iWrite);

MyMessageBoxA是我们自己构造的函数:

int WINAPI MyMessageBoxA(HWND hWnd,LPSTR lpText,LPSTR lpCaption,int uType)
{
//我们来弹出一个消息框,改变一下传入的标题参数值
//那么首要做的,就是先恢复MessageBoxA
WriteProcessMemory((void *)GetCurrentProcess(),PMessageBoxA,pFuncData,5,iWrite);
int Result = MessageBoxA(hWnd,lpText,"ApiHook Test",uType);
WriteProcessMemory((void *)GetCurrentProcess(),PMessageBoxA,AsmCode,5,iWrite);
return Result;
}
恢复API的原入口处数据:

WriteProcessMemory((void *)GetCurrentProcess(),PMessageBoxA,pFuncData,5,iWrite);
防止修改函数入口数据来进行API HOOK的方法:

在程序启动的时候保存关键API入口的5字节,定期检查一次,发现被修改了,改回即可。

----------------------------------------------------------------

下一篇中,会针对SEH技术实现API HOOK和如何防止SEH APIHOOK进行详解。

扫盲到此结束。

[注意] 欢迎加入看雪团队!base上海,招聘安全工程师、逆向工程师多个坑位等你投递!

收藏
点赞1
打赏
分享
最新回复 (47)
雪    币: 201
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xingzhian 活跃值 2008-6-6 16:24
2
0
不错不错啊
雪    币: 201
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xingzhian 活跃值 2008-6-6 16:25
3
0
期待下一篇文章,最好再讲讲怎么调试hook到程序的dll文件
雪    币: 343
活跃值: 活跃值 (163)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
学习班 活跃值 2008-6-6 22:40
4
0
支持一下,最好讲个如何HOOK DEVIVEIOCONTROL的例子.
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
泪无痕 活跃值 2008-6-6 23:31
5
0
学习了。呵呵,小菜扫盲成员
雪    币: 201
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
那年夏天 活跃值 2008-6-6 23:34
6
0
不错,学习了。
雪    币: 349
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
要学会编 活跃值 2008-6-7 00:17
7
0
我是盲成员 学习学习.谢谢楼主
雪    币: 521
活跃值: 活跃值 (134)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
笨笨雄 活跃值 14 2008-6-7 02:25
8
0
写完又写的东西,还是有那么多人没看过,干脆置顶,让大家看过够

WriteProcessMemory((void *)GetCurrentProcess(),PMessageBoxA,pFuncData,5,iWrite);
int Result = MessageBoxA(hWnd,lpText,"ApiHook Test",uType);
WriteProcessMemory((void *)GetCurrentProcess(),PMessageBoxA,AsmCode,5,iWrite);

不过这种方法,我还真第一次看到,学习了
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
AXIxuqing 活跃值 2008-6-7 10:37
9
0
很好很强大  凑字 呵呵
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
watsy 活跃值 2008-6-8 04:31
10
0
可惜我还没看过钩子
雪    币: 202
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小寒 活跃值 2008-6-8 17:52
11
0
学习..学习下,,新人来D
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
incrediman 活跃值 2008-6-9 21:02
12
0
感谢楼主的精彩讲解,我看了有以下两点疑问:
1、你文章前半部分提到的SetWindowsHookEx等API函数,你在你的例子里面没有用到。能否讲解它们的用法?
2、这个例子是否是HOOK自身进程的API函数(如MessageBoxA),我想HOOK的作用是不是更多在于HOOK别的进程的API函数?
---
小弟是菜鸟,说的可能不对,还望指点一二,万分感谢呀!
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
冬日骄阳 活跃值 2008-6-9 23:16
13
0
我是小菜  来看下
雪    币: 793
活跃值: 活跃值 (10)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
zyr零零发 活跃值 1 2008-6-10 09:40
14
0
学习学习再学习
雪    币: 217
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
百折不挠 活跃值 2008-6-10 11:34
15
0
WriteProcessMemory((void *)GetCurrentProcess(),PMessageBoxA,pFuncData,5,iWrite);
这句,是不是只要HOOK了自己进程里的API,其它进程序里调用API也被HOOK了?
雪    币: 233
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tendychen 活跃值 2008-6-10 17:37
16
0
呵呵,很好啊,谢谢
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
incrediman 活跃值 2008-6-10 18:40
17
0
以我的理解,这个例子演示的是动态加截一个API,然后对这个API进行HOOK,修改的当前进程的虚拟内存,实现HOOK,然后调用这个经过HOOK的API,再接着就马上unhook了。总的来说就是自已HOOK自已动态加载的API,以别的进程无关,所以说别的进程调用这个API,即user32.dll时,是不会有任何被HOOK的迹象的。
雪    币: 201
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
bjalq 活跃值 2008-6-11 09:52
18
0
不错,没见过这方面的东西,学习,收藏。
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lsKaKaXi 活跃值 2008-6-11 22:12
19
0
很好..藏之
雪    币: 120
活跃值: 活跃值 (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
YockW 活跃值 1 2008-6-12 13:24
20
0
这里讲解的可以用于HOOK自身进程和其他的进程。
关键看你如何利用了

SetWindowsHookEx的函数原型在文中我已经提到了 :)

通过 GetWindowThreadProcessId  获取到要HOOK的窗口的线程标识符

通过 SetWindowsHookEx 安装钩子

钩子安装成功后,修改API的入口达到API HOOK的目的


:)
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
KUKU军 活跃值 2008-6-12 15:10
21
0
大家都这么厉害!惭愧
雪    币: 5280
活跃值: 活跃值 (535)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
RuShi 活跃值 2008-6-12 15:31
22
0
受教了,谢谢分享
雪    币: 201
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhoumin 活跃值 2008-6-13 00:13
23
0
很好的学习资料, 如能加上一个练习演示实例就好了~~~
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
novasense 活跃值 2008-6-13 10:38
24
0
看来我编程语言基础不行 看的不太懂
雪    币: 205
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wowzjj 活跃值 2008-6-15 15:40
25
0
好像楼主漏说了一点2个函数返回值,参数原形要保持一致
游客
登录 | 注册 方可回帖
返回