TsFltMgr.sys系統(tǒng)藍屏的原因分析(小心QQ電腦管家)
同事一WindowsXP系統(tǒng),正常執(zhí)行,關閉后,第二天無法啟動,詳細癥狀為:
(1)安全模式以及帶網(wǎng)絡功能的安全模式都能夠進入;
(2)正常模式,還沒出現(xiàn)WindowXP滾動欄就開始重新啟動;
(3)進安全模式,禁用自己主動重新啟動后,再正常啟動,出現(xiàn)藍屏,報TsFltMgr.sys內(nèi)存錯誤!
經(jīng)過互聯(lián)網(wǎng)查詢,和不斷摸索,最后發(fā)現(xiàn)居然是可惡的QQ軟件管家惹的禍,進安全模式果斷卸載QQ軟件管家后,再重新啟動,系統(tǒng)全然正常了。
以下轉(zhuǎn)載了一篇分析QQ電腦管家的文章,請參考:
QQ電腦管家中的TsFltMgr Hook框架分析
新版的QQ電腦管家中多了一個名字叫TsFltMgr.sys的驅(qū)動(應該是Sysnap大牛開發(fā)的,膜拜),對該驅(qū)動進行了一些簡單的分析,看見了一套美麗的Hook框架,發(fā)出來與大家分享。分析不正確的地方請多多包涵。
首先TsFltMgr掛鉤了KiFastCallEntry函數(shù),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個字節(jié),在這里被 TsFltMgr 替換成了3個nop和一個jmp。
該jmp會跳轉(zhuǎn)到 KiFastCallEntry_Detour 函數(shù)中,KiFastCallEntry_Detour 函數(shù)代碼例如以下:
代碼:
// 保存現(xiàn)場
pushfd
pushad </p> <p>// 調(diào)用 KiFastCallEntry_Filter 函數(shù),實現(xiàn)過濾
push edi // 本次系統(tǒng)調(diào)用相應的SysCall Table的地址(SSDT或SSDTShadow的地址)
push ebx // 本次系統(tǒng)調(diào)用在SysCall Table中相應的內(nèi)核函數(shù)地址
push eax // 本次系統(tǒng)調(diào)用相應的內(nèi)核函數(shù)在SysCall Table中的功能號
call KiFastCallEntry_Filter // 調(diào)用KiFastCallEntry_Filter,實現(xiàn)過濾
mov [esp+10h], eax // 更改本次調(diào)用相應的內(nèi)核函數(shù)地址!</p> <p>// 恢復現(xiàn)場
popad
popfd</p> <p>// 運行 KiFastCallEntry 函數(shù)中被替換掉的指令,并跳回原函數(shù)
mov edi,esp
cmp esi, g_7fff0000
push g_JmpBack
ret
這里須要注意的是 call KiFastCallEntry_Filter 之后的 mov [esp+10h], eax。之前保存現(xiàn)場時的指令pushad會導致寄存器EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI依次入棧,并通過后面的popad指令恢復這些寄存器的值。因此此處的mov [esp+10h], eax實際上是用 KiFastCallEntry_Filter 函數(shù)的返回值來改寫堆棧中保存的ebx的值,即改寫本次系統(tǒng)調(diào)用相應的內(nèi)核函數(shù)地址。
KiFastCallEntry_Filter 是真正實現(xiàn)過濾的函數(shù),該函數(shù)的參數(shù)和返回值上文已經(jīng)說明了,其詳細實現(xiàn)分析整理后,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內(nèi)部有一張表,暫且命名為 g_FakeSysCallTable,該表中存放的是指向 FAKE_SYSCALL 結構的指針。表中的每個 FAKE_SYSCALL 結構相應一個系統(tǒng)調(diào)用,表的前半部分相應SSDT中的系統(tǒng)調(diào)用,1024項以后相應ShadowSSDT里的系統(tǒng)調(diào)用。
當中 FAKE_SYSCALL 結構大致例如以下(當中非常多域的作用沒弄明確):
代碼:
typedef struct __FAKE_SYSCALL__ {
ULONG xxx1;
ULONG ulSyscallId; // 該系統(tǒng)調(diào)用的功能號
ULONG xxx3;
ULONG ulTableIndex;
ULONG xxx5;
ULONG ulCountForPreWork;
ULONG ulCountForPostWork;
ULONG xxx8;
ULONG ulOrigSysCallAddr; // 真實的系統(tǒng)調(diào)用地址
ULONG ulFakeSysCallAddr; // 假的系統(tǒng)調(diào)用地址
ULONG xxx11;
ULONG xxx12;
ULONG xxx13;
……
} FAKE_SYSCALL, *PFAKE_SYSCALL, **PPFAKE_SYSCALL;
因此 KiFastCallEntry_Filter 函數(shù)的所做的就是依據(jù)系統(tǒng)調(diào)用的功能號在 g_FakeSysCallTable 中索引出相應的 pFakeSysCall 對象,然后推斷該系統(tǒng)調(diào)用是否須要hook,假設須要則將真實的系統(tǒng)調(diào)用地址保存到 pFakeSysCall->ulOrigSysCallAddr 中,并將 pFakeSysCall->ulFakeSysCallAddr 作為假系統(tǒng)調(diào)用的地址返回。
這樣的調(diào)用過程中動態(tài)獲取真實系統(tǒng)調(diào)用地址的方法使 TsFltMgr 的Hook框架有較高的兼容性,比如不會使載入順序晚于TsFltMgr的驅(qū)動中的SSDT Hook失效,比如QQ電腦管家本身帶的TSKsp.sys驅(qū)動。
對于我的測試系統(tǒng)(XP_SP2),TsFltMgr hook的函數(shù)有:
代碼:
// 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
全部假系統(tǒng)函數(shù)都有統(tǒng)一的代碼框架,假系統(tǒng)函數(shù)的代碼框架大致例如以下:
代碼:
NTSTATUS __stdcall FakeNt_XXX(xxx)
{
PFAKE_SYSCALL pFakeSysCall;
ULONG ulXXX = 0;
ULONG ulStatus;
NTSTATUS status;
ULONGLONG ullTickCount;
pFakeSysCall = g_pFakeSysCall_Nt_XXX; // 該系統(tǒng)調(diào)用相應的 pFakeSysCall 對象
status = STATUS_ACCESS_DENIED;
</p> <p> // 貌似是做性能測試時候須要的,實際版本號中 g_bPerformanceTest 為 FALSE
if ( g_bPerformanceTest ) {
ullTickCount = KeQueryInterruptTime();
}</p> <p>
// 系統(tǒng)調(diào)用的調(diào)用前處理!
// +++
InterlockedIncrement(&pFakeSysCall->ulCountForPreWork);
ulStatus = PreWork(&ulXXX, pFakeSysCall);
InterlockedDecrement(&pFakeSysCall->ulCountForPreWork);
// ---
if ( ulStatus != 0xEEEE0004 && ulStatus != 0xEEEE0005)
{
OrigSysCall * pOrigSysCall = pFakeSysCall->ulOrigSysCallAddr;</p> <p> // 調(diào)用原始系統(tǒng)調(diào)用!
if ( pOrigSysCall && NT_SUCCESS(pOrigSysCall(xxx)) )
{
// 系統(tǒng)調(diào)用的調(diào)用后處理!
// +++
InterlockedIncrement(&pFakeSysCall->ulCountForPostWork),
ulStatus = PostWork(&ulXXX),
InterlockedDecrement(&pFakeSysCall->ulCountForPostWork),
// ---
}
}</p> <p> // 0xEEEE0004 應該是拒絕調(diào)用的意思,0xEEEE0005 應該是同意調(diào)用的意思
if (ulStatus == 0xEEEE0005)
status = STATUS_SUCCESS;</p> <p> // PsGetCurrentProcessId 這個調(diào)用的返回值后面并沒實用到,可能是多余的
PsGetCurrentProcessId();</p> <p> // 貌似是做性能測試時候須要的
if ( g_pFakeSysCall_NtTerminateProcess->xxx5 && ullTickCount && g_bPerformanceTest) {
PerformanceTest(&g_pFakeSysCall_NtTerminateProcess->xxx13, ullTickCount);
}</p> <p> return status;
}
以上就是對TsFltMgr Hook框架的一些分析,祝大家元宵快樂~
相關文章

