KiFastCallEntry Hook

发布时间 2023-03-24 11:41:09作者: Hanpto

KiFastCallEntry函数有什么用???

Ring0层Zw系列函数(如ZwSetEvent)在设置完函数服务号之后会调用KiSystemService函数,
在KiSystemService函数中又会跳转到KiFastCallEntry函数内部获取SSDT表的基地址,系统函数服务号,进而获得服务函数的地址,接着调用服务函数。
KiFastCallEntry中获得SSDT表基址,服务函数地址的汇编语句如下:
8053e61c 8b3f mov edi,dword ptr [edi]
8053e61e 8b1c87 mov ebx,dword ptr [edi+eax*4];ebx为服务函数地址


83e921a4 2be1 sub esp,ecx
83e921a6 c1e902 shr ecx,2

其中edi为SSDT的基地址,eax为函数服务号,ebx为服务函数地址。
调用服务函数的汇编语句为:
8053e636 ffd3 call ebx // ebx 系统服务 调用系统服务函数


8053e638 8be5 mov esp,ebp //KiFastCallEntryRetAddress(上一步调用的系统服务函数的返回地址)

KiFastCallEntryHook是什么???
KiFastCallEntryHook是指在KiFastCallEntry函数体中将系统获取到的服务函数地址进行替换,换成对应的过滤(Fake)函数

KiFastCallEntryHook怎么实现???
1.首先利用SSDTHOOK对某个系统服务函数进行Hook,在对应的Kake系统服务函数中使用汇编指令:
mov eax, dword ptr[ebp + 4] 获得EIP,即KiFastCallEntry函数中调用系统服务函数后的下一条指令的地址,即上文①处的地址
2.恢复上面的SSDTHOOK,接着使用栈回溯的思想,从①处的地址开始向上搜索机器码{0x2b,0xe1,0xc1,0xe9,0x02}找到对应的指令
83e921a4 2be1 sub esp,ecx
83e921a6 c1e902 shr ecx,2
在此处进行KiFastCallEntryHook,将其换成指令:
8053e621 e9ea194077 jmp KifastcallentryHook!FakeKiFastCallEntry(f7940010) ;其中FakeKiFastCallEntry为自己实现的一个函数
FakeKiFastCallEntry的主要操作是更改服务函数的地址,即ebx或edx(根据操作系统来看到底是ebx还是edx)
FakeKiFastCallEntry的代码见下面:

点击查看代码
_declspec(naked) VOID FakeKiFastCallEntry()
{
	__asm
	{
		mov edi, edi    //对齐内存
		pushfd		  //eax,ecx,edx,ebx,esp,ebp,esi,edi	
		pushad		  //eflags
		push edi	  //ServiceTable基地址
		push ebx        //服务函数地址
		push eax        //函数服务号
		call SysCallfilter
		/*
		edi ---->esp
		esi ---->esp+04h
		ebp ---->esp+08h
		esp ---->esp+0Ch
		ebx ---->esp+10h 服务函数地址
		edx 
		ecx
		eax
		pushad
		*/
						  //ULONG _stdcall SysCallfilter(ULONG FunctionService, ULONG FunctionAddress, ULONG ServiceTableBase)
		mov  dword ptr[esp + 10h], eax   // esb+10h == ebx 服务函数地址   eax == SysCallfilter的返回值,即返回的过滤函数的地址
		
		popad
		popfd
		sub esp, ecx
		shr ecx, 2
		push PatchAddress1;   //  相当于  PatchAddress1 所保存的值当成了EIP  
		ret                   //   ret == pop eip
	}
}

其中自定义函数SysCallfilter用来根据函数服务号可实现对不同函数的Hook(过滤)

