今世 发表于 2023-7-25 10:28:55

.NET程序的 GDI句柄泄露 的再反思

一:背景

1. 讲故事

上个月我写过一篇 如何洞察 C# 程序的 GDI 句柄泄露 文章,当时用的是 GDIView + WinDbg 把问题搞定,前者用来定位泄露资源,后者用来定位泄露代码,后面有朋友反馈两个问题:

[*]GDIView 统计不准怎么办?
[*]我只有 Dump 可以统计吗?
其实那篇文章也聊过,在 x64 或者 wow64 的程序里,在用户态内存段中有一个 GDI Shared Handle Table 句柄表,这个表中就统计了各自句柄类型的数量,如果能统计出来也就回答了上面的问题,对吧。
32bit 程序的 GDI Shared Handle Table 段是没有的,即 _PEB.GdiSharedHandleTable = NULL。
0:002> dt ntdll!_PEB GdiSharedHandleTable 01051000
+0x0f8 GdiSharedHandleTable : (null)有了这些前置基础,接下来就可以开挖了。
二:挖 GdiSharedHandleTable

1. 测试代码

为了方便测试,我来造一个 DC句柄 的泄露。
    internal class Program
    {

      
      extern static void GDILeak();

      static void Main(string[] args)
      {
            try
            {
                GDILeak();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Console.ReadLine();
      }
    }然后就是 GDILeak 的 C++ 实现代码。
extern "C"
{
        _declspec(dllexport) void GDILeak();
}

void GDILeak()
{
    while (true)
    {
      CreateDCW(L"DISPLAY", nullptr, nullptr, nullptr);

      auto const gdiObjectsCount = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
      std::cout << "GDI objects: " << gdiObjectsCount << std::endl;

      Sleep(10);
    }
}
从卦中可以看到,当前有1029个 GDICell 结构体,接下来怎么鉴别每一条记录上都是什么类型呢?其实这里是有枚举的。

[*]DC = 0x01
[*]Region = 0x04
[*]Bitmap = 0x05
[*]Palette =0x08
[*]Font =0x0a
[*]Brush = 0x10
[*]Pen = 0x30
即 GDIView 中的 红色一列 。

到这里我们可以通过肉眼观察 + F5 检索,可以清晰的看到1029 个句柄对象,其中 1028 个是 DC 对象,其实这就是我们泄露的,截图如下:

3. 脚本处理

如果大家通读会发现这些都是固定步骤,完全可以写成比如 C++ 和 Javascript 的格式脚本,在 StackOverflow 上还真有这样的脚本。
0:000> dt ntdll!_PEB GdiSharedHandleTable @$peb
   +0x0f8 GdiSharedHandleTable : 0x00000000`03560000 Void三:总结

如果大家想从 DUMP 文件中提取 GDI 句柄泄露类型,这是一篇很好的参考资料,相信能从另一个角度给你提供一些灵感。

来源:https://www.cnblogs.com/huangxincheng/archive/2023/07/25/17579180.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: .NET程序的 GDI句柄泄露 的再反思