電腦無聲音提示W(wǎng)indows音頻錯誤0xc00d11d1(0xc00d4e86)的解決辦法
電腦提示:“無法播放音頻,請確保您的計算機的聲卡和顯卡正在工作”,錯誤代碼0xc00d11d1(0xc00d4e86),下面我們就來看看解決辦法2026-02-27
電腦提示硬盤SMART命令失敗怎么辦? SMART錯誤解決辦法
電腦開機黑屏且出現(xiàn)硬盤SMART錯誤是較為嚴重的硬件故障,需要立即采取措施避免數(shù)據(jù)丟失,下面我們就來看看這個問題的解決辦法2026-01-12
電腦鍵盤失靈設備管理器顯示hid keyboard device黃色感嘆號的解決辦法
電腦鍵盤不能用了,查找原因時發(fā)現(xiàn)hid keyboard device選項有黃色感嘆號,這通常意味著系統(tǒng)未能正確識別設備或驅(qū)動異常,下面我們就來看看解決辦法2025-11-04
電腦開機進不去系統(tǒng)提示0xc0000428怎么辦? 0xc0000428錯誤代碼解決辦法
有的小伙伴們在使用電腦的過程中就可能會出現(xiàn)系統(tǒng)藍屏并且提示錯誤代碼0xc0000428的情況,下面我們就來看看詳細解決辦法2025-11-03
電腦提示磁盤未知未初始化怎么處理? 磁盤未知沒有初始化的排查方法
應用打不開,文件找不到,快捷方式不存在,打開資源管理器一看,整個分區(qū)都沒了,接著再打開磁盤管理,顯示“磁盤未知沒有初始化”,這要咋整?下面我們就來看看解決辦法2025-08-06
電腦提示不是有效的win32應用程序怎么辦? 原因分析與解決方法大合集
相信不少朋友都有過這樣的經(jīng)歷,就是當雙擊安裝某個軟件時,經(jīng)常會彈出“不是有效Win32應用程序”的錯誤提示,這是什么意思,又該如何解決呢?一起來看看2025-07-25
電腦提示無法初始化這個硬件的設備驅(qū)動程序代碼37怎么解決?
錯誤代碼 37 表示 windows 檢測到設備驅(qū)動程序存在問題,無法啟動硬件設備,這意味著您的系統(tǒng)上未正確安裝軟件設備驅(qū)動程序,或者您嘗試使用的硬件與 windows 不兼容,下面2025-07-25- 當遇到USB接口驅(qū)動異常時,可以通過多種方法來解決,其中主要就包括重裝USB控制器、禁用USB選擇性暫停設置、更新或安裝新的主板驅(qū)動等2025-04-23
在日常使用U盤的過程中,許多用戶可能會遇到U盤突然無法寫入數(shù)據(jù)的問題,這通常是由于U盤被寫保護所致,寫保護功能雖然可以防止數(shù)據(jù)被意外修改或刪除,但在需要寫入新數(shù)據(jù)時2024-12-14
最近配置了服務器,并做了磁盤陣列,突然有一天硬盤找不到了,然后重新陣列找回,但在磁盤管理器中發(fā)現(xiàn)雖然可以看到分區(qū)了,但提醒硬盤是只讀模式無法寫入文件,參考如下方2024-12-14










