欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

TsFltMgr.sys系统蓝屏的原因分析(小心QQ电脑管家)

程序员文章站 2022-06-18 15:02:59
TsFltMgr.sys系统蓝屏的原因分析(小心QQ电脑管家)经过互联网查询,和不断摸索,最后发现居然是可恶的QQ软件管家惹的祸,进安全模式果断卸载QQ软件管家后,再重新启动,系统全然正常了... 15-03-13...

同事一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 </p> <p>// 调用 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 // 更改本次调用相应的内核函数地址!</p> <p>// 恢复现场
popad
popfd</p> <p>// 运行 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;</p> <p> if ( ulsyscallid >= 0x400 )
return ulsyscalladdr;</p> <p> 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
}</p> <p> 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</p> <p>// 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;
</p> <p> // 貌似是做性能測试时候须要的,实际版本号中 g_bperformancetest 为 false
if ( g_bperformancetest ) {
ulltickcount = kequeryinterrupttime();
}</p> <p>
// 系统调用的调用前处理!
// +++
interlockedincrement(&pfakesyscall->ulcountforprework);
ulstatus = prework(&ulxxx, pfakesyscall);
interlockeddecrement(&pfakesyscall->ulcountforprework);
// ---

if ( ulstatus != 0xeeee0004 && ulstatus != 0xeeee0005)
{
origsyscall * porigsyscall = pfakesyscall->ulorigsyscalladdr;</p> <p> // 调用原始系统调用!
if ( porigsyscall && nt_success(porigsyscall(xxx)) )
{
// 系统调用的调用后处理!
// +++
interlockedincrement(&pfakesyscall->ulcountforpostwork),
ulstatus = postwork(&ulxxx),
interlockeddecrement(&pfakesyscall->ulcountforpostwork),
// ---
}
}</p> <p> // 0xeeee0004 应该是拒绝调用的意思,0xeeee0005 应该是同意调用的意思
if (ulstatus == 0xeeee0005)
status = status_success;</p> <p> // psgetcurrentprocessid 这个调用的返回值后面并没实用到,可能是多余的
psgetcurrentprocessid();</p> <p> // 貌似是做性能測试时候须要的
if ( g_pfakesyscall_ntterminateprocess->xxx5 && ulltickcount && g_bperformancetest) {
performancetest(&g_pfakesyscall_ntterminateprocess->xxx13, ulltickcount);
}</p> <p> return status;
}

以上就是对tsfltmgr hook框架的一些分析,祝大家元宵快乐~