虫趣:当NV显卡驱动碰上Verifier
引用注明>>?【作者:張佩】【原文:www.YiiYee.cn/blog】
今天開電腦的時候,剛完成用戶登陸,就遇到一個藍(lán)屏。桌面還沒有進(jìn)去呢。趁著系統(tǒng)正處于抓取dump文件的過程中,趕緊拍了一張照,留作紀(jì)念。造成藍(lán)屏的不是別人,乃是負(fù)責(zé)圖形渲染和顯示的顯卡驅(qū)動:Nvidia顯卡驅(qū)動。
我使用的系統(tǒng):Win Blue x64。
事出有因
顯卡驅(qū)動負(fù)責(zé)桌面系統(tǒng)的渲染和顯示,其重要性不是一點(diǎn)點(diǎn)。所以輕易是不可能藍(lán)屏的。我剛開始也有點(diǎn)納悶,想自己昨天究竟做了什么,使得自己一大早的就遇到個天雷滾滾從天降——開機(jī)藍(lán)屏。后來看了系統(tǒng)顯示的錯誤原因,才明白過來。從上圖中看到,這是在Verifier開啟的前提下,誘發(fā)出來的NV顯卡驅(qū)動的癲癇病。
Verifier為啥會開啟呢?我想起來了,這確實(shí)事出有因。可追溯到我昨天看的一篇介紹數(shù)字簽名的文章,它介紹了sigverif.exe這個工具,可以檢測系統(tǒng)中已安裝而未被數(shù)字簽名的驅(qū)動程序。在運(yùn)行了這個工具后,我很欣喜地發(fā)現(xiàn)系統(tǒng)中所有驅(qū)動程序都是簽過名的。
其實(shí)這個結(jié)果解釋了我的一個疑惑。因?yàn)槲矣浀米约呵岸螘r間在運(yùn)行Verifier的時候,它總是能找到兩個未簽名的驅(qū)動程序。其中一個是我安裝的VClone虛擬光驅(qū)軟件附帶的內(nèi)核驅(qū)動程序。從VClone的官方文檔來看,它是有數(shù)字簽名的,設(shè)備管理器中也有正確的顯示。為什么Verifier把它列為嫌犯,我對此一直都很疑惑。
現(xiàn)在有了sigverif作為對照,我又再次運(yùn)行了Verifier。選擇驗(yàn)證未簽名的驅(qū)動程序,果然還是有兩個被列了出來。如下圖所示。
就這個情況,我研究了半天,沒有一個結(jié)論。但過程中,我出現(xiàn)了一個小小的操作失誤。在選擇驅(qū)動程序進(jìn)行驗(yàn)證的時候,我選擇了一個不可逆的驗(yàn)證:自動選擇這臺計(jì)算機(jī)上安裝的所有驅(qū)動程序。
我選擇這一項(xiàng)的初衷,是要看看verifier檢索到的驅(qū)動列表,和Sigverif檢索的驅(qū)動列表的區(qū)別。不料這個過程竟然是不可逆的,即使我退出后,再次選擇“刪除現(xiàn)有設(shè)置”,也已經(jīng)沒有用。
但當(dāng)時,我卻沒這么覺得。我以為通過一些動作,已經(jīng)把Verifier設(shè)置都清空了。其實(shí)卻不然呢。這正是發(fā)生今天這個問題的初始緣由了。
自我救贖
重啟后,我又試了兩次,希冀可以登錄到桌面后快速地關(guān)掉Verifier。但事實(shí)卻很無情,我又多遇到了兩次迅捷無比的藍(lán)屏。所以我就進(jìn)入到安全模式。Windows在安全模式下不使用IHV的顯示驅(qū)動,而是加載微軟自己的display only顯示驅(qū)動。
這一次我是安全的。安全模式救了我。我運(yùn)行verifier,并在此選擇“刪除現(xiàn)有設(shè)置”項(xiàng)。在提示重啟出現(xiàn)的時候,我服從并重啟。重啟到正常的系統(tǒng),這次已無問題了。
調(diào)試分析
活過來后,我第一個啟動的是Windbg,并加載dump文件。錯誤類型DRIVER_VERIFIER_DETECTED_VIOLATION對應(yīng)的BSOD號是0xC4,自動分析結(jié)果如下:
2: kd> !analyze -v ******************************************************************** * * Bugcheck Analysis * * * *******************************************************************DRIVER_VERIFIER_DETECTED_VIOLATION (c4) A device driver attempting to corrupt the system has been caught. This is because the driver was specified in the registry as being suspect (by the administrator) and the kernel has enabled substantial checking of this driver. If the driver attempts to corrupt the system, bugchecks 0xC4, 0xC1 and 0xA will be among the most commonly seen crashes. Arguments: Arg1: 00000000000000f6, Referencing user handle as KernelMode. Arg2: 0000000000000100, Handle value being referenced. Arg3: ffffe00008f53900, Address of the current process. Arg4: fffff800028fc879, Address inside the driver that is performing the incorrect reference.Debugging Details: ------------------自動分析的結(jié)果非常重要。它的第一個參數(shù)指明了Verifier錯誤類型,0xf6表示驅(qū)動程序在引用一個用戶句柄的時候,把它的類型錯誤地指示為KernelMode。打開Windbg的幫助文檔,看到更詳細(xì)的參數(shù)解釋:
| Parameter 1 | Parameter 2 | Parameter 3 | Parameter 4 | Cause of Error | 
| 0xF6 (Windows 7 operating systems and later) | Handle value being referenced | Address of the current process | Address inside the driver that performs the incorrect reference | A driver references a user-mode handle as kernel mode. | 
從上面得到另一個很重要的信息:這個錯誤類型,只在Win7以后的系統(tǒng)上才存在。
它的第三個參數(shù)是被應(yīng)用的句柄,值為0x100。它很明顯是一個用戶層句柄,因?yàn)閃inidows系統(tǒng)上的內(nèi)核句柄,其高位是被置1的。比如32位系統(tǒng)上,內(nèi)核句柄應(yīng)該是0x80xxxxxx,64位系統(tǒng)上是0xffffffff’80xxxxxx。雖然沒有明確的文檔說明這一點(diǎn),但僅僅根據(jù)我們的觀察,可以從經(jīng)驗(yàn)上證明之。
所以自動分析是言之有物的,它是在說:在一個地址為ffffe00008f53900(參數(shù)3)的用戶進(jìn)程環(huán)境中, NV顯卡驅(qū)動在代碼執(zhí)行到地址fffff800028fc879(參數(shù)4)附近時,以kernelMode的方式使用了一個用戶句柄0x100。
2: kd> !handle 0x100PROCESS ffffe00008f53900SessionId: 1 Cid: 1538 Peb: 7ff725f06000 ParentCid: 0c20DirBase: 17bda2000 ObjectTable: ffffc00003321400 HandleCount: Image: rundll32.exeHandle Error reading handle count.0100: Object: ffffc000056b72a0 GrantedAccess: 00020019 (Protected) (Inherit) (Audit) Entry: ffffc000033b0400 Object: ffffc000056b72a0 Type: (ffffe00000119730) KeyObjectHeader: ffffc000056b7270 (new version)HandleCount: 1 PointerCount: 32768Directory Object: 00000000 Name: \REGISTRY\MACHINE\SYSTEM\CONTROLSET001\CONTROL\CLASS\{4D36E968-E325-11CE-BFC1-08002BE10318}\0000\NVSPCAPS2: kd> !process ffffe00008f53900 PROCESS ffffe00008f53900SessionId: 1 Cid: 1538 Peb: 7ff725f06000 ParentCid: 0c20DirBase: 17bda2000 ObjectTable: ffffc00003321400 HandleCount: Image: rundll32.exeVadRoot ffffe00008d27330 Vads 61 Clone 0 Private 515. Modified 15118. Locked 0.DeviceMap ffffc000039412a0Token ffffc00003359060ElapsedTime 00:00:01.055UserTime 00:00:00.000KernelTime 00:00:00.000QuotaPoolUsage[PagedPool] 150496QuotaPoolUsage[NonPagedPool] 7792Working Set Sizes (now,min,max) (1608, 50, 345) (6432KB, 200KB, 1380KB)PeakWorkingSetSize 1608VirtualSize 74 MbPeakVirtualSize 74 MbPageFaultCount 1630MemoryPriority BACKGROUNDBasePriority 8CommitCharge 620THREAD ffffe00008ec8080 Cid 1538.153c Teb: 00007ff725f0e000 Win32Thread: fffff901469e8b70 RUNNING on processor 2Not impersonatingDeviceMap ffffc000039412a0Owning Process ffffe00008f53900 Image: rundll32.exeAttached Process N/A Image: N/AWait Start TickCount 8751 Ticks: 50 (0:00:00:00.781)Context Switch Count 593 IdealProcessor: 2 UserTime 00:00:00.000KernelTime 00:00:00.140Win32 Start Address 0x00007ff725f33f0cStack Init ffffd000235d7c90 Current ffffd000235d6f90Base ffffd000235d8000 Limit ffffd000235d2000 Call 0Priority 8 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5Child-SP RetAddr Call Siteffffd000`235d6658 fffff800`d7eea6a8 nt!KeBugCheckExffffd000`235d6660 fffff800`d7eeff98 nt!VerifierBugCheckIfAppropriate+0x3cffffd000`235d66a0 fffff800`d7db7e73 nt!VfCheckUserHandle+0x1b8ffffd000`235d6780 fffff800`d7c285d5 nt! ?? ::NNGAKEGL::`string'+0x10503ffffd000`235d6820 fffff800`d7c402d6 nt!ObReferenceObjectByHandle+0x25ffffd000`235d6870 fffff800`d79d74b3 nt!NtQueryValueKey+0x136ffffd000`235d6b20 fffff800`d79cf900 nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ ffffd000`235d6b90)ffffd000`235d6d28 fffff800`028fc879 nt!KiServiceLinkageffffd000`235d6d30 fffff800`028fc0c2 nvlddmkm+0x9a879ffffd000`235d6de0 fffff800`02946cbe nvlddmkm+0x9a0c2ffffd000`235d6e80 fffff800`029151d4 nvlddmkm+0xe4cbeffffd000`235d6f00 fffff800`02907e5c nvlddmkm+0xb31d4ffffd000`235d6f50 fffff800`0317c1c3 nvlddmkm+0xa5e5cffffd000`235d7360 fffff800`0290730b nvlddmkm!nvDumpConfig+0x29fdebffffd000`235d73a0 fffff800`0315e9b9 nvlddmkm+0xa530bffffd000`235d74c0 fffff800`031fcc05 nvlddmkm!nvDumpConfig+0x2825e1ffffd000`235d7590 fffff800`02301e5c nvlddmkm!nvDumpConfig+0x32082dffffd000`235d75c0 fffff800`022c8e03 dxgkrnl!DXGADAPTER::DdiEscape+0x48ffffd000`235d75f0 fffff960`001813a3 dxgkrnl!DxgkEscape+0x573ffffd000`235d7ab0 fffff800`d79d74b3 win32k!NtGdiDdDDIEscape+0x53ffffd000`235d7b00 00007ff8`349d14aa nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ ffffd000`235d7b00)0000006b`439bf068 00000000`00000000 0x00007ff8`349d14aa這個進(jìn)程rundll32是一個執(zhí)行dll調(diào)用的通用的宿主進(jìn)程,所以它的父進(jìn)程比較能說明問題。我期望它的父進(jìn)程是NV相關(guān)的進(jìn)程,但最后發(fā)現(xiàn)CID為0xc20的進(jìn)程為桌面進(jìn)程。可能的情況是桌面進(jìn)程調(diào)用了D3D的相關(guān)功能,進(jìn)入NV顯卡驅(qū)動并爆發(fā)了問題。在進(jìn)入內(nèi)核驅(qū)動時,用戶程序傳入了一個句柄參數(shù),這個句柄指向一個和NV顯卡相關(guān)的注冊表鍵而,內(nèi)核不正確地使用了這個句柄并導(dǎo)致問題。這個相關(guān)的注冊表鍵值的路徑為:\REGISTRY\MACHINE\SYSTEM\CONTROLSET001\CONTROL\CLASS\{4D36E968-E325-11CE-BFC1-08002BE10318}\0000\NVSPCAPS
2: kd> !process 0 0 *省略其它進(jìn)程信息* PROCESS ffffe00008810900SessionId: 1 Cid: 0c20 Peb: 7ff7be8a6000 ParentCid: 0c10DirBase: 15a40e000 ObjectTable: ffffc000041a5440 HandleCount: Image: explorer.exe逆推代碼錯誤
根據(jù)上面的分析內(nèi)容,已能很輕松地指癥了。從它的調(diào)用棧上可以看出來,在進(jìn)入Verifier檢測函數(shù)前,系統(tǒng)調(diào)用的函數(shù)是ObReferenceObjectByHandle。我們看這個函數(shù)的聲明:
NTSTATUS ObReferenceObjectByHandle(_In_ HANDLE Handle,_In_ ACCESS_MASK DesiredAccess,_In_opt_ POBJECT_TYPE ObjectType,_In_ KPROCESSOR_MODE AccessMode,_Out_ PVOID *Object,_Out_opt_ POBJECT_HANDLE_INFORMATION HandleInformation );關(guān)于AccessMode,MSDN上的解釋是:
AccessMode?[in]
Specifies the access mode to use for the access check. It must be either?UserMode?or?KernelMode. Drivers should always specify?UserMode?for handles they receive from user address space.
所以,對于0x100的用戶句柄,如果在調(diào)用ObReferenceObjectByHandle的時候,指示的Accessmode為KernelMode,就會在Verifier檢驗(yàn)函數(shù)中產(chǎn)生一個類型為0xC4/0xF6的BSOD。這也是一個比較合乎情理的錯誤原因。
到這里問題到還沒有結(jié)束,因?yàn)镺bReferenceObjectByHandle是被間接調(diào)用的,NV驅(qū)動直接調(diào)用的函數(shù)是ZwQueryValueKey(它沒有AccessMode這個參數(shù))。為什么是ZwQueryValueKey呢?這涉及到Zwxxx和Ntxxx兩組系統(tǒng)API的區(qū)別。見下面這一段stack。
ffffd000`235d6870 fffff800`d79d74b3 nt!NtQueryValueKey+0x136 ffffd000`235d6b20 fffff800`d79cf900 nt!KiSystemServiceCopyEnd+0x13 ffffd000`235d6d28 fffff800`028fc879 nt!KiServiceLinkage ffffd000`235d6d30 fffff800`028fc0c2 nvlddmkm+0x9a879在內(nèi)核中調(diào)用Zwxxx函數(shù),它會經(jīng)過一系列復(fù)雜過程,最終調(diào)用到對應(yīng)的Ntxxx函數(shù)。重要的一點(diǎn)是,調(diào)用Zwxxx函數(shù)會把當(dāng)前線程的Previous Mode設(shè)置成Kernel Mode(參考文章:OSR)。
一個在內(nèi)核中執(zhí)行的線程,它既可能是從用戶程序下來的,也可能是一個一直在內(nèi)核中運(yùn)行的系統(tǒng)線程。為了區(qū)分這種情況,線程結(jié)構(gòu)體中保存了一個變量,保存線程此前的Mode(Previous Mode)。對于一個從用戶層調(diào)下來的線程,它的Previous Mode是User Mode。但如果它調(diào)用了哪怕一次Zwxxx函數(shù),其Previous Mode將被改成Kernel Mode,好像它再一次陷入了內(nèi)核(從內(nèi)核陷入內(nèi)核)。
在這個例子中,對ZwQueryValueKey的調(diào)用,很可能影響到接下來NtQueryValueKey中調(diào)用ObReferenceObjectByHandle時的輸入?yún)?shù)。所以,在驅(qū)動程序中調(diào)用Native API,使用Ntxxx函數(shù)比Zwxxx函數(shù)更穩(wěn)妥。
這些內(nèi)容比較隱晦,涉及很多未文檔內(nèi)容。我不確定。但我懷疑:如果NV驅(qū)動把調(diào)用ZwQueryValueKey的代碼改成直接調(diào)用NTQueryValueKey,可能就會解決問題。
其它
在Windbg分析完之后,我看了一下我當(dāng)前使用的NV驅(qū)動版本是331.82,日期為2013年11月,大約兩個月前更新的,也算是比較新。我立刻到NV的官方網(wǎng)站上查看和我顯卡匹配的最新驅(qū)動(GTX 670M),有2014年1月份的最新WHQL版本:332.21。我見此便立刻下載了。我還是有點(diǎn)小膽怯的,所以沒再去幫助NV驗(yàn)證最新的驅(qū)動是否已經(jīng)解決了這個問題。如果有NV的Driver工程師看到我這篇文章,可以試一試。我保留了dump文件,需要時也可以向我索取。
總結(jié)
以上是生活随笔為你收集整理的虫趣:当NV显卡驱动碰上Verifier的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 低轨通信卫星: 开启 6G 通信时代,带
- 下一篇: 打包发布firefox的主题
