博客 / 詳情

返回

記一次 .NET 某RFID標籤打印客户端 崩潰分析

一:背景

1. 講故事

去年微信上有位朋友找到我,説他們的RFID標籤打印出現了偶發性崩潰,一直沒找到原因,讓我幫忙看下怎麼回事?然後就讓這位朋友用procdump抓一個崩潰dump給我,我看看就好。

二:崩潰分析

1. 為什麼會崩潰

雙擊打開dump,windbg會自動定位到崩潰的上下文,這一點我比較喜歡,有的時候也省去了用 !analyze -v 無趣的等待,參考輸出如下:


This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(4120.43a0): Access violation - code c0000005 (first/second chance not available)
For analysis of this file, run !analyze -v
clr!WKS::gc_heap::find_first_object+0xea:
00007ffd`9eaa7ecb 833800          cmp     dword ptr [rax],0 ds:30302c30`2c302c30=????????

從卦中的 find_first_object 來看,這是經典的 gc標記階段,在進行深度優先遍歷的時候發現了無效對象,進而引發災難性後果,可以用 k 8 觀察調用棧。


0:006> k 8
 # Child-SP          RetAddr               Call Site
00 0000000e`c103c4e8 00007ffd`9eaa8955     clr!WKS::gc_heap::find_first_object+0xea
01 0000000e`c103c500 00007ffd`9ea298aa     clr!WKS::GCHeap::Promote+0xc7
02 0000000e`c103c570 00007ffd`9eaf2822     clr!GcEnumObject+0x97
03 0000000e`c103c5c0 00007ffd`9ea27f68     clr!GcInfoDecoder::EnumerateLiveSlots+0x1856
04 0000000e`c103ca20 00007ffd`9ea2887f     clr!GcStackCrawlCallBack+0x2bd
05 0000000e`c103ce40 00007ffd`9eaa25d8     clr!GCToEEInterface::GcScanRoots+0x4b6
06 0000000e`c103e300 00007ffd`9eaa0e55     clr!WKS::gc_heap::mark_phase+0x1d9
07 0000000e`c103e3b0 00007ffd`9eaa0d6b     clr!WKS::gc_heap::gc1+0xef

2. 崩潰原因是什麼

既然託管堆上有壞對象,那如何找到呢?可以用 !verifyheap 識別就好,參考輸出如下:


0:006> !verifyheap 
Could not request method table data for object 0000015A9D59B0D0 (MethodTable: 30302C302C302C30).
Last good object: 0000015A9D59B048.

從卦中可以清晰的看到,Object 0000015A9D59B0D0的 MethodTable 30302C302C302C30 是一個無效值,從形態上看很像一段字符的ascii碼,有點意思,接下來我們觀察對象附近的內存,使用 dp 0000015A9D59B0D0-0xa0 L30 命令觀察。


0:006> dp 0000015A9D59B0D0-0xa0 L30
0000015a`9d59b030  00000000`00000002 00000000`003a002f
0000015a`9d59b040  00000000`00000000 00007ffd`9cd985e0
0000015a`9d59b050  00000000`0000001c 00000002`00000001
0000015a`9d59b060  00000000`00000008 00000000`00000000
0000015a`9d59b070  00000000`00000000 00000000`00000000
0000015a`9d59b080  00000000`00000000 00000000`00000000
0000015a`9d59b090  00000000`00000000 00000000`00000000
0000015a`9d59b0a0  00000000`00000000 65636976`6564227b
0000015a`9d59b0b0  74735f74`736f682e 30223a22`73757461
0000015a`9d59b0c0  312c302c`302c3033 2c383330`2c383132
0000015a`9d59b0d0  30302c30`2c302c30 5c302c30`2c302c30
0000015a`9d59b0e0  302c3130`306e5c72 322c312c`302c302c
0000015a`9d59b0f0  3030302c`302c362c 2c312c31`30303030
0000015a`9d59b100  306e5c72`5c313030 007d2230`2c303030
0000015a`9d59b110  0000002e`00000001 00000000`00000000
0000015a`9d59b120  00000000`00000000 00007ffd`9cd95a68
0000015a`9d59b130  0073006d`00000005 00000073`006e0074
0000015a`9d59b140  00000000`00000000 00000000`00000000
0000015a`9d59b150  0000015a`9b4ddbb0 00000000`00000000
0000015a`9d59b160  00000000`00000000 00007ffd`9cd95a68
0000015a`9d59b170  00720050`00000013 00650074`006e0069
0000015a`9d59b180  00700061`00430072 006c0069`00620061
0000015a`9d59b190  00650069`00740069 00000000`00000073
0000015a`9d59b1a0  00000000`00000000 00007ffd`9cd96878

