zoukankan      html  css  js  c++  java
  • TsFltMgr.sys系统蓝屏的原因就在于QQ电脑管家!

    同事一WindowsXP系统,正常执行,关闭后,第二天无法启动,详细症状为:

    (1)安全模式以及带网络功能的安全模式都能够进入;

    (2)正常模式,还没出现WindowXP滚动栏就開始重新启动;

    (3)进安全模式,禁用自己主动重新启动后,再正常启动,出现蓝屏,报TsFltMgr.sys内存错误!

    经过互联网查询,和不断摸索,最后发现居然是可恶的QQ软件管家惹的祸,进安全模式果断卸载QQ软件管家后,再重新启动,系统全然正常了。


    以下转载了一篇分析QQ电脑管家的文章,请參考:

    QQ电脑管家中的TsFltMgr Hook框架分析


    新版的QQ电脑管家中多了一个名字叫TsFltMgr.sys的驱动(应该是Sysnap大牛开发的,膜拜),对该驱动进行了一些简单的分析,看见了一套美丽的Hook框架,发出来与大家分享。分析不正确的地方请多多包涵。

    首先TsFltMgr挂钩了KiFastCallEntry函数,Hook点在这里:

    代码:
    kd> u KiFastCallEntry+e3
    nt!KiFastCallEntry+0xe3:
    8053dbb3 c1e902        shr     ecx,2
    -------------------------------------------------------------------------
    8053dbb6 90            nop
    8053dbb7 90            nop
    8053dbb8 90            nop
    8053dbb9 e962170c77    jmp     TsFltMgr+0x2320 (f75ff320)
    -------------------------------------------------------------------------
    8053dbbe 0f83a8010000  jae     nt!KiSystemCallExit2+0x9f (8053dd6c)
    8053dbc4 f3a5          rep movs dword ptr es:[edi],dword ptr [esi]
    8053dbc6 ffd3          call    ebx
    
    原始的KiFastCallEntry在 shr ecx, 2 指令后面应该是 mov edi,esp;cmp esi, MmUserProbeAddress,共8个字节,在这里被 TsFltMgr 替换成了3个nop和一个jmp。

    该jmp会跳转到 KiFastCallEntry_Detour 函数中,KiFastCallEntry_Detour 函数代码例如以下:

    代码:
    // 保存现场
    pushfd        
    pushad        
    
    // 调用 KiFastCallEntry_Filter 函数,实现过滤
    push edi                    // 本次系统调用相应的SysCall Table的地址(SSDT或SSDTShadow的地址)
    push ebx                    // 本次系统调用在SysCall Table中相应的内核函数地址
    push eax                    // 本次系统调用相应的内核函数在SysCall Table中的功能号
    call KiFastCallEntry_Filter // 调用KiFastCallEntry_Filter,实现过滤
    mov  [esp+10h], eax         // 更改本次调用相应的内核函数地址!
    
    // 恢复现场
    popad        
    popfd
    
    // 运行 KiFastCallEntry 函数中被替换掉的指令,并跳回原函数
    mov     edi,esp
    cmp     esi, g_7fff0000
    push    g_JmpBack
    ret
    
    这里须要注意的是 call KiFastCallEntry_Filter 之后的 mov [esp+10h], eax。之前保存现场时的指令pushad会导致寄存器EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI依次入栈,并通过后面的popad指令恢复这些寄存器的值。因此此处的mov [esp+10h], eax实际上是用 KiFastCallEntry_Filter 函数的返回值来改写堆栈中保存的ebx的值,即改写本次系统调用相应的内核函数地址。

    KiFastCallEntry_Filter 是真正实现过滤的函数,该函数的參数和返回值上文已经说明了,其详细实现分析整理后,C语言描写叙述例如以下:

    代码:
    ULONG __stdcall KiFastCallEntry_Filter(ULONG ulSyscallId, ULONG ulSyscallAddr, PULONG pulSyscallTable) 
    {
        PFAKE_SYSCALL pFakeSysCall = NULL;
    
        if ( ulSyscallId >= 0x400 ) 
            return ulSyscallAddr;
    
        if ( pulSyscallTable == g_KiServiceTable && ulSyscallId <= g_ServiceNum/* 0x11c */ ) 
        {
            pFakeSysCall = g_FakeSysCallTable[ulSyscallId];        // SSDT
        }
        else if (pulSyscallTable == g_KeServiceDescriptorTable && 
                 g_KeServiceDescriptorTable && ulSyscallId <= g_ServiceNum/* 0x11c */)
        {
            pFakeSysCall = g_FakeSysCallTable[ulSyscallId];        // SSDT
        }
        else if (pulSyscallTable == g_W32pServiceTableAddr && ulSyscallId <= g_ShadowServiceNum/* 0x29b */)
        {
            pFakeSysCall = g_FakeSysCallTable[ulSyscallId + 1024]; // ShadowSSDT
        }
    
        if ( pFakeSysCall && pFakeSysCall->ulFakeSysCallAddr )
        {
            pFakeSysCall->ulOrigSysCallAddr = ulSyscallAddr;
            return pFakeSysCall->ulFakeSysCallAddr;
        }
        return ulSyscallAddr;
    }
    
    这里须要说明的是,TsFltMgr内部有一张表,暂且命名为 g_FakeSysCallTable,该表中存放的是指向 FAKE_SYSCALL 结构的指针。表中的每个 FAKE_SYSCALL 结构相应一个系统调用,表的前半部分相应SSDT中的系统调用,1024项以后相应ShadowSSDT里的系统调用。

    当中 FAKE_SYSCALL 结构大致例如以下(当中非常多域的作用没弄明确):

    代码:
    typedef struct __FAKE_SYSCALL__ {
        ULONG xxx1;
        ULONG ulSyscallId;        // 该系统调用的功能号
        ULONG xxx3;
        ULONG ulTableIndex;    
        ULONG xxx5;
        ULONG ulCountForPreWork;
        ULONG ulCountForPostWork;
        ULONG xxx8;
        ULONG ulOrigSysCallAddr;    // 真实的系统调用地址
        ULONG ulFakeSysCallAddr;    // 假的系统调用地址
        ULONG xxx11;
        ULONG xxx12;
        ULONG xxx13;
        ……
    } FAKE_SYSCALL, *PFAKE_SYSCALL, **PPFAKE_SYSCALL;
    
    因此 KiFastCallEntry_Filter 函数的所做的就是依据系统调用的功能号在 g_FakeSysCallTable 中索引出相应的 pFakeSysCall 对象,然后推断该系统调用是否须要hook,假设须要则将真实的系统调用地址保存到 pFakeSysCall->ulOrigSysCallAddr 中,并将 pFakeSysCall->ulFakeSysCallAddr 作为假系统调用的地址返回。

    这样的调用过程中动态获取真实系统调用地址的方法使 TsFltMgr 的Hook框架有较高的兼容性,比如不会使载入顺序晚于TsFltMgr的驱动中的SSDT Hook失效,比如QQ电脑管家本身带的TSKsp.sys驱动。

    对于我的測试系统(XP_SP2),TsFltMgr hook的函数有:

    代码:
    // SSDT中:
    NtCreateFile、NtCreateKey、NtCreateSection、NtCreateSymbolicLinkObject、NtCreateThread、NtDeleteFile、NtDeleteKey、NtDeleteValueKey、NtDeviceIoControlFile、NtDuplicateObject、NtEnumerateValueKey、NtLoadDriver、NtOpenProcess、NtOpenSection、NtProtectVirtualMemory、NtQueryValueKey、NtRequestWaitReplyPort、NtSetContextThread、NtSetInformationFile、NtSetSystemInformation、NtSetValueKey、NtSuspendThread、NtSystemDebugControl、NtTerminateProcess、NtTerminateThread、NtWriteFile、NtWriteVirtualMemory
    
    // ShadowSSDT中:
    NtUserBuildHwndList、NtUserFindWindowEx、NtUserGetForegroundWindow、NtUserMoveWindow、NtUserQueryWindow、NtUserSendInput、NtUserSetParent、NtUserSetWindowLong、NtUserSetWindowPlacement、NtUserSetWindowPos、NtUserShowWindow、NtUserShowWindowAsync、NtUserWindowFromPoint
    
    全部假系统函数都有统一的代码框架,假系统函数的代码框架大致例如以下:

    代码:
    NTSTATUS __stdcall FakeNt_XXX(xxx)
    {
        PFAKE_SYSCALL pFakeSysCall;
        ULONG ulXXX = 0;
        ULONG ulStatus;
        NTSTATUS status;
        ULONGLONG ullTickCount;
        
        pFakeSysCall = g_pFakeSysCall_Nt_XXX;  // 该系统调用相应的 pFakeSysCall 对象
        
        status = STATUS_ACCESS_DENIED;
      
    
        // 貌似是做性能測试时候须要的,实际版本号中 g_bPerformanceTest 为 FALSE
        if ( g_bPerformanceTest ) {
            ullTickCount = KeQueryInterruptTime();
        }
    
    
        // 系统调用的调用前处理!
        // +++
        InterlockedIncrement(&pFakeSysCall->ulCountForPreWork);
        ulStatus = PreWork(&ulXXX, pFakeSysCall);
        InterlockedDecrement(&pFakeSysCall->ulCountForPreWork);
        // ---
        
        if ( ulStatus != 0xEEEE0004 && ulStatus != 0xEEEE0005) 
        {    
            OrigSysCall * pOrigSysCall = pFakeSysCall->ulOrigSysCallAddr;
    
            // 调用原始系统调用!
            if ( pOrigSysCall && NT_SUCCESS(pOrigSysCall(xxx)) ) 
            {
                // 系统调用的调用后处理!
                // +++
                InterlockedIncrement(&pFakeSysCall->ulCountForPostWork),
                ulStatus = PostWork(&ulXXX),
                InterlockedDecrement(&pFakeSysCall->ulCountForPostWork),
                // ---
            }
        }
    
        // 0xEEEE0004 应该是拒绝调用的意思,0xEEEE0005 应该是同意调用的意思
        if (ulStatus == 0xEEEE0005)
            status = STATUS_SUCCESS;
    
        // PsGetCurrentProcessId 这个调用的返回值后面并没实用到,可能是多余的
        PsGetCurrentProcessId();
    
        // 貌似是做性能測试时候须要的
        if ( g_pFakeSysCall_NtTerminateProcess->xxx5 && ullTickCount && g_bPerformanceTest) {
            PerformanceTest(&g_pFakeSysCall_NtTerminateProcess->xxx13, ullTickCount);
        }
    
        return status;
    }
    
    以上就是对TsFltMgr Hook框架的一些分析,祝大家元宵快乐~ 
  • 相关阅读:
    反射获取class的Class
    线程礼让yield和线程的强制执行join
    Lambda表达式
    java中的静态代理
    java多线程中的callable接口实现
    java利用线程并行的方法模拟龟兔赛跑
    java中的多线程
    HTTP 协议之请求格式
    java的反射概述
    Tomcat和servlet的关系
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4069774.html
Copyright © 2011-2022 走看看