2023-06-26 18:05:42來源:一線碼農(nóng)聊技術(shù)
前段時間有個朋友找到我,說他們的程序有偶發(fā)崩潰的情況,讓我?guī)兔聪略趺椿厥拢槍@種 crash 的程序,用 AEDebug 的方式抓取一個便知,有了 dump 之后接下來就可以分析了。
二:Windbg 分析1. 為什么會崩潰既然是程序的崩潰,我們可以像看藍屏一下看dump文件,使用!analyze -v命令即可。
0:000> !analyze -v******************************************************************************** ** Exception Analysis ** ********************************************************************************CONTEXT: (.ecxr)rax=0000000000000000 rbx=0000000000f7ccb0 rcx=00007ffe23af7ab0rdx=00000000013e3b10 rsi=0000000000f7ccb0 rdi=0000000000f7c7c0rip=00007ffe538e7044 rsp=0000000000f7cf60 rbp=0000000000f7d770 r8=0000000000000001 r9=000000f7000006bd r10=0000000000f7d640r11=0000000000f7cf50 r12=0000000000000001 r13=0000000000000001r14=000000005c520126 r15=00000000013e3b10iopl=0 nv up ei pl nz na po nccs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010204clr!ComPreStubWorker+0xf2e54:00007ffe`538e7044 f6403820 test byte ptr [rax+38h],20h ds:00000000`00000038=??Resetting default scopeEXCEPTION_RECORD: (.exr -1)ExceptionAddress: 00007ffe538e7044 (clr!ComPreStubWorker+0x00000000000f2e54) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000001NumberParameters: 2 Parameter[0]: 0000000000000000 Parameter[1]: 0000000000000038Attempt to read from address 0000000000000038PROCESS_NAME: xxx.exeREAD_ADDRESS: 0000000000000038 ERROR_CODE: (NTSTATUS) 0xc0000005 - 0x%p 0x%p %sEXCEPTION_CODE_STR: c0000005EXCEPTION_PARAMETER1: 0000000000000000EXCEPTION_PARAMETER2: 0000000000000038STACK_TEXT: 00000000`00f7cf60 00007ffe`538e7044 clr!ComPreStubWorker+0xf2e5400000000`00f7d590 00007ffe`53712d62 clr!ComCallPreStub+0x6200000000`00f7d660 00007ffe`1de3ba83 wwkrn64+0xba8300000000`00f7d740 00007ffe`638ebc70 ole32!CPrivDragDrop::PrivDragDrop+0x2b000000000`00f7d790 00007ffe`638eb98c ole32!PrivDragDrop+0x19800000000`00f7d830 00007ffe`638a9c1e ole32!CDragOperation::GetDropTarget+0xee00000000`00f7d8b0 00007ffe`638ac239 ole32!CDragOperation::UpdateTarget+0x4cd....
從上面的信息看,這個程序是一個經(jīng)典的訪問違例異常,違例是因為rax=0導(dǎo)致讀取了不該讀取的地方,接下來我們切到異常上下文看下為什么會是 0 ?
(相關(guān)資料圖)
要想切到異常上下文,先使用.ecxr命令,再使用 ub 反匯編。
0:000> .ecxrrax=0000000000000000 rbx=0000000000f7ccb0 rcx=00007ffe23af7ab0rdx=00000000013e3b10 rsi=0000000000f7ccb0 rdi=0000000000f7c7c0rip=00007ffe538e7044 rsp=0000000000f7cf60 rbp=0000000000f7d770 r8=0000000000000001 r9=000000f7000006bd r10=0000000000f7d640r11=0000000000f7cf50 r12=0000000000000001 r13=0000000000000001r14=000000005c520126 r15=00000000013e3b10iopl=0 nv up ei pl nz na po nccs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010204clr!ComPreStubWorker+0xf2e54:00007ffe`538e7044 f6403820 test byte ptr [rax+38h],20h ds:00000000`00000038=??0:000> ub 00007ffe`538e7044clr!ComPreStubWorker+0xf2e20:00007ffe`538e7010 0f8591d3f0ff jne clr!ComPreStubWorker+0x1b4 (00007ffe`537f43a7)00007ffe`538e7016 488b0e mov rcx,qword ptr [rsi]00007ffe`538e7019 488d81b8ffffff lea rax,[rcx-48h]00007ffe`538e7020 48898424c0050000 mov qword ptr [rsp+5C0h],rax00007ffe`538e7028 48898424b8050000 mov qword ptr [rsp+5B8h],rax00007ffe`538e7030 488b89c0ffffff mov rcx,qword ptr [rcx-40h]00007ffe`538e7037 e8fcfcebff call clr!MethodTable::GetComCallWrapperTemplate (00007ffe`537a6d38)00007ffe`538e703c 48898424b0050000 mov qword ptr [rsp+5B0h],rax
從匯編代碼看,rax 是clr!MethodTable::GetComCallWrapperTemplate方法的返回值,從方法名字看是一個經(jīng)典的 COM 和 .NET 互操作,接下來繼續(xù)用uf反匯編看下這個方法,精簡后的代碼如下:
0:000> uf clr!MethodTable::GetComCallWrapperTemplate00007ffe`537a6d38 48895c2408 mov qword ptr [rsp+8],rbx00007ffe`537a6d3d 57 push rdi...00007ffe`537a6d57 488bcb mov rcx,rbx00007ffe`537a6d5a e8c943f7ff call clr!MethodTable::GetClass (00007ffe`5371b128)00007ffe`537a6d5f 488b4030 mov rax,qword ptr [rax+30h]...
再結(jié)合 coreclr 源碼:
inline ComCallWrapperTemplate *MethodTable::GetComCallWrapperTemplate(){ LIMITED_METHOD_CONTRACT; return GetClass()->GetComCallWrapperTemplate();}class EEClass // DO NOT CREATE A NEW EEClass USING NEW!{ ComCallWrapperTemplate* m_pccwTemplate; // points to interop data structures used when this type is exposed to COM inline ComCallWrapperTemplate *GetComCallWrapperTemplate() { LIMITED_METHOD_CONTRACT; return m_pccwTemplate; }}
到這里大概能推測到是因為EEClass.m_pccwTemplate字段為 null 所致,從注釋看,他是 CLR 用來暴露給 COM 使用的數(shù)據(jù)結(jié)構(gòu),那為什么暴露給 COM 使用的數(shù)據(jù)結(jié)構(gòu)為 NULL 呢? 這個分析起來就復(fù)雜了。
但有一點可以確定,像這種邏輯必然是堅如磐石,受過日月精華,經(jīng)歷過500年的風(fēng)吹雨打,不可能無緣無故的出簍子。
3. 出路在哪里要尋找突破口還得從調(diào)用棧入手,我們用k命令洞察一下。
0:000> k *** Stack trace for last set context - .thread/.cxr resets it # Child-SP RetAddr Call Site00 00000000`00f7cf60 00007ffe`53712d62 clr!ComPreStubWorker+0xf2e5401 00000000`00f7d590 00007ffe`1de3ba83 clr!ComCallPreStub+0x6202 00000000`00f7d660 00007ffe`638ebc70 wwkrn64+0xba8303 00000000`00f7d740 00007ffe`638eb98c ole32!CPrivDragDrop::PrivDragDrop+0x2b0 [com\ole32\com\rot\getif.cxx @ 659] 04 00000000`00f7d790 00007ffe`638a9c1e ole32!PrivDragDrop+0x198 [com\ole32\com\rot\getif.cxx @ 920] 05 00000000`00f7d830 00007ffe`638ac239 ole32!CDragOperation::GetDropTarget+0xee [com\ole32\ole232\drag\drag.cpp @ 1128] 06 00000000`00f7d8b0 00007ffe`638ac91c ole32!CDragOperation::UpdateTarget+0x4cd [com\ole32\ole232\drag\drag.cpp @ 2026] 07 00000000`00f7d9a0 00007ffe`2443f664 ole32!DoDragDrop+0x10c [com\ole32\ole232\drag\drag.cpp @ 3007] 08 00000000`00f7dc80 00007ffe`244ccd8d System_Windows_Forms_ni+0x9cf664...
仔細觀察線程棧信息,不難發(fā)現(xiàn)用戶是在用DoDragDrop方法實現(xiàn)控件的拖拽,不過在執(zhí)行流中有一個陌生的動態(tài)鏈接庫wwkrn64,它到底是何方神圣呢?我們用 lmvm 觀察下。
0:000> lmvm wwkrn64Browse full module liststart end module name00007ffe`1de30000 00007ffe`1df1e000 wwkrn64 C (export symbols) wwkrn64.dll Loaded symbol image file: wwkrn64.dll Image path: D:\xxx\wwall\wwkrn64.dll Image name: wwkrn64.dll Browse all global symbols functions data Timestamp: Wed Apr 26 10:18:26 2023 (644889F2) CheckSum: 00000000 ImageSize: 000EE000 Translations: 0000.04b0 0000.04e4 0409.04b0 0409.04e4 Information from resource tables:
從輸出信息看,果然是一個外來物種,經(jīng)過網(wǎng)上一頓搜索,發(fā)現(xiàn)是一款信息安全軟件,哪家公司就模糊了哈,截圖如下:
到這里就真相大白了,讓朋友把這款軟件卸載掉再試試看,問題就解決了。
4. 安全軟件為什么要介入我這里只能簡單推測一下,ComCallPreStub和ComPreStubWorker方法是 JIT 在編譯某一個方法時的前綴路徑,也是很多加殼軟件以及永恒之藍這樣的蠕蟲病毒重點關(guān)注的方法,所以這些高危方法自然也是安全軟件重點監(jiān)視的,如果安全軟件沒處理好,自然就會誤殺。。。
當(dāng)然真正的原因只能問系鈴人。
可能有些朋友要說了,怎么驗證這兩個方法就是 JIT 編譯的前綴,這里我們用 普通方法+windbg 的方法簡單驗證下吧,參考代碼如下:
internal class Program { static void Main(string[] args) { Debugger.Break(); Test(); Console.ReadLine(); } static void Test() { Console.WriteLine("Test1"); } }
接下來我們重點觀察下Test方法的編譯過程,看過程之前先上一張架構(gòu)圖:
從架構(gòu)圖看 Test() 方法的編譯最終是由clrjit!jitNativeCode來處理的,要想驗證很簡單用bp clrjit!jitNativeCode下一個斷點即可。
0:000> bp clrjit!jitNativeCode0:000> gBreakpoint 0 hitclrjit!jitNativeCode:00007ffb`590cc040 4c894c2420 mov qword ptr [rsp+20h],r9 ss:0000009c`5efed218=00000000000000000:000> k # Child-SP RetAddr Call Site00 0000009c`5efed1f8 00007ffb`5917d683 clrjit!jitNativeCode [D:\a\_work\1\s\src\coreclr\jit\compiler.cpp @ 6941] 01 0000009c`5efed200 00007ffb`594d3091 clrjit!CILJit::compileMethod+0x83 [D:\a\_work\1\s\src\coreclr\jit\ee_il_dll.cpp @ 279] 02 (Inline Function) --------`-------- coreclr!invokeCompileMethodHelper+0x86 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 12774] 03 (Inline Function) --------`-------- coreclr!invokeCompileMethod+0xc5 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 12839] 04 0000009c`5efed270 00007ffb`594d274d coreclr!UnsafeJitFunction+0x7f1 [D:\a\_work\1\s\src\coreclr\vm\jitinterface.cpp @ 13355] 05 0000009c`5efed760 00007ffb`594d22ce coreclr!MethodDesc::JitCompileCodeLocked+0x1f1 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 1051] 06 0000009c`5efed930 00007ffb`59472009 coreclr!MethodDesc::JitCompileCodeLockedEventWrapper+0x466 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 920] 07 0000009c`5efeda90 00007ffb`59473f58 coreclr!MethodDesc::JitCompileCode+0x2a9 [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 860] 08 (Inline Function) --------`-------- coreclr!MethodDesc::PrepareILBasedCode+0x5ae [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 439] 09 (Inline Function) --------`-------- coreclr!MethodDesc::PrepareCode+0x5ae [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 332] 0a 0000009c`5efedb40 00007ffb`5947340c coreclr!CodeVersionManager::PublishVersionableCodeIfNecessary+0x7f8 [D:\a\_work\1\s\src\coreclr\vm\codeversion.cpp @ 1701] 0b 0000009c`5efee070 00007ffb`5947316b coreclr!MethodDesc::DoPrestub+0x16c [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 2215] 0c 0000009c`5efee190 00007ffb`595abec5 coreclr!PreStubWorker+0x21b [D:\a\_work\1\s\src\coreclr\vm\prestub.cpp @ 2039] 0d 0000009c`5efee320 00007ffa`f9a0296e coreclr!ThePreStub+0x550e 0000009c`5efee3d0 00007ffb`595aae93 Example_19_1_1!Example_19_1_1.Program.Main+0x2e [D:\skyfly\19.20230624\src\Example\Example_19_1_1\Program.cs @ 10] ...
如果想在jitNativeCode方法中把md提取出來的話,可以取 r9 參數(shù)。
0:000> !dumpmd poi(r9)Method Name: Example_19_1_1.Program.Test()Class: 00007ffaf9abd520MethodTable: 00007ffaf9ac8880mdToken: 0000000006000006Module: 00007ffaf9ac6908IsJitted: noCurrent CodeAddr: ffffffffffffffffVersion History: ILCodeVersion: 0000000000000000 ReJIT ID: 0 IL Addr: 000001f865be20a7 CodeAddr: 0000000000000000 (MinOptJitted) NativeCodeVersion: 0000000000000000
三:總結(jié)這次崩潰事故的直接原因是由于第三方安全軟件的介入導(dǎo)致的,因ComPreStubWorker是加殼程序和蠕蟲病毒注入的突破口,不管怎樣還是希望安全軟件對高危函數(shù)ComPreStubWorker的照護邏輯再優(yōu)化下吧,減少誤殺的發(fā)生。
關(guān)鍵詞:
一:背景1 講故事前段時間有個朋友找到我,說他們的程序有偶發(fā)崩潰的情
疫情之下,餐飲行業(yè)經(jīng)受了,改革開放以來最嚴峻的寒冬。作為餐飲門店,
1、調(diào)節(jié)臺式電腦屏幕亮度的方法:工具材料臺式機電腦方法步驟①在電腦
大家好,我是漁夫子。代碼重構(gòu)是在不改變外部功能的情況下對現(xiàn)有代碼進
拉鏈表是一種常用的數(shù)據(jù)結(jié)構(gòu),通常用于記錄歷史數(shù)據(jù)的變化。在實際項目
原始需求是如果系統(tǒng)的密碼被修改,或者創(chuàng)建了新用戶,就告警出來。本質(zhì)
sync Condsync Cond是Golang標準庫提供的一個基于互斥鎖 讀寫鎖實現(xiàn)的
哈嘍,大家好,我是了不起。首先,Swagger這個工具能夠自動生成API接口
杭州亞運會已進入100天倒計時,代表中國參加亞運的“電競國家隊”終于
一場時隔26年的“世紀大和解”將長安馬自達送上了熱搜。6月25日晚間,
Jmeter錄制腳本原理腳本錄制時,Jmeter作為代理網(wǎng)關(guān),通過監(jiān)聽某個端口
大家好,我是前端西瓜哥,今天我們來了解WebGL的紋理對象(Texture)紋
Python是一門非常流行的編程語言,擁有豐富的第三方庫和工具,這些庫和
一、商業(yè)圈1 小米汽車售價曝光!起售不到15萬,續(xù)航800km日前媒體獲取
一項新的研究顯示,許多企業(yè)在重命名項目時,不知不覺地將其代碼庫的用