再谈360内核inline Hook

  前几天写了一篇 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我已经说清楚了!吸血蝙蝠

posted @ 2011-04-21 11:08  Russinovich`s Blog  阅读(5760)  评论(11编辑  收藏  举报