同事一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一些分析框架,元宵快乐~

TsFltMgr.sys其原因是,该系统蓝屏QQ计算机管理器!的更多相关文章

  1. Windbg解决系统蓝屏

    win10企业版连续两天遭遇系统蓝屏, 今天就各种检查,准备好好地研究一下这个问题,以下是整个过程: 首先,找到系统蓝屏时的错误日志: [计算机] --> [管理] --> [系统工具] ...

  2. 老毛桃U盘启动盘,通过ghost创建xp系统蓝屏问题

    新买的东芝笔记本只预安装了dos,找来一个老毛桃U盘启动盘,进入winpe用ghost恢复成xp系统:重启后,系统蓝屏,提示的主要报错代码 0x0000007B 与 要求“chkdsk /f”处理. ...

  3. TsFltMgr.sys系统蓝屏的原因就在于QQ电脑管家!

    同事一WindowsXP系统,正常执行,关闭后,第二天无法启动,详细症状为: (1)安全模式以及带网络功能的安全模式都能够进入: (2)正常模式,还没出现WindowXP滚动栏就開始重新启动: (3) ...

  4. Win8.1系统蓝屏提示Wpprecorder.sys文件故障解决方法

    从问题查找上来讲Win8.1蓝屏比win7出现田蓝屏更为麻烦,因为没有错误代码,大家也无从查找!所以只有在有限的提示中找到一些“蛛丝马迹”,而今天我们要讲的是“Win8.1重启或快速启动时蓝屏时提示W ...

  5. 怎么处理系统蓝屏后提示代码0x000000d1的错误?

    电脑开机有时会出现蓝屏,导致蓝屏的原因有很多,每种错误都有不同的代码.下面就来和大家分享一下电脑开机蓝屏出现0x000000d1错误代码是什么原因?我们又该怎么去解决这个问题. 电脑开机蓝屏出现0x0 ...

  6. 烂泥:kvm安装windows系统蓝屏

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. 最近一直在学习有关KVM的知识,实验一直是在虚拟机VM中进行的.今天刚好公司有一台空闲的服务器,直接拿来安装centos.kvm等等,然后相关的配置. ...

  7. Windows 10家庭版升级到专业版,系统蓝屏

    Log Name: SystemSource: Microsoft-Windows-DistributedCOMDate: 9/9/2018 7:56:57 PMEvent ID: 10016Task ...

  8. 系统蓝屏stop:ox000007B错误解决方案

    解决方法:开机进入bios:BIOS->Advanced->SATA Mode:[AHCI改为ATA或Compatibility],然后F10保存退出. ATA是指硬盘使用IDE兼容模式, ...

  9. Windows 系统蓝屏错误小全

    0 0x00000000 作业完成. 1 0x00000001 不正确的函数. 2 0x00000002 系统找不到指定的档案. 3 0x00000003 系统找不到指定的路径. 4 0x000000 ...

随机推荐

  1. mong 备份和恢复

    [root@hy-mrz01 bin]# ./mongofiles list -h114.55.5.57 -db pics 20160602152850deeabcb1bd2644afa0c3a9a8 ...

  2. 一步一步重写 CodeIgniter 框架 (10) —— 使用 CodeIgniter 类库(续)

    上一节简单实现了 CI 的类库扩展模型,所以 _ci_load_class 和 _ci_init_class 写的不是很完备.根据上节课的分析,当 system/libraries 目录下存在 Ema ...

  3. iOS 打印日志的保存 (一)

    当我们真机调试app的时候,作为开发人员的我们可以很方便的通过Xcode的debug area查看相关的打印信息.而测试人员在对app进行测试的时候,一旦出现了crash,这时我们就需要把相关的打印信 ...

  4. erlang 初体验

    近期測试了一下 erlang的坑... 如不出意外.... 大家第一眼看到这语法... 心里第一句一定是"我擦.这TM都是啥!!!!!" 没有变量!!! 没有结构体!!! 没有循环 ...

  5. 【Linux驱动器】Linux-2.6.20.4内核移植

    最近一段时间以来一直学习TQ2440内核开发板移植.嫁接驱动器. 真诚地相信这方面的知识有很大的困难,.但有一种观点认为,从看,难度越大,的提升空间的能力更大! ! 1.解压源代码 从Internet ...

  6. MinGW是什么

    MinGW是什么? MinGW是建立在gcc和binutils项目上的,用来编译和连接代码,使之运行在windows系统上: 提供c.c++和fortran编译器和相关工具: MinGW=Minima ...

  7. grunt 前端开发环境搭建

    1.找管理员开通gitlab权限 2.下载并安装git工具 3.下载并安装nodejs 4.安装cnpm 5.安装kulor-cli cnpm install -g kulor-cli cnpm in ...

  8. dfs-hdu-4620-Fruit Ninja Extreme

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4620 题目大意: 切水果.给n刀,每刀的时间,每刀切的水果的种类.求能切的最多的刀数,使得每相邻的两 ...

  9. spring利用扫描方式对bean的处理(对任何版本如何获取xml配置信息的处理)

    利用扫描的方式将组件注入容器,就也可以不用操作bean来实例化对象了. 下面我做一个例子 我用的spring3.2.2版本的 首先写一个spring.xml. <?xml version=&qu ...

  10. DNS:因特网的目录服务

    作者:华科小涛,http://www.cnblogs.com/hust-ghtao/ 有两种方式来识别主机:通过主机名或IP地址.人们当然喜欢便于记忆的主机名,而路由器则喜欢定长的.有层次结构的IP地 ...