点击查看代码
ULONG _stdcall SysCalfilter(ULONG FunctionService, ULONG FunctionAddress, ULONG ServiceTableBase)
{
	//得到Index,判断Index是不是ZwTerminateProcess,如果是,返回一个新的函数地址,而不是SSDT原先的函数地址
	//某某程序调用了TerminateProcess函数
	ULONG ServiceIndex = SSDT_SERVICE(ZwTerminateProcess);
	if (ServiceIndex == FunctionService)
	{
		return (ULONG)FakeNtTerminateProcess;
	}
	else if (0)//此处可继续对ServiceIndex进行判断,Hook其他函数
	{

	}
	return FunctionAddress;//虚假地址

}
在mov ebx,dword ptr [edi+eax*4]处进行Hook,可将服务函数根据服务函数号的不同动态替换成对应的过滤函数(FakeFunction)。

注意点:不同的操作系统,汇编语句mov ebx,dword ptr [edi+eax4]可能不同,有的是mov edx,dword ptr [edi+eax4]。不过这句汇编语句把服务函数地址是给了ebx还是edx,系统最后都会把服务函数地址给ebx,再通过call ebx指令来调用服务函数。

KiFastCallEntryHook与SSDTHook类似,都是对系统服务函数进行挂钩。KiFastCallEntryHook为通过SSDTHook动态改变系统服务函数的地址(返回一个Fake函数地址,SSDT对应服务函数地址并未被改变,只是改变了运行时的ebx);SSDTHook则是找到对应函数服务号的函数,进行InlineHook,从原服务函数中跳转到Fake函数。

使用Windbg观察调用Zw系列函数的过程(以ZwSetEvent为例,Win7 x86)

