記一次 騰訊會議 的意外崩潰分析

一線碼農發表於2023-04-20

一:背景

1. 講故事

前段時間在用 騰訊會議 直播的時候,居然意外崩潰了,還好不是在訓練營上課,不然又得重錄了,崩完之後發現 騰訊會議 的 bugreport 元件會自動生成一個 minidump,截圖如下:

作為一個.NET高階除錯的技術博主,非 .NET 的程式也得要研究研究哈???,有了這個好奇心,也有了這個 dump,接下來用 windbg 看一看吧。

二:WinDbg 分析

1. 為什麼會崩潰

在 Windows 平臺上不管是硬體異常還是軟體異常 作業系統都會幫忙構造一個 EXCEPTION_POINTERS 結構體,這裡面就包含了程式的崩潰點,錯誤碼等各種非常有價值的資訊,要想洞察這個結構體,要麼在棧上提取,要麼用 !analyze -v 自動化提取,這裡採用後者。


0:000> !analyze -v

CONTEXT:  (.ecxr)
eax=008fdfe4 ebx=00000001 ecx=00000000 edx=2d692620 esi=3c4e1818 edi=3207f464
eip=1c5b34aa esp=008fe034 ebp=008fe094 iopl=0         nv up ei pl nz ac pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010216
meeting_dashboard_module+0x34aa:
1c5b34aa 8b01            mov     eax,dword ptr [ecx]  ds:002b:00000000=????????
Resetting default scope

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 1c5b34aa (meeting_dashboard_module+0x000034aa)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: 00000000
Attempt to read from address 00000000

PROCESS_NAME:  wemeetapp.exe

READ_ADDRESS:  00000000 

ERROR_CODE: (NTSTATUS) 0xc0000005 - 0x%p            0x%p                    %s

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  00000000

EXCEPTION_PARAMETER2:  00000000

STACK_TEXT:  
WARNING: Stack unwind information not available. Following frames may be wrong.
008fe094 7ad808f4     008fe320 00000000 ec0d8f9b meeting_dashboard_module+0x34aa
008fe214 7ad80617     008fe2f0 ec0d89ef 4ee246d8 desktop_common+0x108f4
008fe460 7ad7f4d7     776f6873 008fe400 79641fe1 desktop_common+0x10617
008fe5ec 7ad7ae62     008fe9d0 008fe76c 79bce43a desktop_common+0xf4d7
008fe5f8 79bce43a     008fe6b8 a0f1fbb4 326aed58 desktop_common+0xae62
008fe76c 7ad7de66     00000000 008fe9d0 ec0d8a63 wemeet_framework+0x2e43a
008fe7ec 7ad7deed     00000000 008fe9d0 008fe808 desktop_common+0xde66
008fe7fc 7ad7ae62     008fe9d0 008fe97c 79bce43a desktop_common+0xdee
...

從上面的 ExceptionCode: c0000005 來看,這是一個經典的訪問違例,從崩潰彙編的 mov eax,dword ptr [ecx] ds:002b:00000000=???????? 來看,當前的 ecx 中存放的是 0 ,從 0 上取內容自然就是訪問違例。

2. 為什麼會訪問違例

要想知道訪問違例的原因,就需要分析一下附近的彙編程式碼,用 .ecxr ; k 切到異常前的崩潰上下文。


0:000> .ecxr ; k
eax=008fdfe4 ebx=00000001 ecx=00000000 edx=2d692620 esi=3c4e1818 edi=3207f464
eip=1c5b34aa esp=008fe034 ebp=008fe094 iopl=0         nv up ei pl nz ac pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010216
meeting_dashboard_module+0x34aa:
1c5b34aa 8b01            mov     eax,dword ptr [ecx]  ds:002b:00000000=????????
  *** Stack trace for last set context - .thread/.cxr resets it
 # ChildEBP RetAddr      
WARNING: Stack unwind information not available. Following frames may be wrong.
00 008fe094 7ad808f4     meeting_dashboard_module+0x34aa
01 008fe214 7ad80617     desktop_common+0x108f4
02 008fe460 7ad7f4d7     desktop_common+0x10617
03 008fe5ec 7ad7ae62     desktop_common+0xf4d7
04 008fe5f8 79bce43a     desktop_common+0xae62
05 008fe76c 7ad7de66     wemeet_framework+0x2e43a
06 008fe7ec 7ad7deed     desktop_common+0xde66
07 008fe7fc 7ad7ae62     desktop_common+0xdeed
08 008fe808 79bce43a     desktop_common+0xae62
09 008fe97c 79bc784c     wemeet_framework+0x2e43a
0a 008fe98c 79bc821f     wemeet_framework+0x2784c
0b 008fe9a0 79bdac53     wemeet_framework+0x2821f
0c 008fea1c 79bdb791     wemeet_framework+0x3ac53
...

由於沒有這些 dll 的符號,windbg 為了定義程式碼行數,就只能用 module + 0xxxxx 作為偏移來定位。

現在我們知道 ecx=0,那為什麼會是 0 呢?接下來用 ub 觀察下彙編程式碼邏輯,截圖如下:


