記一次 .NET 某醫療器械 程序崩潰分析

一:背景

1.講故事

前段時間有位朋友在微信上找到我,說他的程序偶發性崩潰,讓我幫忙看下怎么回事,上面給的壓力比較大,對于這種偶發性崩潰,比較好的辦法就是利用 AEDebug 在程序崩潰的時候自動抽一管血出來,看看崩潰點是什么,其實我的系列文章中,關于崩潰類的dump比較少,剛好補一篇上來,話不多說,上 windbg 。

二:WinDbg 分析

1. 崩潰點在哪里

在 windbg 中有一個 !analyze -v 命令可以自動化分析,輸出信息如下:

0:120>?!analyze?-v
*******************************************************************************
*?????????????????????????????????????????????????????????????????????????????*
*????????????????????????Exception?Analysis???????????????????????????????????*
*?????????????????????????????????????????????????????????????????????????????*
*******************************************************************************
CONTEXT:??(.ecxr)
rax=00000000032fed38?rbx=00000000c0000374?rcx=0000000000000000
rdx=0000000000000020?rsi=0000000000000001?rdi=00007ffbada727f0
rip=00007ffbada0a8f9?rsp=000000003103c8b0?rbp=0000000000c40000r8=00007ffb779bdab7??r9=00007ffb782e94c0?r10=0000000000002000
r11=000000002c4aa498?r12=0000000000000000?r13=000000003103eb60
r14=0000000000000000?r15=000000002c873720
iopl=0?????????nv?up?ei?pl?nz?na?pe?nc
cs=0033??ss=002b??ds=002b??es=002b??fs=0053??gs=002b?????????????efl=00000202
ntdll!RtlReportFatalFailure+0x9:
00007ffb`ada0a8f9?eb00????????????jmp?????ntdll!RtlReportFatalFailure+0xb?(00007ffb`ada0a8fb)
Resetting?default?scopeEXCEPTION_RECORD:??(.exr?-1)
ExceptionAddress:?00007ffbada0a8f9?(ntdll!RtlReportFatalFailure+0x0000000000000009)ExceptionCode:?c0000374ExceptionFlags:?00000001
NumberParameters:?1Parameter[0]:?00007ffbada727f0
...

從卦中的 ExceptionCode: c0000374 異常碼來看,表示當前 nt堆損壞,這就尷尬了,一個C#程序咋會把 windows nt 堆給弄壞了,可能是引入了第三方的 C++ 代碼。

由于異常分異常前和異常后,所以需要用 .ecxr 將當前線程切到異常前的崩潰點,然后使用 k 觀察當前的線程棧。

0:120>?.ecxr?;?k
rax=00000000032fed38?rbx=00000000c0000374?rcx=0000000000000000
rdx=0000000000000020?rsi=0000000000000001?rdi=00007ffbada727f0
rip=00007ffbada0a8f9?rsp=000000003103c8b0?rbp=0000000000c40000r8=00007ffb779bdab7??r9=00007ffb782e94c0?r10=0000000000002000
r11=000000002c4aa498?r12=0000000000000000?r13=000000003103eb60
r14=0000000000000000?r15=000000002c873720
iopl=0?????????nv?up?ei?pl?nz?na?pe?nc
cs=0033??ss=002b??ds=002b??es=002b??fs=0053??gs=002b?????????????efl=00000202
ntdll!RtlReportFatalFailure+0x9:
00007ffb`ada0a8f9?eb00????????????jmp?????ntdll!RtlReportFatalFailure+0xb?(00007ffb`ada0a8fb)***?Stack?trace?for?last?set?context?-?.thread/.cxr?resets?it#?Child-SP??????????RetAddr???????????????Call?Site
00?00000000`3103c8b0?00007ffb`ada0a8c3?????ntdll!RtlReportFatalFailure+0x9
01?00000000`3103c900?00007ffb`ada1314e?????ntdll!RtlReportCriticalFailure+0x97
02?00000000`3103c9f0?00007ffb`ada1345a?????ntdll!RtlpHeapHandleError+0x12
03?00000000`3103ca20?00007ffb`ad9aef41?????ntdll!RtlpHpHeapHandleError+0x7a
04?00000000`3103ca50?00007ffb`ad9be520?????ntdll!RtlpLogHeapFailure+0x45
05?00000000`3103ca80?00007ffb`aa3882bf?????ntdll!RtlFreeHeap+0x966e0
06?00000000`3103cb20?00007ffb`66fac78f?????KERNELBASE!LocalFree+0x2f
07?00000000`3103cb60?00007ffb`66f273a4?????mscorlib_ni+0x63c78f
08?00000000`3103cc10?00007ffb`185c4fde?????mscorlib_ni!System.Runtime.InteropServices.Marshal.FreeHGlobal+0x24?[f:\dd\ndp\clr\src\BCL\system\runtime\interopservices\marshal.cs?@?1212]?
09?00000000`3103cc50?00007ffb`185c4fa1?????0x00007ffb`185c4fde
0a?00000000`3103cca0?00007ffb`185edc82?????0x00007ffb`185c4fa1
...

