51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 493|回复: 2
打印 上一主题 下一主题

[资料] 一件真实的故事告诉你安全防护的重要性

[复制链接]
  • TA的每日心情
    无聊
    3 天前
  • 签到天数: 1050 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2023-7-10 14:08:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
     一:背景
      1. 讲故事
      前几天有位朋友找到我,说他的程序有内存泄露,让我帮忙排查一下,截图如下:

      说实话看到 32bit, 1.5G 这些关键词之后,职业敏感告诉我,他这个可能是虚拟地址紧张所致,不管怎么说,有了 Dump 就可以上马分析。
      二:WinDbg分析
      1. 虚拟地址紧张所致吗
      要看是不是虚拟地址紧张,可以用 !address -summary 观察下内存段统计信息,截图如下:

      我去,用 WinDbg Preview 尽然分析不了,在加载 ntdll 的过程中死掉了,如果你是我们调试训练营的朋友,应该会深深的有体会,我们分析的第一个dump就存在这个情况,这个加载不了其实就预示着一种非托管泄露,这里暂不剧透。
      用 WinDbg Preview 分析不了怎么办呢?可以用 Windbg 的其他版本哈,比如 Windbg10, WinDbg6 等等,这里就采用 WinDbg10 X86 版本打开吧。
      0:000> !address -summary
      --- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
      Free                                    179          8cbb1000 (   2.199 GB)           54.97%
      Heap                                   6598          376f6000 ( 886.961 MB)  48.09%   21.65%
      <unknown>                              3091          31954000 ( 793.328 MB)  43.02%   19.37%
      Image                                   376           8c0d000 ( 140.051 MB)   7.59%    3.42%
      Stack                                    75           1780000 (  23.500 MB)   1.27%    0.57%
      Other                                     7             4e000 ( 312.000 kB)   0.02%    0.01%
      TEB                                      25             19000 ( 100.000 kB)   0.01%    0.00%
      PEB                                       1              1000 (   4.000 kB)   0.00%    0.00%
      --- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
      MEM_FREE                                179          8cbb1000 (   2.199 GB)           54.97%
      MEM_COMMIT                             9821          6bfad000 (   1.687 GB)  93.68%   42.18%
      MEM_RESERVE                             352           7492000 ( 116.570 MB)   6.32%    2.85%


      从卦中 MEM_COMMIT 的 %ofTotal= 42.18% 来看,提交内存占总的虚拟地址比重还不到一半,这说明我的猜测是错的,不存在虚拟地址紧张的情况,这里稍微提醒一下的是,这里不存在虚拟地址紧张是因为它开的是 Any CPU 模式,默认能吃到 4G 内存。
      不管怎么说,现在被当头一棒,既然这条路走不通,那会是什么情况导致的呢?一般来说这个内存量我是不愿意分析的,但既然分析到这里也只能继续分析,接下来用 !eeheap -gc 观察下托管堆内存占用情况。
      0:000> !eeheap -gc
      Number of GC Heaps: 1
      generation 0 starts at 0x777c0434
      generation 1 starts at 0x77781000
      generation 2 starts at 0x01861000
      ephemeral segment allocation context: none
       segment     begin  allocated      size
      01860000  01861000  0285ffdc  0xffefdc(16773084)
      ...
      77780000  77781000  77aa25c0  0x3215c0(3282368)
      Large object heap starts at 0x02861000
       segment     begin  allocated      size
      02860000  02861000  031e5cc0  0x984cc0(9981120)
      Total Size:              Size: 0x1f7e47e4 (528369636) bytes.
      ------------------------------
      GC Heap Size:    Size: 0x1f7e47e4 (528369636) bytes.


      从卦中看当前托管堆也才 528M 和 提交内存 1.6G 相距甚远,所以这个 dump 大概率是存在非托管内存泄露,其实 !address -summary 中的 Heap 也能佐证,说到底就是 ntheap 泄露。
      2. ntheap 怎么啦
      深挖 ntheap 我就不挖了,省的误入歧途,文章开头我说过 ntdll 无法加载的现象预示着一种非托管泄露,对 ,就是 GC 的加载堆泄露,加载堆是 CLR 用来映射 C# 程序集,模块,类型,方法等用途的一块私有内存,那怎么去洞察它呢?可以使用 !eeheap -loader 命令洞察。
      0:000> !eeheap -loader
      Loader Heap:
      --------------------------------------
      ...
      Module 05829f78: Size: 0x0 (0) bytes.
      Module 0582a8f8: Size: 0x0 (0) bytes.
      Module 0582b278: Size: 0x0 (0) bytes.
      Module 0582bbf8: Size: 0x0 (0) bytes.
      Module 0582c578: Size: 0x0 (0) bytes.
      Module 0582cef8: Size: 0x0 (0) bytes.
      Module 0582d878: Size: 0x0 (0) bytes.
      ...
      Module 362ea420: Size: 0x0 (0) bytes.
      Total size:      Size: 0x0 (0) bytes.
      --------------------------------------
      Total LoaderHeap size:   Size: 0x7e7e000 (132636672) bytes total, 0x28000 (163840) bytes wasted.
      =======================================


      虽然加载堆只统计到了 132M,但其中的 module 高达 2.3w 个,其实这里会有一些相关内存是加载堆之外无法统计到的,一般正常的程序不可能有这么多的module,所以这就是我们接下来突破的点,那怎么突破呢?最好的办法就是观察下这个 module 中到底有什么 type,使用 !dumpmodule 命令即可。
      0:000> !dumpmodule -mt 0582d878
      Name:       Unknown Module
      Attributes: Reflection
      Assembly:   0c229d38
      LoaderHeap:              00000000
      TypeDefToMethodTableMap: 050676e4
      TypeRefToMethodTableMap: 050676f8
      MethodDefToDescMap:      0506770c
      FieldDefToDescMap:       05067734
      MemberRefToDescMap:      00000000
      FileReferencesMap:       05067784
      AssemblyReferencesMap:   05067798
      Types defined in this module
            MT  TypeDef Name
      ------------------------------------------------------------------------------
      0582dcb0 0x02000002
      0582df90 0x02000003
      0582e018 0x02000004
      0582e0b8 0x02000005
      0582e194 0x02000006
      Types referenced in this module
            MT    TypeRef Name
      ------------------------------------------------------------------------------


      从模块中并没有看到类型的文字描述,那怎么办呢,我们随便抽一个 mt 看下这个 mt 下有什么方法,使用 !dumpmt 命令即可。
      0:000> !dumpmt -md 0582dcb0
      EEClass:         05068980
      Module:          0582d878
      Name:            
      mdToken:         02000002
      File:            Unknown Module
      BaseSize:        0x44
      ComponentSize:   0x0
      Slots in VTable: 8
      Number of IFaces in IFaceMap: 0
      --------------------------------------
      MethodDesc Table
         Entry MethodDe    JIT Name
      739819c8 735e61fc PreJIT System.Object.ToString()
      73987850 735e6204 PreJIT System.Object.Equals(System.Object)
      7398bd80 735e6224 PreJIT System.Object.GetHashCode()
      738ddbe8 735e6238 PreJIT System.Object.Finalize()
      0583b529 0582dc8c   NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterCallBack.InitCallbacks()
      0583b52d 0582dc94   NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterCallBack..ctor()
      0583c7d0 0582dc74    JIT Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterCallBack.Write3_root(System.Object)
      0583c868 0582dc80    JIT Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterCallBack.Write2_CallBack(System.String, System.String, xxx.Models.xxxBack, Boolean, Boolean)


      看到卦中的这些信息,我相信有很多朋友知道是怎么回事了,对,就是 Serialization 泄露,那它序列化什么类型呢 ? 从卦中看就是 xxx.Models.xxxBack 类,即 xmlSerializer.Serialize(xxx.Models.xxxBack) 的相关逻辑,接下来就需要逆向看下到底是哪里写的,结果发现是他的底层库封装的,有些方法有问题,有些没问题,真的是无语哈。
      //有问题的方法
          public static string Serialize(object o, Encoding encoding, string rootName)
          {
              XmlSerializer xmlSerializer = new XmlSerializer(o.GetType(), new XmlRootAttribute(rootName));
              ...
              xmlSerializer.Serialize(memoryStream, o, xmlSerializerNamespaces);
              return encoding.GetString(memoryStream.ToArray());
          }
          //正确的方法
          public static string Serialize(object Obj, Encoding encoding)
          {
              ...
              using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings))
              {
                  XmlSerializerNamespaces xmlSerializerNamespaces = new XmlSerializerNamespaces();
                  xmlSerializerNamespaces.Add("", "");
                  new XmlSerializer(Obj.GetType()).Serialize(xmlWriter, Obj, xmlSerializerNamespaces);
              }
              return encoding.GetString(memoryStream.ToArray());
          }


      这是一个老生常谈的问题,如果你用 new XmlSerializer(o.GetType(), new XmlRootAttribute(rootName)); 模式的话,一定要缓存起来,否则就会泄露,只能说是微软造的一个大坑吧,多少人都踩上去了。
      三:总结
      在我分析的真实dump案例中,见过 Castle ProxyGenerator 的泄露,也见过 CodeAnalysis.CSharp.Scripting 的泄露,还真没见过 XmlSerializer 的泄露,算是完美的补充了我的案例库!

    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

  • TA的每日心情
    开心
    昨天 14:13
  • 签到天数: 652 天

    连续签到: 20 天

    [LV.9]测试副司令

    2#
    发表于 2023-7-10 17:54:09 | 只看该作者
    很棒的实践
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2021-6-9 14:08
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]测试小兵

    3#
    发表于 2023-8-3 11:18:02 | 只看该作者
    看不懂,咋办?
    回复 支持 反对

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-24 07:19 , Processed in 0.068439 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表