zoukankan      html  css  js  c++  java
  • 再谈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我已经说清楚了!吸血蝙蝠

  • 相关阅读:
    Solr简介
    儿童节快乐
    添加新的内容分类
    weka
    Junit测试样例
    Linux MySQL单实例源码编译安装5.5.32
    perconatoolkit 工具集安装
    Linux MySQL单实例源码编译安装5.6
    MySQL 开机自启动
    mysql5.6之前需要账号的安全加固
  • 原文地址:https://www.cnblogs.com/russinovich/p/2023316.html
Copyright © 2011-2022 走看看