從卦中的 KERNELBASE!LocalFree 方法可知,程序正在釋放一個 堆塊,在釋放的過程中拋出了異常,那為什么會釋放失敗呢?原因就比較多了,比如:

  • 原因1:Free 一個已 Free 的堆塊

  • 原因2:Free 了一個別人的堆塊

那到底是哪一種情況呢?有經驗的朋友應該知道,ntheap 默認開啟了 損壞退出 機制,用 !heap -s 命令就能顯示出這種損壞原因。

0:120>?!heap?-s************************************************************************************************************************NT?HEAP?STATS?BELOW
************************************************************************************************************************
**************************************************************
*????????????????????????????????????????????????????????????*
*??????????????????HEAP?ERROR?DETECTED???????????????????????*
*????????????????????????????????????????????????????????????*
**************************************************************Details:Heap?address:??0000000000c40000
Error?address:?000000002c873710
Error?type:?HEAP_FAILURE_BLOCK_NOT_BUSY
Details:????The?caller?performed?an?operation?(such?as?a?freeor?a?size?check)?that?is?illegal?on?a?free?block.
Follow-up:??Check?the?error's?stack?trace?to?find?the?culprit.Stack?trace:
Stack?trace?at?0x00007ffbada7284800007ffbad9aef41:?ntdll!RtlpLogHeapFailure+0x4500007ffbad9be520:?ntdll!RtlFreeHeap+0x966e000007ffbaa3882bf:?KERNELBASE!LocalFree+0x2f00007ffb66fac78f:?mscorlib_ni+0x63c78f00007ffb66f273a4:?mscorlib_ni!System.Runtime.InteropServices.Marshal.FreeHGlobal+0x2400007ffb185c4fde:?+0x185c4fdeLFH?Key???????????????????:?0x1d4fd2a71d8b8280
Termination?on?corruption?:?ENABLEDHeap?????Flags???Reserv??Commit??Virt???Free??List???UCR??Virt??Lock??Fast?(k)?????(k)????(k)?????(k)?length??????blocks?cont.?heap?
-------------------------------------------------------------------------------------
0000000000c40000?00000002???16756??13688??16364????220???140?????5????2??????0???LFH
...

從卦中可以清晰的看到錯誤類型:Error type: HEAP_FAILURE_BLOCK_NOT_BUSY ,這是經典的 Double Free,也就是上面的 原因1 ,接下來我們就要尋找代碼源頭了。。。

2. 是誰的代碼引發的

從線程棧上看,底層的方法區都是十六進制,這表示當前是托管方法,這就好辦了,我們用 !clrstack 看看托管代碼是什么?

0:120>?!clrstack?
OS?Thread?Id:?0x4d54?(120)Child?SP???????????????IP?Call?Site
000000003103cb88?00007ffbad9b0544?[InlinedCallFrame:?000000003103cb88]?Microsoft.Win32.Win32Native.LocalFree(IntPtr)
000000003103cb88?00007ffb66fac78f?[InlinedCallFrame:?000000003103cb88]?Microsoft.Win32.Win32Native.LocalFree(IntPtr)
000000003103cb60?00007ffb66fac78f?DomainNeutralILStubClass.IL_STUB_PInvoke(IntPtr)
000000003103cc10?00007ffb66f273a4?System.Runtime.InteropServices.Marshal.FreeHGlobal(IntPtr)?[f:\dd\ndp\clr\src\BCL\system\runtime\interopservices\marshal.cs?@?1212]
000000003103cc50?00007ffb185c4fde?xxxx.StructToBytes(System.Object)
000000003103ced0?00007ffb185ec6b1?xxx.SendDoseProject(System.String)
...

從卦中可以清晰的看到是托管方法 StructToBytes() 引發的,接下來導出這個方法的源碼,截圖如下:

d5d3e43da2f96c5bb81fcbca95062690.png

