屏分析實(shí)戰(zhàn):從DMP文件到錯(cuò)誤碼解讀)
有一次幫朋友排查筆記本藍(lán)屏事件查看器里只有一個(gè)Kernel-Power 41設(shè)備管理器看著一切正常內(nèi)存跑了幾輪也沒(méi)報(bào)錯(cuò)。后來(lái)我用WinDbg打開(kāi)崩潰時(shí)生成的DMP文件兩分鐘就定位到了問(wèn)題某型號(hào)無(wú)線網(wǎng)卡的舊驅(qū)動(dòng)在DMA寫(xiě)入時(shí)訪問(wèn)了無(wú)效地址更新固件之后再?zèng)]出現(xiàn)過(guò)。藍(lán)屏分析這件事繞不開(kāi)WinDbg——它是微軟官方調(diào)試器能把系統(tǒng)崩潰那一刻的現(xiàn)場(chǎng)完整還原出來(lái)。這篇是這個(gè)系列的第一篇不談玄學(xué)只講落地的實(shí)用技巧從環(huán)境準(zhǔn)備到第一份DMP的完整分析流程再到高頻錯(cuò)誤碼的解讀套路和常見(jiàn)誤判。如果你是第一次接觸藍(lán)屏分析或者之前只會(huì)用事件查看器和BlueScreenView這類(lèi)工具這篇正好適合你。后面我會(huì)盡量用“拿到問(wèn)題→怎么查→怎么確認(rèn)”的順序來(lái)寫(xiě)把我實(shí)際分析過(guò)程中的經(jīng)驗(yàn)和踩過(guò)的坑都攤開(kāi)講。1. 為什么事件查看器不夠用藍(lán)屏分析為什么要死磕WinDbg1.1 藍(lán)屏其實(shí)是一次“內(nèi)核級(jí)的停機(jī)報(bào)告”Windows藍(lán)屏的正式名字叫BugCheck本質(zhì)上是內(nèi)核檢測(cè)到系統(tǒng)已經(jīng)無(wú)法安全繼續(xù)運(yùn)行調(diào)用了KeBugCheckEx主動(dòng)停機(jī)。停機(jī)之前系統(tǒng)會(huì)把當(dāng)前內(nèi)存中的關(guān)鍵信息寫(xiě)入轉(zhuǎn)儲(chǔ)文件也就是我們常說(shuō)的DMP文件。這里要糾正一個(gè)很常見(jiàn)的誤解事件查看器里那條Kernel-Power 41并不是藍(lán)屏的原因它只是“上一次關(guān)機(jī)不正?!钡慕Y(jié)果。真正有價(jià)值的信息在BugCheck事件里也就是Event ID 1001它會(huì)記錄藍(lán)屏的代碼和四個(gè)參數(shù)比如0x000000D1這一類(lèi)。但光看這串?dāng)?shù)字你很難知道問(wèn)題出在哪個(gè)驅(qū)動(dòng)、哪個(gè)操作上。這就像一輛車(chē)突然熄火儀表盤(pán)只告訴你“發(fā)動(dòng)機(jī)故障”但你是想查火花塞、油路還是傳感器必須把行車(chē)電腦的數(shù)據(jù)導(dǎo)出來(lái)逐條看。DMP文件就是那臺(tái)行車(chē)電腦的數(shù)據(jù)WinDbg就是讀取這個(gè)數(shù)據(jù)的設(shè)備。1.2 事件查看器和第三方工具的局限性市面上確實(shí)有一堆號(hào)稱(chēng)一鍵分析藍(lán)屏的工具比如BlueScreenView、WhoCrashed我也用過(guò)它們能幫你快速看到錯(cuò)誤代碼、崩潰模塊、堆棧摘要應(yīng)急夠用。但它們的短板也很明顯只能展示W(wǎng)inDbg分析結(jié)果中很小的一部分尤其在多驅(qū)動(dòng)協(xié)同崩潰、內(nèi)存池?fù)p壞這類(lèi)復(fù)雜場(chǎng)景下幾乎幫不上忙。對(duì)比一下就知道工具能做什么局限事件查看器顯示BugCheck代碼和四個(gè)參數(shù)不解釋參數(shù)含義看不到調(diào)用棧BlueScreenView讀取DMP的摘要信息顯示崩潰模塊沒(méi)有符號(hào)解析能力無(wú)法交叉驗(yàn)證WhoCrashed自動(dòng)給出可能原因誤判率高經(jīng)常把系統(tǒng)驅(qū)動(dòng)當(dāng)元兇WinDbg符號(hào)化調(diào)用棧、完整模塊列表、參數(shù)解釋、內(nèi)存池分析有學(xué)習(xí)成本但是可追溯的完整證據(jù)鏈我用WinDbg分析DMP核心是它能給我一條完整的證據(jù)鏈不僅是“哪個(gè)模塊崩潰了”還包括它在什么IRQL下、訪問(wèn)了什么地址、調(diào)用了什么函數(shù)、當(dāng)前進(jìn)程是什么、驅(qū)動(dòng)文件的版本和時(shí)間戳是多少。把這些拼起來(lái)才能判斷是驅(qū)動(dòng)bug、硬件故障還是內(nèi)存被踩踏的連鎖反應(yīng)。2. 第一次動(dòng)手前選對(duì)工具、配好符號(hào)、確認(rèn)轉(zhuǎn)儲(chǔ)文件2.1 WinDbg版本怎么選經(jīng)典版、Preview還是最新版現(xiàn)在WinDbg實(shí)際上有三個(gè)常見(jiàn)形態(tài)很多人一搜“windbg下載”就懵了。簡(jiǎn)單說(shuō)經(jīng)典WinDbg包含在Windows SDK里界面經(jīng)典命令兼容性最好在老舊系統(tǒng)調(diào)試場(chǎng)景下仍然能打。WinDbg Preview微軟商店里上架的新版現(xiàn)在直接叫WinDbg界面現(xiàn)代化了一點(diǎn)支持暗色主題底部的Command窗口仍是主戰(zhàn)場(chǎng)。官方最新WinDbg繼續(xù)以商店方式更新分析性能更好支持更多擴(kuò)展命令。我的建議是直接用新版WinDbg從微軟商店裝一個(gè)就行。命令基本兼容偶爾有擴(kuò)展命令差異網(wǎng)上資料大多數(shù)都能對(duì)上。經(jīng)典版可以留著萬(wàn)一遇到老系統(tǒng)的轉(zhuǎn)儲(chǔ)兩邊對(duì)照著用。有人會(huì)糾結(jié)漢化包的問(wèn)題。我個(gè)人建議是不要用漢化——藍(lán)屏分析真正高頻操作的還是那幾十條命令菜單就那么幾個(gè)漢化了反而對(duì)不上社區(qū)資料里的術(shù)語(yǔ)。2.2 符號(hào)路徑?jīng)]有符號(hào)的WinDbg等于廢了一半符號(hào)文件.pdb是WinDbg的靈魂。DMP文件里記錄的是一堆虛擬地址沒(méi)有符號(hào)的話WinDbg只能告訴你“某個(gè)模塊調(diào)用了另一個(gè)模塊”卻無(wú)法顯示函數(shù)名調(diào)用棧全是問(wèn)號(hào)。這相當(dāng)于你拿到一份地址列表但沒(méi)有門(mén)牌簿根本不知道每扇門(mén)背后是誰(shuí)。正確做法是配置微軟公共符號(hào)服務(wù)器讓W(xué)inDbg按需自動(dòng)下載。標(biāo)準(zhǔn)符號(hào)路徑是SRV*C:\Symbols*https://msdl.microsoft.com/download/symbols在WinDbg里按CtrlS打開(kāi)符號(hào)路徑設(shè)置或者在命令窗口直接輸入.sympath SRV*C:\Symbols*https://msdl.microsoft.com/download/symbols也可以設(shè)置環(huán)境變量_NT_SYMBOL_PATH這樣對(duì)后續(xù)所有調(diào)試會(huì)話都生效。首次分析時(shí)符號(hào)下載會(huì)慢一些尤其碰到系統(tǒng)大模塊耐心等就行。如果你在公司內(nèi)網(wǎng)或離線環(huán)境符號(hào)下載不了就找一個(gè)有網(wǎng)的機(jī)器把符號(hào)緩存整個(gè)C:\Symbols目錄復(fù)制過(guò)去也能用。更穩(wěn)妥的方式是用symchk預(yù)先拉取目標(biāo)模塊的符號(hào)。2.3 轉(zhuǎn)儲(chǔ)文件從哪里來(lái)Minidump、MEMORY.DMP與轉(zhuǎn)儲(chǔ)參數(shù)DMP文件一般有兩個(gè)位置C:\Windows\Minidump小內(nèi)存轉(zhuǎn)儲(chǔ)通常幾百KB到幾MB包含崩潰時(shí)的關(guān)鍵內(nèi)存和調(diào)用棧日常分析最快。C:\Windows\MEMORY.DMP完整或內(nèi)核內(nèi)存轉(zhuǎn)儲(chǔ)可能有幾個(gè)GB信息最全但分析起來(lái)也慢。如果你想系統(tǒng)在藍(lán)屏?xí)r穩(wěn)定生成轉(zhuǎn)儲(chǔ)去“系統(tǒng)屬性→高級(jí)→啟動(dòng)和故障恢復(fù)→設(shè)置”里檢查一下。建議把“寫(xiě)入調(diào)試信息”留成“自動(dòng)內(nèi)存轉(zhuǎn)儲(chǔ)”或者按需改成“小內(nèi)存轉(zhuǎn)儲(chǔ)(256KB)”。還有兩個(gè)細(xì)節(jié)建議關(guān)掉“自動(dòng)重新啟動(dòng)”否則藍(lán)屏一閃就重啟現(xiàn)場(chǎng)信息丟失來(lái)不及拍照記錄錯(cuò)誤碼。轉(zhuǎn)儲(chǔ)寫(xiě)入依賴(lài)頁(yè)面文件頁(yè)面文件太小會(huì)導(dǎo)致轉(zhuǎn)儲(chǔ)生成失敗。想讓藍(lán)屏分析可靠系統(tǒng)盤(pán)頁(yè)面文件至少留2GB以上最好是“系統(tǒng)管理的大小”。如果你發(fā)現(xiàn)Minidump目錄是空的先別急著懷疑“沒(méi)藍(lán)屏過(guò)”。去系統(tǒng)盤(pán)根目錄看有沒(méi)有MEMORY.DMP那個(gè)同樣能分析。還有可能是之前的轉(zhuǎn)儲(chǔ)被清理工具刪了或者是Windows在快速啟動(dòng)機(jī)制下沒(méi)有完成轉(zhuǎn)儲(chǔ)寫(xiě)入。2.4 先對(duì)齊時(shí)間用事件日志給DMP文件“洗底”很多人拿到DMP就急著拖進(jìn)WinDbg我建議你先做一個(gè)60秒的準(zhǔn)備工作打開(kāi)事件查看器找到“Windows日志→系統(tǒng)”篩選Event ID 1001可以看到每次BugCheck的時(shí)間和錯(cuò)誤代碼。然后把C:\Windows\Minidump里的文件按修改時(shí)間排序挑出對(duì)應(yīng)時(shí)間的那個(gè)。這一步看著簡(jiǎn)單其實(shí)作用很大。它能避免你分析了一份過(guò)期的轉(zhuǎn)儲(chǔ)——尤其是那種“一年前藍(lán)屏過(guò)一次今天又藍(lán)屏”的情況兩個(gè)DMP可能指向完全不同的原因。先對(duì)齊時(shí)間再?zèng)Q定分析哪份文件分析結(jié)論才靠譜。如果你發(fā)現(xiàn)Minidump里有十幾個(gè)文件而用戶只描述“最近經(jīng)常藍(lán)屏”那就先看最新的一份。但不要只看一份后面我會(huì)講到多份轉(zhuǎn)儲(chǔ)之間的橫向?qū)Ρ韧葐畏萆钔诟菀妆┞缎判摹?. 拿到DMP后的五個(gè)核心步驟從!analyze -v到證據(jù)鏈閉合3.1 第一步打開(kāi)轉(zhuǎn)儲(chǔ)文件先看系統(tǒng)版本摘要在WinDbg里打開(kāi)DMP文件菜單路徑是File → Start debugging → Open dump file最新版叫Open Dump File直接選中.dmp文件即可。也可以用命令行windbg -z C:\Windows\Minidump\081318-23451-01.dmp打開(kāi)之后WinDbg會(huì)自動(dòng)加載符號(hào)輸出窗口會(huì)先顯示目標(biāo)系統(tǒng)的版本、構(gòu)建號(hào)、CPU數(shù)量。別直接跳過(guò)這一段因?yàn)槟闶紫纫_認(rèn)的是這份轉(zhuǎn)儲(chǔ)來(lái)自哪個(gè)Windows版本是不是這臺(tái)機(jī)器的有些轉(zhuǎn)儲(chǔ)可能是別的機(jī)器拷貝來(lái)的構(gòu)建號(hào)對(duì)不上后續(xù)分析會(huì)出現(xiàn)誤導(dǎo)。比如輸出里出現(xiàn)Windows 10 Kernel Version 19041說(shuō)明這是2004版或之后的系統(tǒng)。構(gòu)建號(hào)對(duì)分析有參考價(jià)值——某些BugCheck只在特定構(gòu)建號(hào)上出現(xiàn)你可以在社區(qū)搜“構(gòu)建號(hào)錯(cuò)誤碼”往往能直接找到已知問(wèn)題。3.2 第二步執(zhí)行!analyze -v先看它怎么說(shuō)假設(shè)你已經(jīng)看到了熟悉的BugCheck字樣下一步就是在命令窗口輸入!analyze -v這是WinDbg最核心的自動(dòng)分析命令。它會(huì)解析DMP中的關(guān)鍵信息輸出這樣一段內(nèi)容BugCheck D1, {fffff8800a112340, 0000000000000002, 0000000000000008, fffff8800a10f510} DRIVER_IRQL_NOT_LESS_OR_EQUAL Probably caused by : Netwtw04.sys這里有三塊信息要先看BugCheck行錯(cuò)誤代碼和四個(gè)參數(shù)。參數(shù)的解釋在后面細(xì)講。Probably caused byWinDbg給出的推測(cè)模塊。注意“probably”這個(gè)詞它不是結(jié)論是線索。下面的STACK_TEXT崩潰時(shí)的調(diào)用棧這是后續(xù)人工確認(rèn)的重點(diǎn)。!analyze -v還會(huì)輸出IMAGE_NAME、MODULE_NAME、FAILURE_BUCKET_ID等字段。新手最容易犯的錯(cuò)誤是把MODULE_NAME當(dāng)作最終結(jié)論直接寫(xiě)在報(bào)告里但我的經(jīng)驗(yàn)是它至少有30%的概率會(huì)把你的注意力帶偏尤其遇到符號(hào)不全或?;厮荼唤?cái)嗟那闆r。3.3 第三步參數(shù)和?;厮菰趺唇徊骝?yàn)證如果!analyze -v的輸出已經(jīng)很明顯比如?;厮堇镞B續(xù)出現(xiàn)同一個(gè)第三方驅(qū)動(dòng)那基本可以下判斷。但更常見(jiàn)的情況是“似乎指向A驅(qū)動(dòng)又有點(diǎn)牽扯B驅(qū)動(dòng)”這時(shí)候必須做交叉驗(yàn)證。執(zhí)行.ecxr kb.ecxr會(huì)把調(diào)試上下文切換到異常發(fā)生的那個(gè)線程kb顯示它的調(diào)用棧。這一步很關(guān)鍵因?yàn)?analyze -v給出的棧回溯有時(shí)是自動(dòng)判斷的不一定是最完整的現(xiàn)場(chǎng)。切換上下文之后再展開(kāi)往往能多看幾層看到崩潰前到底調(diào)用了什么。如果棧回溯里出現(xiàn)大量系統(tǒng)模塊比如nt!KeBugCheckEx、nt!KiBugCheckDispatch別緊張那是藍(lán)屏的固定路徑真正要盯的是棧里靠下的位置——那個(gè)觸發(fā)異常的具體模塊。再配合lmvm Netwtw04lmvm會(huì)顯示該模塊的詳細(xì)信息包括文件版本、時(shí)間戳、符號(hào)加載狀態(tài)。驅(qū)動(dòng)的時(shí)間戳尤其重要如果崩潰模塊是個(gè)兩年前的舊驅(qū)動(dòng)而它又指向了一個(gè)已知的硬件bug那結(jié)論就非常扎實(shí)。3.4 第四步查錯(cuò)誤碼的參數(shù)含義區(qū)分根因類(lèi)型BugCheck代碼底下的四個(gè)參數(shù)不是擺設(shè)它們是區(qū)分根因的關(guān)鍵。以0x000000D1為例常見(jiàn)含義是參數(shù)1被訪問(wèn)的內(nèi)存地址參數(shù)2中斷請(qǐng)求級(jí)別IRQL參數(shù)3操作類(lèi)型0表示讀1表示寫(xiě)8表示執(zhí)行參數(shù)4引用該內(nèi)存的指令地址如果你發(fā)現(xiàn)參數(shù)1是個(gè)低地址比如0x00000005之類(lèi)的大概率是空指針解引用如果參數(shù)3是1寫(xiě)操作說(shuō)明驅(qū)動(dòng)在往一個(gè)不該寫(xiě)的地方寫(xiě)數(shù)據(jù)如果參數(shù)2非常高比如IRQL等于2以上那就要關(guān)注驅(qū)動(dòng)是否在錯(cuò)誤的IRQL上執(zhí)行了分頁(yè)內(nèi)存訪問(wèn)。這里要提醒一下不同Windows版本的參數(shù)含義可能有細(xì)微差別。拿不準(zhǔn)的時(shí)候去微軟文檔里搜“Bug Check 0xD1”或者“Bug Check 0x50”查對(duì)應(yīng)頁(yè)面比盲目猜要靠譜得多。3.5 第五步用模塊列表和驅(qū)動(dòng)時(shí)間戳補(bǔ)全證據(jù)鏈?;厮堇镏傅侥硞€(gè)驅(qū)動(dòng)這只是“最后一步踩空”。真正重要的是搞清楚“為什么它會(huì)踩空”。這時(shí)候要看整個(gè)系統(tǒng)的驅(qū)動(dòng)加載情況lm t n這條命令會(huì)列出所有已加載的內(nèi)核模塊和驅(qū)動(dòng)。重點(diǎn)看第三方驅(qū)動(dòng)比如網(wǎng)卡、顯卡、虛擬化軟件、安全軟件的驅(qū)動(dòng)。很多時(shí)候崩潰棧里看到的驅(qū)動(dòng)并不是問(wèn)題的源頭——源頭可能是另一個(gè)驅(qū)動(dòng)破壞了內(nèi)存池然后崩潰正好發(fā)生在第三個(gè)驅(qū)動(dòng)里。我的判斷順序是這樣的先用!analyze -v拿到候選模塊再用lmvm確認(rèn)候選模塊的版本和時(shí)間戳?xí)r間戳太舊是重大嫌疑然后用.ecxrkb看完整調(diào)用棧確認(rèn)崩潰路徑最后lm t n檢查全局驅(qū)動(dòng)列表看有沒(méi)有其他老版本驅(qū)動(dòng)或已知問(wèn)題驅(qū)動(dòng)存在。這一套走完結(jié)論通常就比較穩(wěn)了比單看一行Probably caused by可靠得多。4. 高頻藍(lán)屏代碼實(shí)戰(zhàn)五個(gè)錯(cuò)誤碼的分析套路4.1 0x0000000A / 0xD1 這類(lèi)驅(qū)動(dòng)內(nèi)存訪問(wèn)違規(guī)怎么處理0x0000000AIRQL_NOT_LESS_OR_EQUAL和0x000000D1DRIVER_IRQL_NOT_LESS_OR_EQUAL本質(zhì)上是一家人都是在過(guò)高的IRQL下訪問(wèn)了可分頁(yè)內(nèi)存導(dǎo)致內(nèi)核無(wú)法繼續(xù)。區(qū)別是0x0A可以發(fā)生在任意內(nèi)核代碼里0xD1則明確指向驅(qū)動(dòng)。遇到這類(lèi)錯(cuò)誤碼我的套路是先看參數(shù)3如果操作類(lèi)型是寫(xiě)1重點(diǎn)查驅(qū)動(dòng)釋放內(nèi)存后是否仍在寫(xiě)也就是懸垂指針再看參數(shù)2的IRQL值如果大于PASSIVE_LEVEL0而?;卣{(diào)用到了分頁(yè)內(nèi)存那就是驅(qū)動(dòng)在錯(cuò)誤的時(shí)間做了錯(cuò)誤的事最后把崩潰棧里的模塊和時(shí)間戳記錄下來(lái)去網(wǎng)上搜“模塊名錯(cuò)誤碼”看是否已知問(wèn)題。這條排查鏈路特別適合網(wǎng)卡、聲卡驅(qū)動(dòng)的藍(lán)屏。很多人遇到0xD1第一時(shí)間懷疑內(nèi)存硬件但我經(jīng)手的案例里相當(dāng)大比例是驅(qū)動(dòng)bug。4.2 0x00000050 PAGE_FAULT_IN_NONPAGED_AREA的問(wèn)題別急著換內(nèi)存0x50的意思是系統(tǒng)訪問(wèn)了一個(gè)無(wú)效物理地址比如引用了已被釋放的頁(yè)面、訪問(wèn)了不存在的物理內(nèi)存或者對(duì)不可分頁(yè)區(qū)域執(zhí)行了分頁(yè)操作。錯(cuò)誤信息里經(jīng)??吹揭欢裯t!Mi開(kāi)頭的函數(shù)很容易讓人以為是內(nèi)存條壞了。但這里有個(gè)經(jīng)典誤判0x50也可能是驅(qū)動(dòng)“釋放內(nèi)存后繼續(xù)使用”造成的。判斷方式有兩個(gè)看崩潰棧中是否有第三方驅(qū)動(dòng)在ExFreePool之后又訪問(wèn)了同一個(gè)地址看參數(shù)1錯(cuò)誤的內(nèi)存地址是否接近驅(qū)動(dòng)常用的池地址范圍。如果?;厮堇镏挥衝t內(nèi)核函數(shù)沒(méi)有任何第三方模塊那硬件嫌疑才上升。這種情況下我會(huì)建議先跑MemTest86再檢查CPU的IMC內(nèi)存控制器和BIOS里的XMP設(shè)置最后才是換內(nèi)存。4.3 0x0000003B SYSTEM_SERVICE_EXCEPTION異常代碼是關(guān)鍵0x3B表示在執(zhí)行系統(tǒng)服務(wù)時(shí)發(fā)生了未處理的異常。這類(lèi)藍(lán)屏經(jīng)常和顯卡驅(qū)動(dòng)、存儲(chǔ)驅(qū)動(dòng)、安全軟件驅(qū)動(dòng)有關(guān)。它的四參數(shù)里第一個(gè)就是異常代碼比如0xc0000005訪問(wèn)沖突、0xc000000d非法指令后面是異常發(fā)生的指令地址。我的分析重點(diǎn)是看?;厮葜谐霈F(xiàn)的是哪個(gè)模塊。如果指向dxgkrnl.sys、nvlddmkm.sys、athwb.sys這類(lèi)圖形或網(wǎng)絡(luò)驅(qū)動(dòng)大概率是驅(qū)動(dòng)在處理請(qǐng)求時(shí)越界。如果?;厮堇锶窍到y(tǒng)模塊那可能是系統(tǒng)服務(wù)出現(xiàn)了堆損壞需要進(jìn)一步檢查是否有其他驅(qū)動(dòng)破壞了堆。補(bǔ)充一條經(jīng)驗(yàn)0x3B的崩潰現(xiàn)場(chǎng)經(jīng)常發(fā)生在系統(tǒng)負(fù)載高、USB設(shè)備插拔頻繁的時(shí)間點(diǎn)排查時(shí)不妨問(wèn)問(wèn)用戶“藍(lán)屏前在做什么”往往能幫你快速縮小范圍。4.4 0x0000001A MEMORY_MANAGEMENT內(nèi)存池?fù)p壞的排查方向0x1A是內(nèi)存管理器檢查到內(nèi)部狀態(tài)不一致比如頁(yè)表項(xiàng)被寫(xiě)壞、PFN列表被破壞。這類(lèi)錯(cuò)誤一出很多人直接判定“內(nèi)存條壞了”但我更愿意先把它當(dāng)“內(nèi)存被驅(qū)動(dòng)踩了”來(lái)查。!analyze -v輸出里會(huì)有一個(gè)子錯(cuò)誤碼Arg1不同子碼對(duì)應(yīng)不同的內(nèi)存損壞場(chǎng)景??吹?x1A時(shí)我會(huì)額外執(zhí)行!memanalysis或者用!pool這兩個(gè)命令會(huì)掃描內(nèi)存池的標(biāo)記和潛在損壞。如果某個(gè)驅(qū)動(dòng)名的池標(biāo)記反復(fù)出現(xiàn)那基本可以斷定是這個(gè)驅(qū)動(dòng)在池上亂寫(xiě)導(dǎo)致內(nèi)存管理結(jié)構(gòu)錯(cuò)亂。這時(shí)候換內(nèi)存條解決不了問(wèn)題該更新驅(qū)動(dòng)、卸載沖突軟件才對(duì)。4.5 0x000000EF CRITICAL_PROCESS_DIED先查系統(tǒng)再查驅(qū)動(dòng)0xEF表示系統(tǒng)關(guān)鍵進(jìn)程意外退出比如wininit.exe、csrss.exe、services.exe。這類(lèi)問(wèn)題要分兩步看第一步確認(rèn)是哪個(gè)進(jìn)程死了、退出碼多少第二步看這個(gè)進(jìn)程死前在跑什么是不是某個(gè)驅(qū)動(dòng)引發(fā)的。參數(shù)1通常指向進(jìn)程對(duì)象參數(shù)2是退出碼。比如退出碼是0xc0000409棧緩沖區(qū)溢出那就要重點(diǎn)查是否有安全軟件或反作弊軟件在注入該進(jìn)程。0xEF還有一個(gè)常見(jiàn)背景是系統(tǒng)文件被破壞或系統(tǒng)盤(pán)故障我一般會(huì)先建議跑sfc /scannow和chkdsk /f再回來(lái)分析驅(qū)動(dòng)因素。0xEF比較麻煩的一點(diǎn)是進(jìn)程死了的現(xiàn)場(chǎng)往往很干凈?;厮堇锊灰欢芸吹皆獌茨K。這時(shí)候我會(huì)直接對(duì)比前后多份轉(zhuǎn)儲(chǔ)看是否有同一個(gè)驅(qū)動(dòng)反復(fù)出現(xiàn)在崩潰線程的模塊列表里。5. 踩坑記錄符號(hào)加載失敗、誤報(bào)驅(qū)動(dòng)與轉(zhuǎn)儲(chǔ)缺失的排查鏈路5.1 符號(hào)加載失敗別再被“問(wèn)號(hào)堆?!睅业谝淮斡肳inDbg分析DMP時(shí)打開(kāi)文件后?;厮萑???還以為轉(zhuǎn)儲(chǔ)文件壞了。實(shí)際上就是符號(hào)沒(méi)配好。排查鏈路是這樣的先輸入.sympath確認(rèn)當(dāng)前符號(hào)路徑看緩存目錄寫(xiě)沒(méi)寫(xiě)對(duì)檢查C:\Symbols目錄是否正在增長(zhǎng)如果文件數(shù)在漲說(shuō)明符號(hào)在下載確認(rèn)網(wǎng)絡(luò)能訪問(wèn)符號(hào)服務(wù)器公司內(nèi)網(wǎng)或防火墻經(jīng)常會(huì)攔如果路徑?jīng)]問(wèn)題但個(gè)別模塊還是問(wèn)號(hào)執(zhí)行.reload /f強(qiáng)制重載一次仍不行就人工檢查目標(biāo)模塊的符號(hào)包去微軟符號(hào)服務(wù)器對(duì)應(yīng)頁(yè)面手動(dòng)下載。符號(hào)加載失敗時(shí)!analyze -v的輸出質(zhì)量會(huì)直線下降可能連Probably caused by都打不出來(lái)。所以遇到問(wèn)號(hào)堆棧第一件事不是懷疑DMP損壞而是查符號(hào)。5.2 懷疑驅(qū)動(dòng)A卻崩在驅(qū)動(dòng)Bprobable cause的“偽證”時(shí)刻有一次一份0x50轉(zhuǎn)儲(chǔ)!analyze -v明確指向某安全軟件的監(jiān)控驅(qū)動(dòng)時(shí)間戳也合理?xiàng);厮堇镆泊_實(shí)有它。我當(dāng)時(shí)差點(diǎn)直接下結(jié)論讓用戶卸載。后來(lái)多看了兩眼發(fā)現(xiàn)真正引發(fā)崩潰的是一個(gè)磁盤(pán)過(guò)濾驅(qū)動(dòng)它釋放了一塊池內(nèi)存之后沒(méi)有把指針置空另一個(gè)模塊在用同一個(gè)指針時(shí)踩到了被釋放的區(qū)域最終崩在了安全軟件驅(qū)動(dòng)的地盤(pán)上。這個(gè)案例給我最大的教訓(xùn)是Probably caused by是“崩潰時(shí)正好在這”不代表“根因一定在這”?,F(xiàn)在我的習(xí)慣是每次得出結(jié)論前至少驗(yàn)證三處lmvm的時(shí)間戳、.ecxrkb的完整棧、全局驅(qū)動(dòng)列表里是否有異常驅(qū)動(dòng)。三者指向同一模塊才敢寫(xiě)最終判斷。5.3 轉(zhuǎn)儲(chǔ)文件缺失或打不開(kāi)先檢查這兩種常見(jiàn)原因打不開(kāi)DMP最常見(jiàn)的原因是架構(gòu)不匹配。比如你用64位WinDbg去打開(kāi)32位系統(tǒng)生成的內(nèi)核轉(zhuǎn)儲(chǔ)會(huì)報(bào)錯(cuò)或者顯示一堆亂碼。解決辦法是明確轉(zhuǎn)儲(chǔ)來(lái)源系統(tǒng)的架構(gòu)下載對(duì)應(yīng)x64或x86的調(diào)試器。轉(zhuǎn)儲(chǔ)文件缺失則要回系統(tǒng)設(shè)置里查重點(diǎn)看“啟動(dòng)和故障恢復(fù)”中的“寫(xiě)入調(diào)試信息”選項(xiàng)。還有一個(gè)容易忽略的點(diǎn)系統(tǒng)盤(pán)的頁(yè)面文件必須足夠大否則藍(lán)屏瞬間寫(xiě)轉(zhuǎn)儲(chǔ)失敗。有些優(yōu)化軟件為了省磁盤(pán)空間會(huì)把頁(yè)面文件設(shè)置得特別小這會(huì)導(dǎo)致藍(lán)屏了卻什么也沒(méi)留下。如果你在做批量電腦維護(hù)記得把這個(gè)選項(xiàng)單獨(dú)檢查一遍別等藍(lán)屏了才發(fā)現(xiàn)根本沒(méi)轉(zhuǎn)儲(chǔ)文件可分析。5.4 硬件問(wèn)題濾鏡判斷內(nèi)存故障前先排除驅(qū)動(dòng)踩踏很多藍(lán)屏代碼尤其是0x1A、0x50、0x0A這一類(lèi)表面看起來(lái)都像內(nèi)存問(wèn)題。我看過(guò)太多的排查報(bào)告上來(lái)就寫(xiě)“內(nèi)存故障請(qǐng)更換內(nèi)存條”。但如果你往深挖一層會(huì)發(fā)現(xiàn)不少案子里驅(qū)動(dòng)踩內(nèi)存才是根源。我的個(gè)人判斷順序是這樣先用!analyze -v和?;厮菖懦?qū)動(dòng)因素如果確認(rèn)棧里全是系統(tǒng)模塊再考慮運(yùn)行!memanalysis看內(nèi)存池標(biāo)記內(nèi)存池也干凈才輪到硬件方向這時(shí)候建議先跑MemTest86和CPU壓力測(cè)試再做替換法。反過(guò)來(lái)也有一種情況?;厮堇锎_實(shí)指向某個(gè)驅(qū)動(dòng)模塊但這個(gè)模塊的行為異常是硬件錯(cuò)誤引發(fā)的比如CPU緩存或內(nèi)存控制器出錯(cuò)導(dǎo)致驅(qū)動(dòng)讀到錯(cuò)誤數(shù)據(jù)。這種場(chǎng)景l(fā)mvm時(shí)間戳再新也說(shuō)不清。這時(shí)需要結(jié)合系統(tǒng)事件日志里有沒(méi)有WHEA硬件錯(cuò)誤記錄來(lái)判斷如果同時(shí)出現(xiàn)Event 18、Event 19這類(lèi)WHEA事件那硬件因素就得優(yōu)先考慮。5.5 命令拆彈內(nèi)核轉(zhuǎn)儲(chǔ)大、分析慢時(shí)的高效技巧如果只拿到了幾個(gè)GB的MEMORY.DMP打開(kāi)和波動(dòng)都很痛苦。我的建議是先用小內(nèi)存轉(zhuǎn)儲(chǔ)Minidump做快速定位絕大多數(shù)場(chǎng)景小轉(zhuǎn)儲(chǔ)的信息足夠如果一定要用完整轉(zhuǎn)儲(chǔ)打開(kāi)后先輸!analyze -v別急著展開(kāi)其他命令讓它慢慢跑完分析過(guò)程中可以用.logopen把輸出重定向到文件避免窗口滾動(dòng)丟失內(nèi)容遇到反復(fù)崩潰的機(jī)器別滿足于分析最新的那份把最近三五個(gè)DMP都過(guò)一遍看看崩潰模塊是否一致。如果每次都指向同一個(gè)驅(qū)動(dòng)這個(gè)證據(jù)比任何單次現(xiàn)場(chǎng)都更有說(shuō)服力。另外微軟文檔里很多BugCheck頁(yè)面下面有!analyze -show的用法它可以重新顯示自動(dòng)分析結(jié)果。如果你在分析過(guò)程中把輸出刷掉了不用重新跑一遍自動(dòng)分析直接執(zhí)行這個(gè)命令就能把結(jié)論調(diào)回來(lái)。我自己還有一個(gè)習(xí)慣分析完成之后把!analyze -v、lmvm和相關(guān)棧回溯的文本都保存到日志里文件名命名成“日期_BugCheck代碼_核心模塊.txt”。這樣后續(xù)再藍(lán)屏直接翻舊檔對(duì)比能省不少時(shí)間。這個(gè)系列下一篇我打算重點(diǎn)講怎么在一堆DMP里做橫向?qū)Ρ纫约叭绾螐谋罎⒓?xì)節(jié)反推“是驅(qū)動(dòng)更新問(wèn)題還是硬件退化問(wèn)題”。先把這一篇里的流程用熟練遇到藍(lán)屏就不會(huì)再像個(gè)無(wú)頭蒼蠅一樣亂猜了。