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

一:背景

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 
object 083d8f50: bad member 093D8F90 at 083D8F74
Last good object: 083D8F3C.
object 0c75e4c0: bad member 083D8F77 at 0C75E4C8
Last good object: 0C75E454.

從卦中信息看,確實存在着兩個壞對象 083d8f500c75e4c0,接下來的研究重點就是爲什麼這兩個對象會破壞?

2. 對象爲什麼損壞了

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


0:003> !do 083d8f50
Name:        System.Windows.DependencyProperty
MethodTable: 727ebe60
EEClass:     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
EEClass:     72721ec8
Size:        12(0xc) bytes
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
727e7f44  40007e9        0 ...geSortedObjectMap  0 instance 093d8f90 _mapStore

0:003> !DumpObj /d 093d8f90
<Note: this object has an invalid CLASS field>
Invalid object

根據對象的佈局知識,093d8f90 存放的是 mt,看樣子是mt被損壞了,接下來用 dp 觀察下這個地址的附近內存。


0:003> dp 093d8f90-0x20 L10
093d8f70  727e8b50 032e3750 1085da60 04a46fd9
093d8f80  00000000 727e8b50 032e3a48 08157ef0
093d8f90  03217272 00000000 727e8b50 032e353c
093d8fa0  08079f9c 02c028d2 00000000 727e8b50

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> !do 093d8f84
Name:        MS.Internal.WeakEventTable+EventKey
MethodTable: 727e8b50
EEClass:     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  00000003 00000000 727e72c9 083d8ba4
083d8f44  083d8ec8 775e7de3 00000000 727ebe61
083d8f54  083d8ba4 0ec9e934 083d8ec8 083d8f20
083d8f64  00000000 00000000 00000000 00020368
083d8f74  093d8f90 00000000 727ee329 083d8ec8
083d8f84  0ec9eaf4 000000e0 00000000 727e7f45
083d8f94  083d8fa4 00000001 000000e0 00000000
083d8fa4  727e7fb1 00000002 083d8f04 000000e0

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

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

4. 另一個壞對象也是如此嗎

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


0:003> !do 0c75e4c0
Name:        System.Windows.Media.Pen
MethodTable: 6f38a110
EEClass:     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翻轉大多是輻射導致計算機數字信號偶發的紊亂,這也是爲什麼醫院要加個鉛版來阻隔,而這個程序所處的環境剛好是輻射比較多的高鐵系。

所以分析完之後,非我等調試師能爲之,遠離輻射。。。

图片名称
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章