從方法邏輯看,這位朋友用了 Marshal 做了互操作,為了能夠進一步分析,需要找到 localResource 堆塊句柄,使用 !clrstack -l 顯示方法棧參數。

0:120>?!clrstack?-l
OS?Thread?Id:?0x4d54?(120)
...
000000003103cca0?00007ffb185c4fa1?xxx.StructToBytes(System.Object)LOCALS:0x000000003103cd0c?=?0x000000000000018f0x000000003103ccf8?=?0x00000000030844200x000000003103ccf0?=?0x00000000030844200x000000003103cce8?=?0x00000000000000000x000000003103cce0?=?0x0000000000000000
...

經過對比,發現并沒有顯示 localResource 值,這就很尷尬了。。。一般在 dump 中 IntPtr 類型是顯示不出來的,遇到好幾次了,比較鬧心。。。既然顯示不出來堆塊句柄值。。。那怎么辦呢?天要絕人之路嗎?

3. 絕處逢生

既然托管層找不到堆塊句柄,那就到非托管層去找,比如這里的 KERNELBASE!LocalFree+0x2f 函數,msdn 上的定義如下:

HLOCAL?LocalFree([in]?_Frees_ptr_opt_?HLOCAL?hMem
);

那如何找到這個 hMem 值呢?在 x86 程序中可以直接用 kb 就能提取出來,但在 x64 下是無效的,因為它是用寄存器來傳遞方法參數,此時的寄存器值已經刷新到了 ntdll!NtWaitForMultipleObjects+0x14 上,比如下面的 rcx 肯定不是 hMem 值。

0:120>?r
rax=000000000000005b?rbx=0000000000005b08?rcx=0000000000000002
rdx=000000003103b690?rsi=0000000000000002?rdi=0000000000000000
rip=00007ffbad9b0544?rsp=000000003103b658?rbp=0000000000001da4r8=0000000000001000??r9=0101010101010101?r10=0000000000000000
r11=0000000000000246?r12=0000000000000000?r13=000000003103c930
r14=0000000000001f98?r15=0000000000000000
iopl=0?????????nv?up?ei?pl?zr?na?po?nc
cs=0033??ss=002b??ds=002b??es=002b??fs=0053??gs=002b?????????????efl=00000246
ntdll!NtWaitForMultipleObjects+0x14:
00007ffb`ad9b0544?c3??????????????ret

怎么辦呢?其實還有一條路,就是觀察 KERNELBASE!LocalFree+0x2f 方法的匯編代碼,看看它有沒有將 rcx 臨時性的存到 線程棧 上。

0:120>?u?KERNELBASE!LocalFree
KERNELBASE!LocalFree:
00007ffb`aa388290?48895c2410??????mov?????qword?ptr?[rsp+10h],rbx
00007ffb`aa388295?4889742418??????mov?????qword?ptr?[rsp+18h],rsi
00007ffb`aa38829a?48894c2408??????mov?????qword?ptr?[rsp+8],rcx
00007ffb`aa38829f?57??????????????push????rdi
00007ffb`aa3882a0?4883ec30????????sub?????rsp,30h
00007ffb`aa3882a4?488bd9??????????mov?????rbx,rcx
00007ffb`aa3882a7?f6c308??????????test????bl,8
00007ffb`aa3882aa?753f????????????jne?????KERNELBASE!LocalFree+0x5b?(00007ffb`aa3882eb)

很開心的看到,當前的 rcx 存到了 rsp+8 位置上,那如何拿到 rsp 呢?可以用 k 提取父函數 mscorlib_ni+0x63c78f 中的 Child-SP 值。

0:120>?k#?Child-SP??????????RetAddr???????????????Call?Site...
0e?00000000`3103ca80?00007ffb`aa3882bf?????ntdll!RtlFreeHeap+0x966e0
0f?00000000`3103cb20?00007ffb`66fac78f?????KERNELBASE!LocalFree+0x2f
10?00000000`3103cb60?00007ffb`66f273a4?????mscorlib_ni+0x63c78f
...

因為這個 Child-SP 是 call 之前的 sp, 匯編中的 sp 是 call 之后的,所以相差一個 retaddr 指針單元,所以計算方法是:ChildSp- 0x8 + 0x8 就是 堆塊句柄。

