记一次 .NET某新能源MES系统 非托管泄露

一次,net,新能源,mes,系统,托管,泄露 · 浏览次数 : 289

小编点评

**1. 代码分析** 分析代码可发现代码是动态生成程序集导致的程序集泄露,猜测代码是在哪里 copy 过来的。 **2. 解决方案** * 确定代码在哪里 copy 过来的。 * 找出代码中使用的所有 dll。 * 逐个分析每个 dll 是否包含相关代码。 * 如果发现代码包含相关代码,则分析其作用并进行修改。 * 如果无法确定代码来源,则建议不要使用该代码。 * 使用其他方法,如反分析或反代码分析工具等,来分析代码。 **3. 总结** * 代码中可能存在代码在哪里 copy 过来的问题。 * 找到所有相关代码并进行分析。 * 逐个分析每个代码,并进行修改或删除。 * 使用其他方法来分析代码,如反分析或反代码分析工具等。

正文

一:背景

1. 讲故事

前些天有位朋友找到我,说他们的程序有内存泄露,跟着我的错题集也没找出是什么原因,刚好手头上有一个 7G+ 的 dump,让我帮忙看下是怎么回事,既然找到我了那就给他看看吧,不过他的微信头像有点像 二道贩子,不管到我这里是不是 三道,该分析的还得要分析呀。😄😄😄

二:WinDbg 分析

1. 到底是哪里的泄露

这个非常简单,对用户态内存段做一个分组处理就明白了,可以用 windbg 的 !address -summary 观察便知。


