.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]