0:000> ub 1c5b34aa L20
meeting_dashboard_module+0x3449:
1c5b3449 00c6            add     dh,al
1c5b344b 45              inc     ebp
1c5b344c b000            mov     al,0
1c5b344e e8cde2ffff      call    meeting_dashboard_module+0x1720 (1c5b1720)
1c5b3453 8d45b0          lea     eax,[ebp-50h]
1c5b3456 c645fc04        mov     byte ptr [ebp-4],4
1c5b345a 50              push    eax
1c5b345b 8bce            mov     ecx,esi
1c5b345d ff15a8cb611c    call    dword ptr [meeting_dashboard_module+0x6cba8 (1c61cba8)]
1c5b3463 8d4db0          lea     ecx,[ebp-50h]
1c5b3466 8945c8          mov     dword ptr [ebp-38h],eax
1c5b3469 c645fc00        mov     byte ptr [ebp-4],0
1c5b346d e8feebffff      call    meeting_dashboard_module+0x2070 (1c5b2070)
1c5b3472 51              push    ecx
1c5b3473 8b4d0c          mov     ecx,dword ptr [ebp+0Ch]
1c5b3476 8bd4            mov     edx,esp
1c5b3478 c7450c00000000  mov     dword ptr [ebp+0Ch],0
1c5b347f 890a            mov     dword ptr [edx],ecx
1c5b3481 8bcf            mov     ecx,edi
1c5b3483 e8d8010000      call    meeting_dashboard_module+0x3660 (1c5b3660)
1c5b3488 837dc801        cmp     dword ptr [ebp-38h],1
1c5b348c 8b4f08          mov     ecx,dword ptr [edi+8]
1c5b348f 8b01            mov     eax,dword ptr [ecx]
1c5b3491 7509            jne     meeting_dashboard_module+0x349c (1c5b349c)
1c5b3493 6a01            push    1
1c5b3495 6a01            push    1
1c5b3497 ff507c          call    dword ptr [eax+7Ch]
1c5b349a eb2b            jmp     meeting_dashboard_module+0x34c7 (1c5b34c7)
1c5b349c ff5048          call    dword ptr [eax+48h]
1c5b349f 8bc8            mov     ecx,eax
1c5b34a1 ff15ccc6611c    call    dword ptr [meeting_dashboard_module+0x6c6cc (1c61c6cc)]
1c5b34a7 8b4f08          mov     ecx,dword ptr [edi+8]
1c5b34aa 8b01            mov     eax,dword ptr [ecx]
...

從彙編程式碼看,當前的 ecx 是來自於地址 edi+8,edi 的值有可能會在 meeting_dashboard_module+0x6c6cc (1c61c6cc) 方法中被修改,我們一併觀察下。


0:000> dp @edi+8 L2
3207f46c  ???????? ????????

0:000> u 1c61c6cc
meeting_dashboard_module+0x6c6cc:
1c61c6cc ??              ???
                ^ Memory access error in 'u 1c61c6cc'

我去,都是 ???,這表示當前的資料和機器指令都沒有納入到 dump 中,這也就是為什麼 dump 小的原因。

到這裡好像就沒法繼續分析了,天要絕人之路嗎?

3. 還有希望嗎

雖然被當頭一棒,但總得要掙扎一下吧,突破口也只能是彙編程式碼了,透過仔細觀察,由於倒數第五行是一個 jmp 指令,所以語句指令 1c5b349c 肯定是從別的地方飛躍過來的,翻譯成 C 程式碼就是一個 if else 的判斷,截圖如下:

既然走到了 else 的邏輯,那必然 ebp-38h 上的值肯定不是 1,那到底是多少呢?可以來查一查。

0:000> dp @ebp-38h L1
008fe05c  00000000

@ebp-38h 是誰給的呢?繼續觀察彙編程式碼,發現是 meeting_dashboard_module+0x6cba8 函式的返回值 eax 給的 ,從彙編邏輯看, 0 是一種異常狀態。

4. 為什麼會返回 0

返回 0 也暗示了程式碼在哪裡報了一些錯,可以用 GetLastError() 來獲取可能呼叫 win32api 出錯時設定的錯誤碼,用 !teb 觀察裡面的 LastErrorValue 值。


0:000> !teb
TEB at 0078a000
    ExceptionList:        008fcf94
    StackBase:            00900000
    StackLimit:           008ee000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 0078a000
    EnvironmentPointer:   00000000
    ClientId:             00003ccc . 00004484
    Real ClientId:        00000000 . 00000000
    RpcHandle:            00000000
    Tls Storage:          40a94180
    PEB Address:          00787000
    LastErrorValue:       18
    LastStatusValue:      0
    Count Owned Locks:    0
    HardErrorMode:        0

這裡的 18 是一個十進位制,十六進位制就是 0x12 ,那這個錯誤碼代表什麼意思呢? !error 已經不支援了,只能上 msdn 上找答案,截圖如下:

彙總一下:在騰訊會議錄製期間,可能是處理什麼檔案拋了一個 There are no more files. 錯誤,在錯誤處理的後續邏輯中拋了崩潰。

有了這個資訊之後,可以到外網搜一下 (https://windowsreport.com/there-are-no-more-files),常見的解決辦法如下:

  • Solution 1 — Remove folder lock
  • Solution 2 – Repair your registry
  • Solution 3 — Run a full system scan
  • Solution 4 — Update your OS
  • Solution 5 — Remove recently installed software
  • Solution 6 — Uninstall Comodo Cleaner/ ASUS security data manager
  • Solution 7 — Boot into Safe Mode

具體是什麼原因,由於缺少符號再深入分析下去得要花一些時間了,這裡就到此為止吧。

三:總結

崩潰的 dump 已經第一時間提交上去了,相信騰訊會議的研發團隊能夠很快解決,作為一個付費會員,真心希望在下次錄製的時候不要再崩了。

相關文章