從卦中可以看到 0000015A9D59B0D0 附近被一段字符串覆蓋了,看樣子是有域外代碼將string寫溢出了。。。。接下來使用 da 把這段內容給 dig 出來。


0:006> da /c100 0000015a`9d59b0a0+0x8
0000015a`9d59b0a8  "{"device.host_status":"030,0,0,1218,038,0,0,0,000,0,0,0\r\n001,0,0,0,1,2,6,0,00000001,1,001\r\n0000,0"}"

從卦中看是一段json字符串,看樣子應該是非託管代碼回寫string溢出了,但這個對象生前是不是string呢?這個只能在當前破壞現場尋找了,使用 !lno 觀察附近的好對象。


0:006> !lno 0000015a9d59b128
Before:  0000015a9d59b048          136 (0x88)	System.Int32[]
Current: 0000015a9d59b128           40 (0x28)	System.String
After:   0000015a9d59b150           24 (0x18)	Free
Heap local consistency not confirmed.

0:006> !lno 0000015a9d59b150
Before:  0000015a9d59b048          136 (0x88)	System.Int32[]
Current: 0000015a9d59b150           24 (0x18)	Free
After:   0000015a9d59b168           64 (0x40)	System.String
Heap local consistency not confirmed.

0:006> !mdt -e:2 0000015a9d59b048
0000015a9d59b048 (System.Int32[], Elements: 28)
[0] 0x1
....
[16] 0x0
[17] 0x0
[18] 0x0
[19] 0x0
[20] 0x6564227b
[21] 0x65636976
[22] 0x736f682e
[23] 0x74735f74
[24] 0x73757461
[25] 0x30223a22
[26] 0x302c3033
[27] 0x312c302c

0:006> !do 0000015a9d59b168
Name:        System.String
MethodTable: 00007ffd9cd95a68
EEClass:     00007ffd9cd72ec0
Size:        64(0x40) bytes
File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      PrinterCapabilities
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ffd9cd98648  4000283        8         System.Int32  1 instance               19 m_stringLength
00007ffd9cd968e0  4000284        c          System.Char  1 instance               50 m_firstChar
00007ffd9cd95a68  4000288       e0        System.String  0   shared           static Empty
                                 >> Domain:Value  0000015a9b506c70:NotInit  <<

0:006> !do 0000015a9d59b128
Name:        System.String
MethodTable: 00007ffd9cd95a68
EEClass:     00007ffd9cd72ec0
Size:        36(0x24) bytes
File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      mstns
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ffd9cd98648  4000283        8         System.Int32  1 instance                5 m_stringLength
00007ffd9cd968e0  4000284        c          System.Char  1 instance               6d m_firstChar
00007ffd9cd95a68  4000288       e0        System.String  0   shared           static Empty
                                 >> Domain:Value  0000015a9b506c70:NotInit  <<

從卦中可以看到,這個 json 把 前面的 int[28] 也給部分破壞了,後面跟着字符串 PrinterCapabilitiesmstns,看樣子這塊和打印操作有關,將這些信息告訴朋友,讓朋友重點關注下。

由於當前看到的是第二現場,無法知道誰導致的第一現場,如果想知道,需要上各種黑科技,這個在我之前的文章中多有涉及。

三:總結

這次生產事故還是挺有意思,比較考驗你對託管堆以及對內存的敏感度。

圖片名稱
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.