TsFltMgr.sys系统蓝屏的原因是什么

1个回答

写回答

绵绵weishazi

2025-07-09 23:38

+ 关注

互联网
互联网

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

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

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

电脑
电脑

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

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

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

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

新版的QQ电脑管家中多了一个名字叫TsFltMgr.sys的驱动,对该驱动进行了一些简单的分析,看见了一套美丽的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_KiServiceTableulSyscallId = g_ServiceNum/* 0x11c */ )

{

pFakeSysCall = g_FakeSysCallTable[ulSyscallId]; // SSDT

}

else if (pulSyscallTable == g_KeServiceDescriptorTable

g_KeServiceDescriptorTableulSyscallId = g_ServiceNum/* 0x11c */)

{

pFakeSysCall = g_FakeSysCallTable[ulSyscallId]; // SSDT

}

else if (pulSyscallTable == g_W32pServiceTableAddrulSyscallId = g_ShadowServiceNum/* 0x29b */)

{

pFakeSysCall = g_FakeSysCallTable[ulSyscallId + 1024]; // ShadowSSDT

}

if ( pFakeSysCallpFakeSysCall-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 != 0xEEEE0004ulStatus != 0xEEEE0005)

{

OrigSysCall * pOrigSysCall = pFakeSysCall-ulOrigSysCallAddr;

// 调用原始系统调用!

if ( pOrigSysCallNT_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-xxx5ullTickCountg_bPerformanceTest) {

PerformanceTest(g_pFakeSysCall_NtTerminateProcess-xxx13, ullTickCount);

}

return status;

}

举报有用(4分享收藏

Copyright © 2025 IZhiDa.com All Rights Reserved.

知答 版权所有 粤ICP备2023042255号