0:120>?dp?00000000`3103cb60-0x8+0x8?L1
00000000`3103cb60??00000000`2c873720

上面的 000000002c873720 就是堆塊句柄,接下來用命令 !heap -x 000000002c873720 觀察堆塊情況。

0:120>?!heap?-x?000000002c873720
Entry?????????????User??????????????Heap??????????????Segment???????????????Size??PrevSize??Unused????Flags
-------------------------------------------------------------------------------------------------------------
000000002c873710??000000002c873720??0000000000c40000??000000002c8703c0????????30??????-????????????0??LFH;free

果不其然,這個堆塊已經是 Free 狀態了,再 Free 必然會報錯,經典的 Double Free 哈。

4. 回首再看源碼

仔細閱讀源碼,發現有兩個問題。

  1. 沒有對 localResource 加鎖處理,在并發的時候容易出現問題。

  2. localResource 是一個類級別變量,在多個方法中被使用。

將信息反饋給朋友之后,建議朋友加鎖并降低 localResource 作用域。

三:總結

這次偶發的生產崩潰事故,主要原因是朋友的代碼在邏輯上出了點問題,沒有合理的保護好 localResource 句柄資源,反復釋放導致的 ntheap 破壞。

這個 dump 雖然問題比較小白,但逆向分析找出原因,還是挺考驗基本功的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281565.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281565.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281565.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

1251: 字母圖形 [水題]

1251: 字母圖形 [水題] 時間限制: 1 Sec 內存限制: 128 MB提交: 140 解決: 61 統計題目描述 利用字母可以組成一些美麗的圖形,下面給出了一個例子: ABCDEFG BABCDEF CBABCDE DCBABCD EDCBABC 這是一個5行7列的圖形,請找出這個圖形的規律&…

c語言 三角形三邊abc,C語言代碼輸入abc三個數,求一這3個數為邊長的三角形面積...

2011-01-04 回答#include #include #include #include #include int main(){float a 0.0;float b 0.0;float c 0.0;float s 0.0;double area 0.0;while(true){printf("input your date(a,b,c):");scanf("%f%f%f",&a,&b,&c);if(!isdigit((…

shell腳本中向hive動態分區插入數據

在hive上建表與普通分區表創建方法一樣; 1 CREATE TABLE dwa_m_user_association_circle(2 device_number string, 3 oppo_number string, 4 prov_id_oppo string, 5 area_id_oppo string, 6 dealer_oppo string, 7 short_call_nums bigint, 8 long3…

WPF效果第二百零二篇之TreeView帶連接線

前面文章中分享了TreeView支持多選;然而在項目上使用時,領導覺得不滿意:體現不了真正的從屬關系;既然領導都發話了;那就開整就行了;今天就再來個帶有連接線的TreeView效果:1、來看看TreeViewItem的Template:2、展開和收縮動畫:3、參考資料https://www.codeproject.com/tips/673…

ObjectTive C語言語法,[譯]理解 Objective-C 運行時(下篇)

本文來自網易云社區作者:宋申易所以到底 objc_msgSend 發生了什么?很多事情。看一下這段代碼:[self printMessageWithString:"Hello World!"];這實際上被編譯器翻譯成:objc_msgSend(self, selector(printMessageWithStr…

菜鳥學習MVC實錄:弄清項目各類庫的作用和用法

MVC模式即:模型(Model)-視圖(View)-控制器(Controller) Model (模型):是應用程序中用于處理應用程序數據邏輯的部分。通常模型對象負責數據庫中存取數據View…

SSL服務器

2019獨角獸企業重金招聘Python工程師標準>>> SSL 是一個安全協議,它提供使用 TCP/IP 的通信應用程序間的隱私與完整性。因特網的 超文本傳輸協議(HTTP)使用 SSL 來實現安全的通信。 在客戶端與服務器間傳輸的數據是通過使用對稱算…

微軟Skype Translator將支持阿拉伯語即時語音翻譯

據美國科技時代網(Tech Times)3月9日報道,日前,微軟旗下即時翻譯軟件Skype Translator再添新語種,微軟宣布Skype Translator已經支持阿拉伯語。Skype用戶可通過使用阿拉伯語即時翻譯與朋友、家人以及海外商業伙伴進行交流。 據報道&#xff0…

是什么讓.NET7的Min和Max方法性能暴增了45倍?

簡介在之前的一篇文章.NET性能系列文章一:.NET7的性能改進中我們聊到Linq中的Min()和Max()方法.NET7比.NET6有高達45倍的性能提升,當時Benchmark代碼和結果如下所示:[Params(1000)] public int Length { get; set; }private int[] arr;[Globa…

html標記語言 --框架

html標記語言 --框架六、框架1、什么是框架 框架將瀏覽器劃分成不同的部分&#xff0c;每一部分加載不同的網頁 實現同一瀏覽器窗口中加載多個頁面的效果。 語法格式<frameset>.......</frameset>2. 屬性2.1 cols使用“像素數”和%分割左右窗口&#xff0c;“*” 表…

c語言兔子洞,數據結構水題選講 - osc_y08db3kb的個人空間 - OSCHINA - 中文開源技術交流社區...

[Ynoi2011]ODT\(O(nlog^2n)\) 的做法非常顯然直接把樹重鏈剖分一下&#xff0c;每個點維護輕兒子的平衡樹就行但是這題 \(1e6\) 的數據范圍使得 \(O(nlog^2n)\) 沒那么容易卡過去(當然很多人卡過去了考慮給一個點很多重兒子那么若一個點有 \(k\) 個重兒子&#xff0c;修改復雜度…

centos 7.x systemd service 配置方法整理

一、存放路徑/etc/systemd/system二、service配置整理2.1 zookeeper.service[Unit]DescriptionZooKeeper ServiceAftersyslog.targetAfternetwork.target[Service]#使用shell腳本啟動的要用forking模式TypeforkingUserzookeeperGroupzookeeper#腳本啟動ExecStart/usr/local/zoo…

MAVEN集成測試環境搭建

1. MAVEN SVN HUDSON SONAR集成測試環境搭建、1.1 軟件準備 Hudson、Jenkins、Sonar1.2 軟件安裝 說明&#xff1a;本例均使用將應用程序部署至web容器下&#xff0c;Hudson和Sonar有其他部署啟動方式&#xff0c;如有需要請自行使用&#xff0c;本文不做贅述。1.2.1 安裝hu…

ubus c語言例子,openwrt之ubus例子

好一個icrootLEDE:/# ubus call test_ubus helloworld {"id":1,"msg":"hi","array":["a","b"]}{"id": 1,"msg": "hi","shuzu": ["a","b"]}文件目…

使用Spring訪問Mongodb的方法大全——Spring Data MongoDB查詢指南

1.概述 Spring Data MongoDB 是Spring框架訪問mongodb的神器&#xff0c;借助它可以非常方便的讀寫mongo庫。本文介紹使用Spring Data MongoDB來訪問mongodb數據庫的幾種方法&#xff1a; 使用Query和Criteria類JPA自動生成的查詢方法使用Query 注解基于JSON查詢在開始前&#…

mysqldump導出備份數據庫報Table ‘performance_schema.session_variables‘ doesn‘t exist

今天在bash進行本地數據庫往云端數據庫導數據的時候&#xff0c;在本地導出.sql文件這第一步就出現了錯誤問題&#xff0c;導出sql文件的命令&#xff1a; 1 mysqldump -u 用戶名 -p 數據庫名 > xxx.sql 在做這一步將數據導出的時候報了這么一個錯誤&#xff0c; 1 mysqldu…

在Identity框架中使用RoleBasedAuthorization

本文將介紹在 Identity 框架中如何使用 Sang.AspNetCore.RoleBasedAuthorization[1] 庫。核心介紹Identity 和 jwt 的基本配置我們在這里不再贅述&#xff0c;可以參考最后的項目樣例。核心的代碼主要為 IRolePermission 的實現。internal class MyRolePermission : IRolePermi…

2016年印度公有云服務市場將達13億美元

根據IT咨詢公司Gartner最新調查數據顯示&#xff0c;2016年印度公有云服務市場預計將增長35.9%&#xff0c;達到13億美元。 增長最快的是云系統基礎設施即服務&#xff08;IaaS&#xff09;&#xff0c;2016年預計將增長45.5%&#xff1b;其次是平臺即服務&#xff08;PaaS&…

PAT 1042. 字符統計

1042. 字符統計 請編寫程序&#xff0c;找出一段給定文字中出現最頻繁的那個英文字母。 輸入格式&#xff1a; 輸入在一行中給出一個長度不超過1000的字符串。字符串由ASCII碼表中任意可見字符及空格組成&#xff0c;至少包含1個英文字母&#xff0c;以回車結束&#xff08;回車…

Magicodes.IE 2.7.0-beta發布

2.7.0-beta2022.10.27使用SixLabors.ImageSharp替代System.Drawing&#xff0c;感謝linch90 &#xff08;見pr#454&#xff09;2.6.92022.10.26fix: 動態數據源導出到多個sheet的問題 &#xff08;見#449&#xff09;2.6.82022.10.18Excel模板導出添加API&#xff0c;以支持通過…