前几天写了一篇 360内核 inline Hook 分析 有朋友感觉我没有把360的Hook的函数说清楚以至于产生误会。上一篇博客确实没有把这个问题说清楚。 在上篇文章中说到360是Hook nt!KiFastCallEntry+0xe1 这个位置是没有错的,只是严格的讲是在_KiSystemServiceRepeat 当中进行的Hook,很有意思的是_KiSystemServiceRepeat 是被包含在_KiFastCallEntry这个函数当中,而又从外部可以独立调用。 为什么要在这儿Hook呢?这要从ring3进入ring0的过程说起。 我们知道从ring3 进入ring0,有两种方法一种是int 2E;另一种,就是我们常用的sysenter;前一种现在已经不用了,速度太慢主要是在windows 2000当中用。现在主要是用sysenter。当在ring3中调用sysenter就会发生模式的切换从ring3->ring0。sysenter这条指令Intel是优化过的,主要是借助三个MSR寄存器来完成加速过程的,不过这两种方法最终都要确定内核的调用例程的位置,这点是不变的。那么这个例程是什么呢?其实最终就是调用了内核当中的_KiSystemService。 代码如下: PUBLIC _KiSystemService _KiSystemService proc ENTER_SYSCALL kss_a, kss_t ; set up trap frame and save state jmp _KiSystemServiceRepeat _KiSystemService endp
上面的函数很简单就是跳转到了_KiSystemServiceRepeat。
那么有人可能会问 SSDT,Shadow SSDT这两个表是什么时候被用到了,答案就在_KiSystemServiceRepeat。
注意:_KiSystemServiceRepeat这个函数是被包含在_KiFastCallEntry当中的,我就把_KiFastCallEntry全部贴上来。
360 Hook的地方我已经高亮标记出来了,真正调用SSDT例程的位置我也标记了。
PUBLIC _KiFastCallEntry _KiFastCallEntry proc
; ; Sanitize the segment registers ; mov ecx, KGDT_R3_DATA OR RPL_MASK push KGDT_R0_PCR pop fs mov ds, ecx mov es, ecx
; ; When we trap into the kernel via fast system call we start on the DPC stack. We need ; shift to the threads stack before enabling interrupts. ; mov ecx, PCR[PcTss] ; mov esp, [ecx]+TssEsp0
push KGDT_R3_DATA OR RPL_MASK ; Push user SS push edx ; Push ESP pushfd Kfsc10: push 2 ; Sanitize eflags, clear direction, NT etc add edx, 8 ; (edx) -> arguments popfd ; .errnz(EFLAGS_INTERRUPT_MASK AND 0FFFF00FFh) or byte ptr [esp+1], EFLAGS_INTERRUPT_MASK/0100h ; Enable interrupts in eflags
push KGDT_R3_CODE OR RPL_MASK ; Push user CS push dword ptr ds:[USER_SHARED_DATA+UsSystemCallReturn] ; push return address push 0 ; put pad dword for error on stack push ebp ; save the non-volatile registers push ebx ; push esi ; push edi ; mov ebx, PCR[PcSelfPcr] ; Get PRCB address push KGDT_R3_TEB OR RPL_MASK ; Push user mode FS mov esi, [ebx].PcPrcbData+PbCurrentThread ; get current thread address ; ; Save the old exception list in trap frame and initialize a new empty ; exception list. ;
push [ebx].PcExceptionList ; save old exception list mov [ebx].PcExceptionList, EXCEPTION_CHAIN_END ; set new empty list mov ebp, [esi].ThInitialStack
; ; Save the old previous mode in trap frame, allocate remainder of trap frame, ; and set the new previous mode. ; push MODE_MASK ; Save previous mode as user sub esp,TsPreviousPreviousMode ; allocate remainder of trap frame sub ebp, NPX_FRAME_LENGTH + KTRAP_FRAME_LENGTH mov byte ptr [esi].ThPreviousMode,MODE_MASK ; set new previous mode of user ; ; Now the full trap frame is build. ; Calculate initial stack pointer from thread initial stack to contain NPX and trap. ; If this isn't the same as esp then we are a VX86 thread and we are rejected ;
cmp ebp, esp jne short Kfsc91
; ; Set the new trap frame address. ; and dword ptr [ebp].TsDr7, 0 test byte ptr [esi].ThDebugActive, 0ffh ; See if we need to save debug registers mov [esi].ThTrapFrame, ebp ; set new trap frame address
jnz Dr_FastCallDrSave ; if nz, debugging is active on thread
Dr_FastCallDrReturn: ;
SET_DEBUG_DATA ; Note this destroys edi sti ; enable interrupts
FpoValue = 0
_KiSystemServiceRepeat: mov edi, eax ; copy system service number shr edi, SERVICE_TABLE_SHIFT ; isolate service table number and edi, SERVICE_TABLE_MASK ; mov ecx, edi ; save service table number add edi, [esi]+ThServiceTable ; compute service descriptor address mov ebx, eax ; save system service number and eax, SERVICE_NUMBER_MASK ; isolate service table offset
; ; If the specified system service number is not within range, then attempt ; to convert the thread to a GUI thread and retry the service dispatch. ;
cmp eax, [edi]+SdLimit ; check if valid service jae Kss_ErrorHandler ; if ae, try to convert to GUI thread
; ; If the service is a GUI service and the GDI user batch queue is not empty, ; then call the appropriate service to flush the user batch. ;
cmp ecx, SERVICE_TABLE_TEST ; test if GUI service jne short Kss40 ; if ne, not GUI service mov ecx, PCR[PcTeb] ; get current thread TEB address xor ebx, ebx ; get number of batched GDI calls
KiSystemServiceAccessTeb: or ebx, [ecx]+TbGdiBatchCount ; may cause an inpage exception
jz short Kss40 ; if z, no batched calls push edx ; save address of user arguments push eax ; save service number call [_KeGdiFlushUserBatch] ; flush GDI user batch pop eax ; restore service number pop edx ; restore address of user arguments
; ; The arguments are passed on the stack. Therefore they always need to get ; copied since additional space has been allocated on the stack for the ; machine state frame. Note that we don't check for the zero argument case - ; copy is always done regardless of the number of arguments because the ; zero argument case is very rare. ;
Kss40: inc dword ptr PCR[PcPrcbData+PbSystemCalls] ; system calls
if DBG
mov ecx, [edi]+SdCount ; get count table address jecxz short @f ; if zero, table not specified inc dword ptr [ecx+eax*4] ; increment service count @@: ;
endif
FPOFRAME ?FpoValue, 0
mov esi, edx ; (esi)->User arguments mov ebx, [edi]+SdNumber ; get argument table address xor ecx, ecx mov cl, byte ptr [ebx+eax] ; (ecx) = argument size mov edi, [edi]+SdBase ; get service table address mov ebx, [edi+eax*4] ; (ebx)-> service routine sub esp, ecx ; allocate space for arguments 360是在这儿Hook的 shr ecx, 2 ; (ecx) = number of argument DWORDs mov edi, esp ; (edi)->location to receive 1st arg cmp esi, _MmUserProbeAddress ; check if user address jae kss80 ; if ae, then not user address
KiSystemServiceCopyArguments: rep movsd ; copy the arguments to top of stack. ; Since we usually copy more than 3 ; arguments. rep movsd is faster than ; mov instructions.
; ; Check if low resource usage should be simulated. ;
if DBG
test _MmInjectUserInpageErrors, 2 jz short @f stdCall _MmTrimProcessMemory, <0> jmp short kssdoit @@:
mov eax,PCR[PcPrcbData+PbCurrentThread] mov eax,[eax]+ThApcState+AsProcess test dword ptr [eax]+PrFlags,0100000h ; is this a inpage-err process? je short @f stdCall _MmTrimProcessMemory, <0> @@:
endif
; ; Make actual call to system service ;
kssdoit: call ebx ; call system service 看到这儿就感觉很爽
kss60:
; ; Check for return to user mode at elevated IRQL. ;
if DBG
test byte ptr [ebp]+TsSegCs,MODE_MASK ; test if previous mode user jz short kss50b ; if z, previous mode not user mov esi,eax ; save return status CurrentIrql ; get current IRQL or al,al ; check if IRQL is passive level jnz kss100 ; if nz, IRQL not passive level mov eax,esi ; restore return status
; ; Check if kernel APCs are disabled or a process is attached. ; mov ecx,PCR[PcPrcbData+PbCurrentThread] ; get current thread address mov dl,[ecx]+ThApcStateIndex ; get APC state index or dl,dl ; check if process attached jne kss120 ; if ne, process is attached mov edx,[ecx]+ThCombinedApcDisable ; get kernel APC disable or edx,edx ; check if kernel APCs disabled jne kss120 ; if ne, kernel APCs disabled. kss50b: ;
endif
kss61:
; ; Upon return, (eax)= status code. This code may also be entered from a failed ; KiCallbackReturn call. ;
mov esp, ebp ; deallocate stack space for arguments
; ; Restore old trap frame address from the current trap frame. ;
kss70: mov ecx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address mov edx, [ebp].TsEdx ; restore previous trap frame address mov [ecx].ThTrapFrame, edx ; ; ; System service's private version of KiExceptionExit ; (Also used by KiDebugService) ; ; Check for pending APC interrupts, if found, dispatch to them ; (saving eax in frame first). ; public _KiServiceExit _KiServiceExit:
cli ; disable interrupts DISPATCH_USER_APC ebp, ReturnCurrentEax
; ; Exit from SystemService ;
EXIT_ALL NoRestoreSegs, NoRestoreVolatile
; ; The address of the argument list is not a user address. If the previous mode ; is user, then return an access violation as the status of the system service. ; Otherwise, copy the argument list and execute the system service. ;
kss80: test byte ptr [ebp].TsSegCs, MODE_MASK ; test previous mode jz KiSystemServiceCopyArguments ; if z, previous mode kernel mov eax, STATUS_ACCESS_VIOLATION ; set service status jmp kss60 ;
;++ ; ; _KiServiceExit2 - same as _KiServiceExit BUT the full trap_frame ; context is restored ; ;-- public _KiServiceExit2 _KiServiceExit2:
cli ; disable interrupts DISPATCH_USER_APC ebp
; ; Exit from SystemService ;
EXIT_ALL ; RestoreAll
if DBG
kss100: push PCR[PcIrql] ; put bogus value on stack for dbg
FpoValue = ?FpoValue + 1
FPOFRAME ?FpoValue, 0 mov byte ptr PCR[PcIrql],0 ; avoid recursive trap cli ;
; ; IRQL_GT_ZERO_AT_SYSTEM_SERVICE - attempted return to usermode at elevated ; IRQL. ; ; KeBugCheck2(IRQL_GT_ZERO_AT_SYSTEM_SERVICE, ; System Call Handler (address of system routine), ; Irql, ; 0, ; 0, ; TrapFrame); ;
stdCall _KeBugCheck2,<IRQL_GT_ZERO_AT_SYSTEM_SERVICE,ebx,eax,0,0,ebp>
; ; APC_INDEX_MISMATCH - attempted return to user mode with kernel APCs disabled ; or a process attached. ; ; KeBugCheck2(APC_INDEX_MISMATCH, ; System Call Handler (address of system routine), ; Thread->ApcStateIndex, ; Thread->CombinedApcDisable, ; 0, ; TrapFrame); ;
kss120: movzx eax,byte ptr [ecx]+ThApcStateIndex ; get APC state index mov edx,[ecx]+ThCombinedApcDisable ; get kernel APC disable stdCall _KeBugCheck2,<APC_INDEX_MISMATCH,ebx,eax,edx,0,ebp>
endif ret
_KiFastCallEntry endp
从上面的代码可以看出360 实际上是拦截了所有的SSDT的调用,包括Shadow SSDT。这种方式比瑞星的Hook更加彻底,技术上也更高一筹。现在我认为360的inline Hook我已经说清楚了!![吸血蝙蝠](http://image91.360doc.com/DownloadImg/2015/11/1209/61428050_1.png)
|