0:000> !address -summary
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free                                  44571     7ffc`f0900000 ( 127.988 TB)           99.99%
<unknown>                             77531        2`47cee000 (   9.122 GB)  74.52%    0.01%
Heap                                  16406        0`a45cf000 (   2.568 GB)  20.98%    0.00%
Image                                  2116        0`15ad7000 ( 346.840 MB)   2.77%    0.00%
Stack                                  2286        0`0d160000 ( 209.375 MB)   1.67%    0.00%
TEB                                     762        0`005f4000 (   5.953 MB)   0.05%    0.00%
Other                                    39        0`00207000 (   2.027 MB)   0.02%    0.00%
PEB                                       1        0`00001000 (   4.000 kB)   0.00%    0.00%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                              44571     7ffc`f0900000 ( 127.988 TB)           99.99%
MEM_COMMIT                            86575        1`e571e000 (   7.585 GB)  61.96%    0.01%
MEM_RESERVE                           12566        1`29fd2000 (   4.656 GB)  38.04%    0.00%

从卦中可以清晰的看到 MEM_COMMIT=7.5G 同时 Heap=2.5G,说明可能是托管内存泄露,接下来用 !eeheap -gc 观察下托管堆内存。


0:000> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000020F1BC03E80
generation 1 starts at 0x0000020F1AFE7BA0
generation 2 starts at 0x0000020D2E4B1000
ephemeral segment allocation context: none
Small object heap
         segment             begin         allocated         committed    allocated size    committed size
0000020D2E4B0000  0000020D2E4B1000  0000020D3E4B0000  0000020D3E4B0000  0xffff000(268431360)  0xffff000(268431360)
0000020D09830000  0000020D09831000  0000020D1982FFE0  0000020D19830000  0xfffefe0(268431328)  0xffff000(268431360)
0000020D9D9E0000  0000020D9D9E1000  0000020DAD9DFFB8  0000020DAD9E0000  0xfffefb8(268431288)  0xffff000(268431360)
0000020DD50C0000  0000020DD50C1000  0000020DE50BFFB0  0000020DE50C0000  0xfffefb0(268431280)  0xffff000(268431360)
0000020E10B90000  0000020E10B91000  0000020E20B8FF10  0000020E20B90000  0xfffef10(268431120)  0xffff000(268431360)
0000020E54C60000  0000020E54C61000  0000020E64C60000  0000020E64C60000  0xffff000(268431360)  0xffff000(268431360)
0000020E9C050000  0000020E9C051000  0000020EAC04FF70  0000020EAC050000  0xfffef70(268431216)  0xffff000(268431360)
0000020ED89B0000  0000020ED89B1000  0000020EE89AFF20  0000020EE89B0000  0xfffef20(268431136)  0xffff000(268431360)
0000020F11FF0000  0000020F11FF1000  0000020F1C20E0A8  0000020F1C21A000  0xa21d0a8(169988264)  0xa229000(170037248)
Frozen object heap
         segment             begin         allocated         committed    allocated size    committed size
Large object heap starts at 0x0000020D3E4B1000
         segment             begin         allocated         committed    allocated size    committed size
0000020D3E4B0000  0000020D3E4B1000  0000020D460B3F00  0000020D460D4000  0x7c02f00(130035456)  0x7c23000(130166784)
0000020D72620000  0000020D72621000  0000020D7A19C808  0000020D7A19D000  0x7b7b808(129480712)  0x7b7c000(129482752)
0000020D7CFD0000  0000020D7CFD1000  0000020D84B0CDF0  0000020D84B2D000  0x7b3bdf0(129220080)  0x7b5c000(129351680)
0000020E08B90000  0000020E08B91000  0000020E0FAC4350  0000020E0FAC5000  0x6f33350(116601680)  0x6f34000(116604928)
0000020F2A040000  0000020F2A041000  0000020F2DB4A738  0000020F2DB4B000  0x3b09738(61904696)  0x3b0a000(61906944)
Total Allocated Size:              Size: 0xabf0bd10 (2884680976) bytes.
Total Committed Size:              Size: 0xabf5a000 (2885001216) bytes.
------------------------------
GC Allocated Heap Size:    Size: 0xabf0bd10 (2884680976) bytes.
GC Committed Heap Size:    Size: 0xabf5a000 (2885001216) bytes.


从卦中数据看当前托管堆也才 2.8G,这就比较懵逼了,看样子还是比较难搞的 非托管内存泄露,内存大概被 heap + VirtualAlloc/FileMap 合力吃掉了,接下来该怎么分析呢?有点迷茫。。。

2. 还有其他突破口吗

一般来说这种问题看dump效果已经很不好了,比较好的方式就是用 perfview 去监控 VirtualAlloc,HeapAlloc 分配,但现在可惜的是我们只有一个dump,怎么办呢? Windbg 命令除了可以看 ntheap,还可以看 clr 的私有堆,即 loader堆,或许有什么新的发现,可以使用命令 !eeheap -loader


0:000> !dumpdomain
--------------------------------------
System Domain:      00007fffeb742af0
...
--------------------------------------
Domain 1:           0000020d2c794430
Assembly:           0000020d59298350 [mr4vbdbg, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]
ClassLoader:        0000020D579778E0
SecurityDescriptor: 0000020D593DE320
  Module
  00007fff8f0a5af8    mr4vbdbg, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

Assembly:           0000020d5751b040 [51buoqnx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]
ClassLoader:        0000020D57974130
SecurityDescriptor: 0000020D593E0060
  Module
  00007fff8f059798    51buoqnx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
...
Assembly:           0000020d5751c000 [r2bjpfrk, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]
ClassLoader:        0000020D579741E0
SecurityDescriptor: 0000020D593DF340
  Module
  00007fff8f05aff8    r2bjpfrk, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
...
Assembly:           0000020d5929acf0 [qgt1j2cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]
ClassLoader:        0000020D57976B20
SecurityDescriptor: 0000020D593DD6F0
  Module
  00007fff8f0a11d8    qgt1j2cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
...
Total size:      Size: 0x0 (0) bytes.
--------------------------------------
Total LoaderHeap size:   Size: 0x50f6d000 (1358352384) bytes total, 0x2f43000 (49557504) bytes wasted.
=======================================


我去,不看不知道,一看吓一跳,刷了好久也没刷完,,,看了下总大小是 1.35G,说明当前程序存在着程序集泄露,而且程序集的名字也是奇奇怪怪的比如上面的 r2bjpfrk , qgt1j2cs, 看样子都是动态生成出来的。

这里要提醒一下的是,不要看这里面是 1.35G,它还会涉及到其他未被关联到的内存,比如 VirtualAlloc/MapFile/GCHeap 等等

接下来抽几个 module 看看里面都定义了什么,使用 !dumpmodule -mt 命令即可。


0:000> !dumpmodule -mt 00007fff8f0a11d8
Name: qgt1j2cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
Attributes:              PEFile 
Types defined in this module

              MT          TypeDef Name
------------------------------------------------------------------------------
00007fff8f0a1938 0x02000002 ServiceBase.WebService.DynamicWebLoad.xxxImplService

Types referenced in this module

              MT            TypeRef Name
------------------------------------------------------------------------------
00007fffc3232730 0x02000001 System.Web.Services.Protocols.SoapHttpClientProtocol
00007fffe81789e0 0x02000002 System.IAsyncResult
00007fffe81759d8 0x02000003 System.AsyncCallback
00007fffe15f42f8 0x02000004 System.Xml.Serialization.XmlElementAttribute
00007fffe57810a8 0x02000007 System.CodeDom.Compiler.GeneratedCodeAttribute
00007fffe80ee5f8 0x02000008 System.Diagnostics.DebuggerStepThroughAttribute
00007fffe5780210 0x02000009 System.ComponentModel.DesignerCategoryAttribute
00007fffc3239d60 0x0200000a System.Web.Services.WebServiceBindingAttribute
00007fffc323a2f8 0x0200000b System.Web.Services.Protocols.WebClientProtocol
00007fffc32322d8 0x0200000c System.Web.Services.Protocols.SoapDocumentMethodAttribute
00007fffe80f5dd8 0x0200000d System.Object
00007fffe80f59c0 0x0200000e System.String

经过一顿搜索,发现模块中都是这些内容,仔细分析 TypeDef NameTypeRef Name,大概就能猜测到,代码中有 SoapHttpClient 去访问这个 xxxImplService 类名的服务地址,有了这些信息就可以分析源码了。

3. 寻找源码

很快就定位到了代码,原来是在请求 WebService 的过程中用 CSharpCodeProvider 动态生成了程序集,而且还塞了 4个 dll,截图如下:

到这里就知道了来龙去脉,最后就是让朋友合理的去卸载这里的 程序集 ,或者干脆绕过去。

三:总结

这次事故主要是朋友用 CSharpCodeProvider 动态生成程序集导致的程序集泄露,猜测代码是在哪里 copy 过来的,一定要搞清楚原理才能放心用,合理创建合理释放。

图片名称

与记一次 .NET某新能源MES系统 非托管泄露相似的内容:

记一次 .NET某新能源MES系统 非托管泄露

一:背景 1. 讲故事 前些天有位朋友找到我,说他们的程序有内存泄露,跟着我的错题集也没找出是什么原因,刚好手头上有一个 7G+ 的 dump,让我帮忙看下是怎么回事,既然找到我了那就给他看看吧,不过他的微信头像有点像 二道贩子,不管到我这里是不是 三道,该分析的还得要分析呀。😄😄😄 二:Wi

记一次 .NET 某拍摄监控软件 卡死分析

一:背景 1. 讲故事 今天本来想写一篇 非托管泄露 的生产事故分析,但想着昨天就上了一篇非托管文章,连着写也没什么意思,换个口味吧,刚好前些天有位朋友也找到我,说他们的拍摄监控软件卡死了,让我帮忙分析下为什么会卡死,听到这种软件,让我不禁想起了前些天 在程序员桌子上安装监控 的新闻,参考如下: 我

记一次 .NET某网络边缘计算系统 卡死分析

一:背景 1. 讲故事 早就听说过有什么 网络边缘计算,这次还真给遇到了,有点意思,问了下 chatgpt 这是干嘛的 ? 网络边缘计算是一种计算模型,它将计算能力和数据存储位置从传统的集中式数据中心向网络边缘的用户设备、传感器和其他物联网设备移动。这种模型的目的是在接近数据生成源头的地方提供更快速

记一次 .NET某机械臂上位系统 卡死分析

一:背景 1. 讲故事 前些天有位朋友找到我,说他们的程序会偶发性的卡死一段时间,然后又好了,让我帮忙看下怎么回事?窗体类的程序解决起来相对来说比较简单,让朋友用procdump自动抓一个卡死时的dump,拿到dump之后,上 windbg 说话。 二:WinDbg 分析 1. 主线程在做什么 要想

记一次 .NET某工厂报警监控设置 崩溃分析

一:背景 1. 讲故事 前些天有位朋友在微信上丢了一个崩溃的dump给我,让我帮忙看下为什么出现了崩溃,在 Windows 的事件查看器上显示的是经典的 访问违例 ,即 c0000005 错误码,不管怎么说有dump就可以上windbg开干了。 二:WinDbg 分析 1. 程序为谁崩溃了 在 Wi

记一次 .NET某游戏币自助机后端 内存暴涨分析

一:背景 1. 讲故事 前些天有位朋友找到我,说他们的程序内存会偶发性暴涨,自己分析了下是非托管内存问题,让我帮忙看下怎么回事?哈哈,看到这个dump我还是非常有兴趣的,居然还有这种游戏币自助机类型的程序,下次去大玩家看看他们出币的机器后端是不是C#写的?由于dump是linux上的程序,刚好win

记一次 .NET某工控视觉自动化系统 卡死分析

一:背景 1. 讲故事 今天分享的dump是训练营里一位学员的,从一个啥也不会到现在分析的有模有样,真的是看他成长起来的,调试技术学会了就是真真实实自己的,话不多说,上windbg说话。 二:WinDbg 分析 1. 为什么会卡死 这位学员是从事工控大类下的视觉自动化,也是目前.NET的主战场,这个

记一次 .NET某质量检测中心系统 崩溃分析

一:背景 1. 讲故事 这些天有点意思,遇到的几个程序故障都是和Windows操作系统或者第三方组件有关系,真的有点无语,今天就带给大家一例 IIS 相关的与大家分享,这是一家国企的.NET程序,出现了崩溃急需分析。 二:WinDbg 分析 1. 为什么会崩溃 崩溃原因相对还是好找的,双击dump文

记一次 .NET某工业设计软件 崩溃分析

一:背景 1. 讲故事 前些天有位朋友找到我,说他的软件在客户那边不知道什么原因崩掉了,从windows事件日志看崩溃在 clr 里,让我能否帮忙定位下,dump 也抓到了,既然dump有了,接下来就上 windbg 分析吧。 二:WinDbg 分析 1. 为什么崩溃在 clr 一般来说崩溃在clr

记一次 .NET某工控WPF程序被人恶搞的 卡死分析

一:背景 1. 讲故事 这一期程序故障除了做原理分析,还顺带吐槽一下,熟悉我的朋友都知道我分析dump是免费的,但免费不代表可以滥用我的宝贵时间,我不知道有些人故意恶搞卡死是想干嘛,不得而知,希望后面类似的事情越来越少吧!废话不多说,我们来看看是如何被恶搞的。 二:WinDbg 分析 1. 程序是如