点击查看代码
kd> u ZwSetEvent
nt!ZwSetEvent:
83e91090 b8 43 01 00 00      mov     eax,143h			;函数服务号323
83e91095 8d542404        lea     edx,[esp+4]
83e91099 9c              pushfd
83e9109a 6a08            push    8
83e9109c e84d0f0000      call    nt!KiSystemService (83e91fee)
83e910a1 c20800          ret     8
--------------------------------------------------------------
kd> u KiSystemService l 50				(int 2e指令  先填充TRAP_FRAME)
nt!KiSystemService:			
83e91fee 6a00            push    0			;填充ErrCode
83e91ff0 55              push    ebp		;填充Ebp
83e91ff1 53              push    ebx		;填充Ebx
83e91ff2 56              push    esi		;填充Esi
83e91ff3 57              push    edi		;填充Edi
83e91ff4 0fa0            push    fs		;填充fs段寄存器
83e91ff6 bb30000000      mov     ebx,30h		
83e91ffb 668ee3          mov     fs,bx		;让fs寄存器指向KPRC
83e91ffe bb23000000      mov     ebx,23h		
83e92003 8edb            mov     ds,bx
83e92005 8ec3            mov     es,bx
83e92007 648b3524010000  mov     esi,dword ptr fs:[124h]		;取出ETHREAD赋给Esi
83e9200e 64ff3500000000  push    dword ptr fs:[0]	
83e92015 64c70500000000ffffffff mov dword ptr fs:[0],0FFFFFFFFh	;填充ExceptionList
83e92020 ffb63a010000    push    dword ptr [esi+13Ah]			;填充PreviousMode
83e92026 83ec48          sub     esp,48h					;让esp指向陷阱帧头部
83e92029 8b5c246c        mov     ebx,dword ptr [esp+6Ch]		;将进入内核前的cs的值赋值给ebx
83e9202d 83e301          and     ebx,1					;将ebx与1进行与运算,如果为1说明是从用户模式切换进内核模式,将其保存到线程的先前模式中
83e92030 889e3a010000    mov     byte ptr [esi+13Ah],bl		;将b1赋值给当前线程的先前模式
83e92036 8bec            mov     ebp,esp					;让ebp指向陷阱帧的头部
83e92038 8b9e28010000    mov     ebx,dword ptr [esi+128h]		;将线程的TrapFrame保存到ebx中
83e9203e 895d3c          mov     dword ptr [ebp+3Ch],ebx		;填充edx		
83e92041 83652c00        and     dword ptr [ebp+2Ch],0			
83e92045 f64603df        test    byte ptr [esi+3],0DFh			;判断当前线程是否处于调试模式   test    byte ptr [esi+_ETHREAD.Tcb.DebugActive],0DFh	
83e92049 89ae28010000    mov     dword ptr [esi+128h],ebp		;将当前陷阱帧头部的地址赋值给线程的TrapFrame
83e9204f fc              cld
83e92050 0f859afeffff    jne     nt!Dr_kss_a (83e91ef0)		;如果DebugActive不为-1,则处于调试模式,会跳转到Dr_kss_a执行
83e92056 8b5d60          mov     ebx,dword ptr [ebp+60h]		;将ebp赋值给ebx
83e92059 8b7d68          mov     edi,dword ptr [ebp+68h]		;将eip赋值给edi
83e9205c 89550c          mov     dword ptr [ebp+0Ch],edx		;用edx填充DbgArgPointer
83e9205f c74508000ddbba  mov     dword ptr [ebp+8],0BADB0D00h		;为DbgArgMask赋值
83e92066 895d00          mov     dword ptr [ebp],ebx			;将ebx保存的内容赋值给DbgEbp
83e92069 897d04          mov     dword ptr [ebp+4],edi			;将edi的内容赋值给DbgEip
83e9206c fb              sti
83e9206d e9dd000000      jmp     nt!KiFastCallEntry+0x8f (83e9214f)
---------------------------------------------------------------------
nt!KiFastCallEntry:				(sysenter指令,先填充内核栈,再填充TRAP_FRAME)
83e920c0 b923000000      mov     ecx,23h					
83e920c5 6a30            push    30h
83e920c7 0fa1            pop     fs				;fs执行KPCR
83e920c9 8ed9            mov     ds,cx				
83e920cb 8ec1            mov     es,cx				;将ds和es都用0x23赋值
83e920cd 648b0d40000000  mov     ecx,dword ptr fs:[40h]	;将TSS赋值给ecx
83e920d4 8b6104          mov     esp,dword ptr [ecx+4]		;取出在Ring0执行时使用的esp
83e920d7 6a23            push    23h				;填充HardWareSegSS
83e920d9 52              push    edx				;填充HardWareEsp
83e920da 9c              pushfd					;填充Eflags
83e920db 6a02            push    2					
83e920dd 83c208          add     edx,8				;edx+8,指向保存参数的栈地址
83e920e0 9d              popfd					
83e920e1 804c240102      or      byte ptr [esp+1],2
83e920e6 6a1b            push    1Bh				;填充CS
83e920e8 ff350403dfff    push    dword ptr ds:[0FFDF0304h]	;填充EIP
83e920ee 6a00            push    0					;填充陷阱帧,和上述的KiSystemService类似
83e920f0 55              push    ebp
83e920f1 53              push    ebx
kd> u KiFastCallEntry l 100
nt!KiFastCallEntry:
83e920c0 b923000000      mov     ecx,23h
83e920c5 6a30            push    30h
83e920c7 0fa1            pop     fs
83e920c9 8ed9            mov     ds,cx
83e920cb 8ec1            mov     es,cx
83e920cd 648b0d40000000  mov     ecx,dword ptr fs:[40h]
83e920d4 8b6104          mov     esp,dword ptr [ecx+4]
83e920d7 6a23            push    23h
83e920d9 52              push    edx
83e920da 9c              pushfd
83e920db 6a02            push    2
83e920dd 83c208          add     edx,8
83e920e0 9d              popfd
83e920e1 804c240102      or      byte ptr [esp+1],2
83e920e6 6a1b            push    1Bh
83e920e8 ff350403dfff    push    dword ptr ds:[0FFDF0304h]
83e920ee 6a00            push    0
83e920f0 55              push    ebp
83e920f1 53              push    ebx
83e920f2 56              push    esi
83e920f3 57              push    edi
83e920f4 648b1d1c000000  mov     ebx,dword ptr fs:[1Ch]
83e920fb 6a3b            push    3Bh
83e920fd 8bb324010000    mov     esi,dword ptr [ebx+124h]
83e92103 ff33            push    dword ptr [ebx]
83e92105 c703ffffffff    mov     dword ptr [ebx],0FFFFFFFFh
83e9210b 8b6e28          mov     ebp,dword ptr [esi+28h]
83e9210e 6a01            push    1
83e92110 83ec48          sub     esp,48h
83e92113 81ed9c020000    sub     ebp,29Ch
83e92119 c6863a01000001  mov     byte ptr [esi+13Ah],1
83e92120 3bec            cmp     ebp,esp
83e92122 7597            jne     nt!KiFastCallEntry2+0x49 (83e920bb)
83e92124 83652c00        and     dword ptr [ebp+2Ch],0
83e92128 f64603df        test    byte ptr [esi+3],0DFh
83e9212c 89ae28010000    mov     dword ptr [esi+128h],ebp
83e92132 0f8538feffff    jne     nt!Dr_FastCallDrSave (83e91f70)
83e92138 8b5d60          mov     ebx,dword ptr [ebp+60h]
83e9213b 8b7d68          mov     edi,dword ptr [ebp+68h]
83e9213e 89550c          mov     dword ptr [ebp+0Ch],edx
83e92141 c74508000ddbba  mov     dword ptr [ebp+8],0BADB0D00h
83e92148 895d00          mov     dword ptr [ebp],ebx
83e9214b 897d04          mov     dword ptr [ebp+4],edi
83e9214e fb              sti
83e9214f 8bf8            mov     edi,eax   ;此处为KiSystemService函数的跳转地址		;将服务号赋值给edi
83e92151 c1ef08          shr     edi,8						;服务号右移8位,此时保留了系统服务号的8~13位			0~11位表内系统服务索引
83e92154 83e710          and     edi,10h						;将edi与0x10与运算,会保留服务号的12~13位,此时edi=0x00或0x10	12~13位表的索引
83e92157 8bcf            mov     ecx,edi						;将计算后结果保存到ecx中
83e92159 03bebc000000    add     edi,dword ptr [esi+0BCh]			;将edi加上系统服务表基地址 add     edi,dword ptr [esi+_KTHREAD.ServiceTable]
83e9215f 8bd8            mov     ebx,eax						;将服务号赋值给ebx
83e92161 25ff0f0000      and     eax,0FFFh					;保留系统服务号低12位(与系统服务表Limit域做比较,如果超过Limit说明越界)
83e92166 3b4708          cmp     eax,dword ptr [edi+8]				;判断是否超过界限
83e92169 0f8333fdffff    jae     nt!KiBBTUnexpectedRange (83e91ea2)		;越界异常处理
83e9216f 83f910          cmp     ecx,10h						;检查ecx是否等0x10,如果不等,使用基本的SDT,否则使用子系统SDT
83e92172 751a            jne     nt!KiFastCallEntry+0xce (83e9218e)
83e92174 8b8e88000000    mov     ecx,dword ptr [esi+88h]					
83e9217a 33f6            xor     esi,esi
83e9217c 0bb1700f0000    or      esi,dword ptr [ecx+0F70h]
83e92182 740a            je      nt!KiFastCallEntry+0xce (83e9218e)
83e92184 52              push    edx
83e92185 50              push    eax
83e92186 ff154ce9fb83    call    dword ptr [nt!KeGdiFlushUserBatch (83fbe94c)]
83e9218c 58              pop     eax
83e9218d 5a              pop     edx
83e9218e 64ff05b0060000  inc     dword ptr fs:[6B0h]			;准备调用相应的函数
83e92195 8bf2            mov     esi,edx					;将参数地址赋值给esi
83e92197 33c9            xor     ecx,ecx					;ecx清零
83e92199 8b570c          mov     edx,dword ptr [edi+0Ch]		;将Number域赋值给edx
83e9219c 8b3f            mov     edi,dword ptr [edi]			;此处为获取SSDT基地址	取出Base域赋值给edi
83e9219e 8a0c10          mov     cl,byte ptr [eax+edx]			;将所需的参数长度赋值给cl
83e921a1 8b1487          mov     edx,dword ptr [edi+eax*4]		;此处为获取服务函数地址	取出要调用的函数地址
83e921a4 2be1            sub     esp,ecx					;此处为Hook点,将sub和shr指令换成jmp FakeKiFastCallEntry(自定义函数)  原指定的作用是将栈顶向上移动存放参数
83e921a6 c1e902          shr     ecx,2					;ecx除4,得到参数个数
83e921a9 8bfc            mov     edi,esp					;将栈顶赋值给edi
83e921ab 3b351ce7fb83    cmp     esi,dword ptr [nt!MmUserProbeAddress (83fbe71c)]		
83e921b1 0f832e020000    jae     nt!KiSystemCallExit2+0xa5 (83e923e5)
83e921b7 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
83e921b9 f6456c01        test    byte ptr [ebp+6Ch],1			;对陷阱帧中保存的cs进行判断		
83e921bd 7416            je      nt!KiFastCallEntry+0x115 (83e921d5)	;_KiSystemServiceCopyArguments(在此函数中完整参数的传递:用户栈->内核栈 此时edi保存内核栈参数地址,esi保存用户栈参数地址,edx保存服务函数地址)
83e921bf 648b0d24010000  mov     ecx,dword ptr fs:[124h]
83e921c6 8b3c24          mov     edi,dword ptr [esp]
83e921c9 89993c010000    mov     dword ptr [ecx+13Ch],ebx
83e921cf 89b92c010000    mov     dword ptr [ecx+12Ch],edi
83e921d5 8bda            mov     ebx,edx
83e921d7 f60508b9f88340  test    byte ptr [nt!PerfGlobalGroupMask+0x8 (83f8b908)],40h
83e921de 0f954512        setne   byte ptr [ebp+12h]
83e921e2 0f858c030000    jne     nt!KiServiceExit2+0x17b (83e92574)
83e921e8 ffd3            call    ebx					;此处为调用系统服务函数
83e921ea f6456c01        test    byte ptr [ebp+6Ch],1			;此处为系统服务函数调用完后返回地址
83e921ee 7434            je      nt!KiFastCallEntry+0x164 (83e92224)
83e921f0 8bf0            mov     esi,eax
83e921f2 ff156851e583    call    dword ptr [nt!_imp__KeGetCurrentIrql (83e55168)]
83e921f8 0ac0            or      al,al
83e921fa 0f853b030000    jne     nt!KiServiceExit2+0x142 (83e9253b)
83e92200 8bc6            mov     eax,esi
83e92202 648b0d24010000  mov     ecx,dword ptr fs:[124h]
83e92209 f68134010000ff  test    byte ptr [ecx+134h],0FFh
83e92210 0f8543030000    jne     nt!KiServiceExit2+0x160 (83e92559)
83e92216 8b9184000000    mov     edx,dword ptr [ecx+84h]
83e9221c 0bd2            or      edx,edx
83e9221e 0f8535030000    jne     nt!KiServiceExit2+0x160 (83e92559)
83e92224 8be5            mov     esp,ebp
83e92226 807d1200        cmp     byte ptr [ebp+12h],0
83e9222a 0f8550030000    jne     nt!KiServiceExit2+0x187 (83e92580)
83e92230 648b0d24010000  mov     ecx,dword ptr fs:[124h]
83e92237 8b553c          mov     edx,dword ptr [ebp+3Ch]
83e9223a 899128010000    mov     dword ptr [ecx+128h],edx

注:
x86下:KiFastCallEntry地址可以通过Hook SSDT服务函数找到,也可以使用Windbg读取msr寄存器0x176获得

点击查看代码
kd> rdmsr 0x176
msr[176] = 00000000`83e920c0
kd> u 00000000`83e920c0
nt!KiFastCallEntry:
83e920c0 b923000000      mov     ecx,23h

用户栈参数拷贝到内核栈中
.text:004069A7 _KiSystemServiceCopyArguments@0 proc near
.text:004069A7 ; CODE XREF: KiSystemServiceAccessTeb()+39↑j
.text:004069A7 ; DATA XREF: KiPreprocessAccessViolation(x,x,x):loc_4628DF↓o
.text:004069A7 rep movsd ;拷贝参数
.text:004069A9 call edx ;调用函数
.text:004069A9 _KiSystemServiceCopyArguments@0 endp