安全之路 -- 多核处理器同步函数KiIpiGenericCall函数在XP SP3下的寻址方式
前言
众所周知,在进行内核Inline Hook的时候,如果需要Patch的字节数少于等于8,我们可以借助CMPXCHG8B或者Interlockedcompareexchange64大法进行原子挂钩(这两个函数会申请总线锁和缓存锁,达到多核同步的效果);但是如果字节数大于8怎么办呢?我们可以使用DPC播报(KeInsertQueueDpc或者KeGenericCallDpc(XP未导出))完成,将所有核挂入DPC,然后由一个核实现挂钩,最早的安全软件们都是这么做的。但是,如果在申请DPC的过程中,有一个核因为某些原因处于DIRQL状态,无法执行DPC,最后就会导致系统挂死,高版本系统会触发DPC_WATCHDOG_VIOLATION(0x133)蓝屏。
所以鉴于以上原因,我们发现了另外一个新函数:KiIpiGenericCall,这个函数会把所有核提到IPI Level,这个IRQL甚至已经高过了时钟中断,这种级别可以打断几乎所有IRQL的例程,强行将CPU提升至IPI。不过遗憾的是,这个函数在XP并未导出。
下列提出两种比较WORK-AROUND的方式供大家选择:
- 找到导出的KeSetTargetProcessorDpc函数地址,然后按相对偏移+特征码找到下一个KiIpiGenericCall函数地址;
- 直接根据NT内核文件相对偏移RVA+特征码验证,找到函数。
.text:8050C6B4 ; ---------------------------------------------------------------------------
.text:8050C6B9 90 90 90 90 90 db 5 dup(90h)
.text:8050C6BE ; Exported entry 621. KeSetTargetProcessorDpc
.text:8050C6BE
.text:8050C6BE ; =============== S U B R O U T I N E =======================================
.text:8050C6BE
.text:8050C6BE ; Attributes: bp-based frame
.text:8050C6BE
.text:8050C6BE ; void __stdcall KeSetTargetProcessorDpc(PRKDPC Dpc, CCHAR Number)
.text:8050C6BE public _KeSetTargetProcessorDpc@8
.text:8050C6BE _KeSetTargetProcessorDpc@8 proc near ; CODE XREF: PopInvokeSystemStateHandler(x,x)+1B2↓p
.text:8050C6BE ; PoInitializePrcb(x)+48↓p ...
.text:8050C6BE
.text:8050C6BE Dpc = dword ptr 8
.text:8050C6BE Number = byte ptr 0Ch
.text:8050C6BE
.text:8050C6BE 8B FF mov edi, edi
.text:8050C6C0 55 push ebp
.text:8050C6C1 8B EC mov ebp, esp
.text:8050C6C3 8A 45 0C mov al, [ebp+Number]
.text:8050C6C6 8B 4D 08 mov ecx, [ebp+Dpc]
.text:8050C6C9 04 20 add al, 20h ; ' '
.text:8050C6CB 88 41 02 mov [ecx+2], al
.text:8050C6CE 5D pop ebp
.text:8050C6CF C2 08 00 retn 8
.text:8050C6CF _KeSetTargetProcessorDpc@8 endp
.text:8050C6CF
.text:8050C6CF ; ---------------------------------------------------------------------------
.text:8050C6D2 90 90 90 90 90 db 5 dup(90h)
.text:8050C6D7
.text:8050C6D7 ; =============== S U B R O U T I N E =======================================
.text:8050C6D7
.text:8050C6D7 ; Attributes: bp-based frame
.text:8050C6D7
.text:8050C6D7 ; __stdcall KiIpiGenericCall(x, x)
.text:8050C6D7 _KiIpiGenericCall@8 proc near ; CODE XREF: KiRestoreFastSyscallReturnState()+36↓p
.text:8050C6D7 ; KiAdjustInterruptTime(x,x)+3F↓p ...
.text:8050C6D7
.text:8050C6D7 arg_0 = dword ptr 8
.text:8050C6D7 arg_4 = dword ptr 0Ch
.text:8050C6D7
.text:8050C6D7 ; FUNCTION CHUNK AT .text:8051EA7C SIZE 0000000A BYTES
.text:8050C6D7
.text:8050C6D7 8B FF mov edi, edi
.text:8050C6D9 55 push ebp
.text:8050C6DA 8B EC mov ebp, esp
.text:8050C6DC 53 push ebx
.text:8050C6DD 56 push esi
.text:8050C6DE 8B 35 7C 86 4D 80 mov esi, ds:__imp__KeGetCurrentIrql@0 ; KeGetCurrentIrql()
.text:8050C6E4 FF D6 call esi ; KeGetCurrentIrql() ; KeGetCurrentIrql()
.text:8050C6E6 6A 02 push 2
.text:8050C6E8 59 pop ecx ; NewIrql
.text:8050C6E9 3A C1 cmp al, cl
.text:8050C6EB 0F 83 8B 23 01 00 jnb loc_8051EA7C
.text:8050C6F1
.text:8050C6F1 loc_8050C6F1: ; CODE XREF: KiIpiGenericCall(x,x)+123AA↓j
.text:8050C6F1 FF 15 74 86 4D 80 call ds:__imp_@KfRaiseIrql@4 ; KfRaiseIrql(x)
.text:8050C6F7 FF 75 0C push [ebp+arg_4]
.text:8050C6FA 8A D8 mov bl, al
.text:8050C6FC FF 55 08 call [ebp+arg_0]
.text:8050C6FF 8A CB mov cl, bl ; NewIrql
.text:8050C701 8B F0 mov esi, eax
.text:8050C703 FF 15 9C 86 4D 80 call ds:__imp_@KfLowerIrql@4 ; KfLowerIrql(x)
.text:8050C709 8B C6 mov eax, esi
.text:8050C70B 5E pop esi
.text:8050C70C 5B pop ebx
.text:8050C70D 5D pop ebp
.text:8050C70E C2 08 00 retn 8
.text:8050C70E _KiIpiGenericCall@8 endp
.text:8050C70E
本文为博主总结文章,欢迎转载,请注明出处。