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+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