InstallHook proc invoke SetWindowsHookEx,WH_GETMESSAGE,addr GetMsgProc,hInstance,NULL mov hHook,eax ret InstallHook endp
UninstallHook proc invoke UnhookWindowsHookEx,hHook invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8 ret UninstallHook endp
GetApi proc DllNameAddress:DWORD,ApiNameAddress:DWORD
invoke GetModuleHandle,DllNameAddress ;取DLL模块句柄 .if eax==NULL invoke LoadLibrary ,DllNameAddress ;加载DLL .endif invoke GetProcAddress,eax,ApiNameAddress ;取API地址
mov eax,eax ret
GetApi endp
;============================下面是核心部分=========================
WriteApi proc Process:DWORD ,Papi:DWORD,Ptype:DWORD,Psize:DWORD
LOCAL mbi:MEMORY_BASIC_INFORMATION LOCAL msize:DWORD
;返回页面虚拟信息 invoke VirtualQueryEx,Process, Papi,addr mbi,SIZEOF MEMORY_BASIC_INFORMATION
;修改为可读写模式
invoke VirtualProtectEx,Process, mbi.BaseAddress,8h,PAGE_EXECUTE_READWRITE,addr
mbi.Protect
;开始写内存
invoke WriteProcessMemory,Process, Papi, Ptype,Psize ,NULL
PUSH eax
;改回只读模式
invoke VirtualProtectEx,Process,mbi.BaseAddress,8h,PAGE_EXECUTE_READ,addr mbi.Protect
pop eax
ret
WriteApi endp
;替代的API,参数要和原来一样
MyAPI proc bs:DWORD ,dwReserved:DWORD
invoke MessageBox, NULL, CommandLine, addr mdb, MB_YESNO ;弹出信息框选择是否阻止
.if eax==7 ;如果选择否
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8 ;先还原API invoke ExitWindowsEx,bs,dwReserved ;再调用API invoke WriteApi,WProcess,Papi1, addr hacker ,sizeof HOOKAPI ;调用完后再改回来 .endif
mov eax,TRUE ret
MyAPI endp
End DllEntry
===============================hookdll.def=============================
LIBRARY hookdll EXPORTS InstallHook EXPORTS UninstallHook
=====[ 4.2. 分析 ]==============================================
HOOKAPI struct a byte ? PMyapi DWORD ? d BYTE ? e BYTE ? HOOKAPI ends
为了便于理解和使用,我定义了一个结构:这个结构有4个成员,第一个成员a,是个字节型,我用来放
0B8h(mov eax),PMyapi一个整数型,用来放我们的替代API函数的地址(0X000),第3个和第4个成员我分别
用来放JMP和EAX(jmp eax)那么连起来就是 mov,0X0000 ; jmp eax
.if reason==DLL_PROCESS_ATTACH push hInst pop hInstance
invoke GetCommandLine mov CommandLine,eax
;初始化
mov hacker.a,0B8h ;mov eax, ;mov hacker.d PMyapi ;0x0000 mov hacker.d,0FFh ;jmp mov hacker.e, 0E0h ;eax
invoke GetCurrentProcess
mov WProcess ,eax
当DLL加载时,我们先保存模块句柄,读取程序命令行,然后初始化HOOKAPI结构,写入我们要写到内存的
指令(PMyapi以后写入)并调用GetCurrentProcess取出进程伪句柄方便以后写内存.
invoke GetApi,addr DllName1,addr ApiName1 mov Papi1,eax
invoke ReadProcessMemory,WProcess,Papi1,addr ApiBak1,8,NULL mov hacker.PMyapi,offset MyAPI ;0x0000
invoke WriteApi,WProcess,Papi1, addr hacker ,size HOOKAPI ;HOOK API
接下来用子程GetApi取出要挂勾API的入口点,并用ReadProcessMemory读出入口点8字节备份之,写入 PMyapi调用子程WriteApi改写API的入口点,这个子程我不准备详细说了,它非常的简单,无非就是几个
API的调用.它的核心就是通过WriteProcessMemory改写内存.
.if reason==DLL_PROCESS_DETACH
invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8
.endif
mov eax,TRUE ret
如果这个DLL被卸载了,那么那个在DLL里的替代函数(MyAPI)将是无效的,如果这个时候程序再调用这
个API,将出现非法操作,因此在DLL卸载前,我们必须还原API.
总结一下,现在只要程序加载这个DLL,这个程序的ExitWindowsEx就会被我们勾住,接下来要怎样才能
让所有的程序都加载这个DLL呢?这就需要安装全局勾子:
InstallHook proc invoke SetWindowsHookEx,WH_GETMESSAGE,addr GetMsgProc,hInstance,NULL invoke WriteApi,WProcess,Papi1, addr hacker ,sizeof HOOKAPI
mov hHook,eax ret InstallHook endp
通过SetWindowsHookEx安装勾子,最后一个参数可以决定该钩子是局部的还是系统范围的。如果该值
为NULL,那么该钩子将被解释成系统范围内的,那它就可以监控所有的进程及它们的线程。
如果该函数调用成功的话,将在eax中返回钩子的句柄,否则返回NULL。我们必须保存该句柄,因为后
面我们还要它来卸载钩子,可以看出,我们创建的Hook类型是WH_CALLWNDPROC类型,该类型的Hook在进程
与系统一通信时就会被加载到进程空间,从而调用dll的初始化函数完成真正的Hook,值得一提的是:因
为要调用SetWindowsHookEx来安装钩子,我们GUI程序的这个DLL不会被
UnhookWidowHookEx卸载,也就只有一次DLL_PROCESS_ATTACH事件,因此这里再要
HOOK API一次!
我们回头来看看钩子回调函数:
GetMsgProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD invoke CallNextHookEx,hHook,nCode,wParam,lParam mov eax,TRUE ret GetMsgProc endp
可以看到这里只是调用CallNextHookEx将消息交给Hook链中下一个环节处理,因为这里API函数 SetWindowsHookEx的唯一作用就是让进程加载我们的dll。
UninstallHook proc invoke UnhookWindowsHookEx,hHook invoke WriteApi,WProcess,Papi1, addr ApiBak1 ,8 ret UninstallHook endp
要卸载一个钩子时调用UnhookWidowHookEx函数,该函数仅有一个参数,就是欲卸载的钩子的句柄。钩
子卸载后我们也要还原我们GUI程序的API.
LIBRARY hookdll EXPORTS InstallHook EXPORTS UninstallHook
我们公开DLL里的InstallHook和UninstallHook函数,方便程序调用,这样我们只要在另外的程序中调
用InstallHook便可安装全局勾子,勾住所有程序中的API:ExitWindowsEx,执行我们自定的子程!
如果不需要了,可以调用UninstallHook卸载全局勾子.
请注意:对于远程钩子,钩子函数必须放到DLL中,它们将从DLL中映射到其它的进程空间中去。当
WINDOWS映射DLL到其它的进程空间中去时,不会把数据段也进行映射。简言之,所有的进程仅共享DLL
的代码,至于数据段,每一个进程都将有其单独的拷贝。这是一个很容易被忽视的问题。您可能想当然
的以为,在DLL中保存的值可以在所有映射该DLL的进程之间共享。在通常情况下,由于每一个映射该
DLL的进程都有自己的数据段,所以在大多数的情况下您的程序运行得都不错。但是钩子函数却不是如
此。对于钩子函数来说,要求DLL的数据段对所有的进程也必须相同。这样您就必须把数据段设成共享
的:
一般来说, 目标文件有三个段, 分别是 text/data/bss 段.
.text 段放置代码, 是只读且可运行段
.data 段放置静态数据, 这些数据会被放置入 exe 文件. 这个段是可读写, 但是不能运行的.
.bss 段放置动态数据, 这些数据不被放入 exe 文件, 在exe文件被加载入内存后才分配的空间.
你可以通过在链接开关中指定段的属性来实现:
/SECTION:name,[E][R][W][S][D][K][L][P][X]
其中S表示共享,已初期化的段名是.data,未初始化的段名是.bss。假如您想要写一个包含钩子函数的
DLL,而且想使它的未初始化的数据段在所有进程间共享,您必须这么做: link /section:.bss[S] /DLL /SUBSYSTEM:WINDOWS ..........
否则,您的全局勾子将不能正常工作!
=====[ 5. 结束语 ]================================================
我欢迎任何人提出更多的这里没有提到的挂钩方法,我肯定那会有很多。同样欢迎补充我介绍得不
是很详细的方法。也可以把我懒得写的其它方法完成,把源代码发给我。这篇文档的目的是演示挂钩技
术的细节,我希望我做到了。 ============================[ End ]========================
水平有限,欢迎大家指出错漏之处。QQ:23453161 Email:kker.cn@163.com
例子源程序(MASM+RadASM和Windows XP2系统下编译通过): 上一页 1 2 |