當前位置: 妍妍網 > 碼農

記一次 .NET某列控連鎖系統 崩潰分析

2024-02-23碼農

一:背景

1. 講故事

過年喝了不少酒,腦子不靈光了,停了將近一個月沒寫部落格,今天就當新年開工寫一篇吧。

去年年初有位朋友找到我,說他們的系統會偶發性崩潰,在網上也發了不少貼文求助,沒找到自己滿意的答案,讓我看看有沒有什麽線索,看樣子這是一個牛皮蘚的問題,既然對方有了dump,那就分析起來吧。

二:WinDbg分析

1.為什麽會崩潰

不管是 windows 還是 linux 上的.net程式崩潰都會存在異常碼,前者是ExceptionCode,後者是 SignalCode,所以先用 !analyze -v 觀察看看。


0:003> !analyze -v
CONTEXT: (.ecxr)
eax=00000008 ebx=00639498 ecx=00000001 edx=0c75e4c8 esi=0063ec88 edi=083d8f77
eip=71ecaf96 esp=1ad2fa00 ebp=1ad2fa58 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
clr!SVR::gc_heap::mark_object_simple1+0x382:
71ecaf96 f70000000080 test dword ptr [eax],80000000h ds:002b:00000008=????????
Resetting default scope
EXCEPTION_RECORD: (.exr -1)
ExceptionAddress: 71ecaf96 (clr!SVR::gc_heap::mark_object_simple1+0x00000382)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000001
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 00000008
Attempt to read from address 00000008

從卦中資訊看,程式崩在 clr!SVR::gc_heap::mark_object_simple1 方法裏,這表示當前gc觸發時clr在托管堆標記物件時發現堆損壞了,那到底是不是托管堆損壞呢?可以用 !verifyheap 命令驗證下,輸出如下:


0:003> !verifyheap 
object083d8f50: bad member 093D8F90 at 083D8F74
Last good object083D8F3C.
object0c75e4c0: bad member 083D8F77 at 0C75E4C8
Last good object0C75E454.

從卦中資訊看,確實存在著兩個壞物件 083d8f50 0c75e4c0 ,接下來的研究重點就是為什麽這兩個物件會破壞?

2. 物件為什麽損壞了

為了方便解讀我們從 083d8f50 入手,先用 !do 觀察它的布局。


0:003> !do083d8f50
Name: System.Windows.DependencyProperty
MethodTable: 727ebe60
EE class: 72708570
Size: 44(0x2c) bytes
Fields:
MT Field Offset Type VT Attr Value Name
...
727e7f0c 40011fe 24 ....InsertionSortMap 1 instance 083d8f74 _metadataMap
...
0:003> !DumpVC /d 727e7f0c 083d8f74
Name: MS.Utility.InsertionSortMap
MethodTable: 727e7f0c
EE class: 72721ec8
Size: 12(0xc) bytes
Fields:
MT Field Offset Type VT Attr Value Name
727e7f44 40007e90 ...geSortedObjectMap 0 instance 093d8f90 _mapStore
0:003> !DumpObj /d 093d8f90
<Note: thisobject has an invalid class field>
Invalid object

根據物件的布局知識, 093d8f90 存放的是 mt,看樣子是mt被損壞了,接下來用 dp 觀察下這個地址的附近記憶體。


0:003> dp 093d8f90-0x20 L10
093d8f70 727e8b50 032e37501085da60 04a46fd9
093d8f80 00000000727e8b50 032e3a48 08157ef0
093d8f90 0321727200000000727e8b50 032e353c
093d8fa0 08079f9c 02c028d2 00000000727e8b50
0:003> !lno 093d8f90
Before: 093d8f84 20 (0x14) MS.Internal.WeakEventTable+EventKey
After: 093d8f98 20 (0x14) MS.Internal.WeakEventTable+EventKey
Heap local consistency confirmed.
0:003> !do093d8f84
Name: MS.Internal.WeakEventTable+EventKey
MethodTable: 727e8b50
EE class: 727076c4
Size: 20(0x14) bytes
File: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll
Fields:
MT Field Offset Type VT Attr Value Name
727ec0f4 4001a28 4 ....WeakEventManager 0 instance 032e3a48 _manager
7112dbd4 4001a29 8 System.Object 0 instance 08157ef0 _source
7112f6bc 4001a2a c System.Int32 1 instance 52523634 _hashcode

從卦中資訊看 mt=03217272 肯定是不對的,但奇怪的是這附近的記憶體並沒失真壞,它是 EventKey._hashcode 的16進制表示,我去,這就奇葩了。。。一下子陷入了迷茫。

3. 記憶體為什麽沒壞

說實話分析了這麽多的dump,這種情況還是第一次遇見,根據上一節的分析,現在可以懷疑 093d8f90 這個地址本身就是錯的,接下來觀察它的所屬地址 083d8f74 附近的記憶體。


0:003> dp 083d8f74 -0x40 L20
083d8f34 0000000300000000727e72c9 083d8ba4
083d8f44 083d8ec8 775e7de3 00000000727ebe61
083d8f54 083d8ba4 0ec9e934 083d8ec8 083d8f20
083d8f64 00000000000000000000000000020368
083d8f74 093d8f90 00000000727ee329 083d8ec8
083d8f84 0ec9eaf4 000000e000000000727e7f45
083d8f94 083d8fa4 00000001000000e000000000
083d8fa4 727e7fb1 00000002083d8f04 000000e0

如果你仔細觀察卦中的區域記憶體地址,你會發現一個有意思的現象,它附近的物件都是 083 開頭的,憑什麽它是 093 開頭的?對他產生了懷疑之後,我們觀察下托管堆中 mt=727e7f44 下是否存在 093d8f90 的例項,截圖如下:

從卦中可以清晰的看到確實不存在 093d8f90 物件,但存在一個將 9 -> 8 之隔的 083d8f90 ,有經驗的朋友應該知道,這又是一例經典的bit位翻轉導致的程式崩潰,可以用 .formats 命令觀察下二進制的布局:

4. 另一個壞物件也是如此嗎

剛才的 093d8f90 我們搞明白了是由於bit位翻轉導致,那 083d8f77 也是如此嗎?有了剛才的經驗這個就比較好驗證了,可以查一下 mt=727ee328 下是否有這個例項。


0:003> !do0c75e4c0
Name: System.Windows.Media.Pen
MethodTable: 6f38a110
EE class: 6f1568b4
Size: 40(0x28) bytes
File: C:\Windows\Microsoft.Net\assembly\GAC_32\PresentationCore\v4.0_4.0.0.0__31bf3856ad364e35\PresentationCore.dll
Fields:
MT Field Offset Type VT Attr Value Name
...
727ee328 40011dc 8 ...endencyObjectType 0 instance 083d8f77 _dType
...
0:003> !dumpheap -mt 727ee328
 Address MT Size
...
083d8db0 727ee328 20
083d8f7c 727ee328 20
...

又是一個無語的結論,原來 083d8f7c 被錯賦成了 083d8f77 ,用 .formats 命令觀察之後發現有 3個bit的翻轉。截圖如下:

三:總結

有豐富經驗的朋友肯定知道,bit翻轉大多是 放射線 導致電腦數位訊號偶發的紊亂,這也是為什麽醫院要加個鉛版來阻隔,而這個程式所處的環境剛好是放射線比較多的高鐵系。

所以分析完之後,非我等偵錯師能為之,遠離放射線。。。