当前位置:首页 > 科技  > 软件

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

来源: 责编: 时间:2023-08-05 11:45:59 3259观看
导读一、背景1. 讲故事上个月我写过一篇 如何洞察 C# 程序的 GDI 句柄泄露 文章,当时用的是 GDIView + WinDbg 把问题搞定,前者用来定位泄露资源,后者用来定位泄露代码,后面有朋友反馈两个问题:GDIView 统计不准怎么办?我只有 D

一、背景

1. 讲故事

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

  • GDIView 统计不准怎么办?
  • 我只有 Dump 可以统计吗?

其实那篇文章也聊过,在 x64 或者 wow64 的程序里,在用户态内存段中有一个 GDI Shared Handle Table 句柄表,这个表中就统计了各自句柄类型的数量,如果能统计出来也就回答了上面的问题,对吧。xqF28资讯网——每日最新资讯28at.com

32bit 程序的 GDI Shared Handle Table 段是没有的,即 _PEB.GdiSharedHandleTable = NULL。xqF28资讯网——每日最新资讯28at.com

0:002> dt ntdll!_PEB GdiSharedHandleTable 01051000  +0x0f8 GdiSharedHandleTable : (null)xqF28资讯网——每日最新资讯28at.com

有了这些前置基础,接下来就可以开挖了。xqF28资讯网——每日最新资讯28at.com

二、挖 GdiSharedHandleTable

1. 测试代码

为了方便测试,我来造一个 DC句柄 的泄露。xqF28资讯网——每日最新资讯28at.com

internal class Program    {        [DllImport("Example_20_1_5", CallingConvention = CallingConvention.Cdecl)]        extern static void GDILeak();        static void Main(string[] args)        {            try            {                GDILeak();            }            catch (Exception ex)            {                Console.WriteLine(ex.Message);            }            Console.ReadLine();        }    }

然后就是 GDILeak 的 C++ 实现代码。xqF28资讯网——每日最新资讯28at.com

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);    }}

程序跑起来后,如果你是x64的程序那没有关系,但如果你是 32bit 的程序一定要生成一个 Wow64 格式的 Dump,千万不要抓它的 32bit dump,否则拿不到 GdiSharedHandleTable 字段也就无法后续分析了,那如何生成 Wow64 格式的呢?我推荐两种方式。xqF28资讯网——每日最新资讯28at.com

  • 使用64bit任务管理器(系统默认)生成
  • 使用 procdump -64 -ma QQ.exe 中的 -64 参数

这里我们采用第一种方式,截图如下:xqF28资讯网——每日最新资讯28at.com

图片图片xqF28资讯网——每日最新资讯28at.com

2. 分析 GdiSharedHandleTable

使用伪寄存器变量提取出 GdiSharedHandleTable 字段,输出如下:xqF28资讯网——每日最新资讯28at.com

0:000> dt ntdll!_PEB GdiSharedHandleTable @$peb   +0x0f8 GdiSharedHandleTable : 0x00000000`03560000 Void

接下来使用 !address 找到这个 GdiSharedHandleTable 的首末地址。xqF28资讯网——每日最新资讯28at.com

0:000> !address 0x00000000`03560000Usage:                  OtherBase Address:           00000000`03560000End Address:            00000000`036e1000Region Size:            00000000`00181000 (   1.504 MB)State:                  00001000          MEM_COMMITProtect:                00000002          PAGE_READONLYType:                   00040000          MEM_MAPPEDAllocation Base:        00000000`03560000Allocation Protect:     00000002          PAGE_READONLYAdditional info:        GDI Shared Handle TableContent source: 1 (target), length: 181000

上一篇我们聊过每新增一个GDI句柄都会在这个表中增加一条 GDICell,输出如下:xqF28资讯网——每日最新资讯28at.com

typedef struct { PVOID64 pKernelAddress; USHORT wProcessId; USHORT wCount; USHORT wUpper; USHORT wType; PVOID64 pUserAddress;} GDICell;

这个 GDICell 有两个信息比较重要。xqF28资讯网——每日最新资讯28at.com

  • wProcessId 表示进程 ID
  • wType 表示句柄类型。

理想情况下是对 句柄类型 进行分组统计就能知道是哪里的泄露,接下来的问题是如何找呢?可以仔细观察结构体, wProcessId 和 wType 的偏移是 3USHORT=6byte,我们在内存中找相对偏移不就可以了吗?接下来在内存中搜索这块xqF28资讯网——每日最新资讯28at.com

0:000> ~..  0  Id: 101c.4310 Suspend: 0 Teb: 00000000`009bf000 Unfrozen      Start: Example_20_1_4_exe!wmainCRTStartup (00000000`00d4ffe0)      Priority: 0  Priority class: 32  Affinity: fff0:000> s-w 03560000 036e1000 101c00000000`03562060  101c 0000 af01 0401 0b00 0830 0000 0000  ..........0.....00000000`035782a0  101c ff1d ffff ffff 0000 0000 1d0f 010f  ................00000000`0357c688  101c 0000 3401 0401 0160 0847 0000 0000  .....4..`.G........00000000`035a5f98  101c 0000 0801 0401 0dc0 08a1 0000 0000  ................00000000`035a5fb0  101c 0000 0801 0401 0c60 08a1 0000 0000  ........`.......00000000`035a5fc8  101c 0000 0801 0401 0840 08a1 0000 0000  ........@.......00000000`035a5fe0  101c 0000 0801 0401 0b00 08a1 0000 0000  ................

图片图片xqF28资讯网——每日最新资讯28at.com

从卦中可以看到,当前有1029个 GDICell 结构体,接下来怎么鉴别每一条记录上都是什么类型呢?其实这里是有枚举的。xqF28资讯网——每日最新资讯28at.com

  1. DC = 0x01
  2. Region = 0x04
  3. Bitmap = 0x05
  4. Palette =0x08
  5. Font =0x0a
  6. Brush = 0x10
  7. Pen = 0x30

即 GDIView 中的 红色一列 。xqF28资讯网——每日最新资讯28at.com

图片图片xqF28资讯网——每日最新资讯28at.com

到这里我们可以通过肉眼观察 + F5 检索,可以清晰的看到1029 个句柄对象,其中 1028 个是 DC 对象,其实这就是我们泄露的,截图如下:xqF28资讯网——每日最新资讯28at.com

图片图片xqF28资讯网——每日最新资讯28at.com

3. 脚本处理

如果大家通读会发现这些都是固定步骤,完全可以写成比如 C++ 和 Javascript 的格式脚本,在 StackOverflow 上还真有这样的脚本。xqF28资讯网——每日最新资讯28at.com

$$ Run as: $$>a<DumpGdi.txt$$ Written by Alois Kraus 2016$$ uses pseudo registers r0-5 and r8-r14r @$t1=0r @$t8=0r @$t9=0r @$t10=0r @$t11=0r @$t12=0r @$t13=0r @$t14=0$$ Increment count is 1 byte until we find a matching field with the current pidr @$t4=1r @$t0=$peb$$ Get address of GDI handle table into t5.foreach /pS 3 /ps 1 ( @$GdiSharedHandleTable { dt ntdll!_PEB GdiSharedHandleTable @$t0 } ) { r @$t5 = @$GdiSharedHandleTable }$$ On first call !address produces more output. Do a warmup.foreach /pS 50 ( @$myStartAddress {!address  @$t5} ) {  }$$ Get start address of file mapping into t2.foreach /pS 4 /ps 40 ( @$myStartAddress {!address  @$t5} ) { r @$t2 = @$myStartAddress }$$ Get end address of file mapping into t3.foreach /pS 7 /ps 40 ( @$myEndAddress {!address  @$t5} ) { r @$t3 = @$myEndAddress }.printf "GDI Handle Table %p %p", @$t2, @$t3.for(; @$t2 < @$t3; r @$t2 = @$t2 + @$t4) {  $$ since we walk bytewise through potentially invalid memory we need first to check if it points to valid memory  .if($vvalid(@$t2,4) == 1 )   {     $$ Check if pid matches     .if (wo(@$t2) == @$tpid )      {         $$ increase handle count stored in $t1 and increase step size by 0x18 because we know the cell structure GDICell has a size of 0x18 bytes.        r @$t1 = @$t1+1        r @$t4 = 0x18        $$ Access wType of GDICELL and increment per GDI handle type        .if (by(@$t2+6) == 0x1 )  { r @$t8 =  @$t8+1  }        .if (by(@$t2+6) == 0x4 )  { r @$t9 =  @$t9+1  }        .if (by(@$t2+6) == 0x5 )  { r @$t10 = @$t10+1 }        .if (by(@$t2+6) == 0x8 )  { r @$t11 = @$t11+1 }        .if (by(@$t2+6) == 0xa )  { r @$t12 = @$t12+1 }        .if (by(@$t2+6) == 0x10 ) { r @$t13 = @$t13+1 }        .if (by(@$t2+6) == 0x30 ) { r @$t14 = @$t14+1 }     }   } }.printf "/nGDI Handle Count      %d", @$t1.printf "/n/tDeviceContexts: %d", @$t8.printf "/n/tRegions:        %d", @$t9.printf "/n/tBitmaps:        %d", @$t10.printf "/n/tPalettes:       %d", @$t11.printf "/n/tFonts:          %d", @$t12.printf "/n/tBrushes:        %d", @$t13.printf "/n/tPens:           %d", @$t14.printf "/n/tUncategorized:  %d/n", @$t1-(@$t14+@$t13+@$t12+@$t11+@$t10+@$t9+@$t8)

最后我们用脚本跑一下,哈哈,是不是非常清楚。xqF28资讯网——每日最新资讯28at.com

0:000> $$>a< "D:/testdump/DumpGdi.txt"GDI Handle Table 0000000003560000 00000000036e1000GDI Handle Count      1028 DeviceContexts: 1028 Regions:        0 Bitmaps:        0 Palettes:       0 Fonts:          0 Brushes:        0 Pens:           0 Uncategorized:  0

三、总结

如果大家想从 DUMP 文件中提取 GDI 句柄泄露类型,这是一篇很好的参考资料,相信能从另一个角度给你提供一些灵感。xqF28资讯网——每日最新资讯28at.com

本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-140-0.html.NET 程序的 GDI 句柄泄露的再反思

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 这款新兴工具平台,让你的电脑效率翻倍

下一篇: 虚拟键盘 API 的妙用

标签:
  • 热门焦点
  • 量化指标是与非:挽救被量化指标扼杀的技术团队

    量化指标是与非:挽救被量化指标扼杀的技术团队

    作者 | 刘新翠整理 | 徐杰承本文整理自快狗打车技术总监刘新翠在WOT2023大会上的主题分享,更多精彩内容及现场PPT,请关注51CTO技术栈公众号,发消息【WOT2023PPT】即可直接领取
  • 让我们一起聊聊文件的操作

    让我们一起聊聊文件的操作

    文件【1】文件是什么?文件是保存数据的地方,是数据源的一种,比如大家经常使用的word文档、txt文件、excel文件、jpg文件...都是文件。文件最主要的作用就是保存数据,它既可以保
  • 一文搞定Java NIO,以及各种奇葩流

    一文搞定Java NIO,以及各种奇葩流

    大家好,我是哪吒。很多朋友问我,如何才能学好IO流,对各种流的概念,云里雾里的,不求甚解。用到的时候,现百度,功能虽然实现了,但是为什么用这个?不知道。更别说效率问题了~下次再遇到,
  • 2023年,我眼中的字节跳动

    2023年,我眼中的字节跳动

    此时此刻(2023年7月),字节跳动从未上市,也从未公布过任何官方的上市计划;但是这并不妨碍它成为中国最受关注的互联网公司之一。从2016-17年的抖音强势崛起,到2018年的&ldquo;头腾
  • 猿辅导与新东方的两种“归途”

    猿辅导与新东方的两种“归途”

    作者|卓心月 出品|零态LT(ID:LingTai_LT)如何成为一家伟大企业?答案一定是对&ldquo;势&rdquo;的把握,这其中最关键的当属对企业战略的制定,且能够站在未来看现在,即使这其中的
  • 重估百度丨大模型,能撑起百度的“今天”吗?

    重估百度丨大模型,能撑起百度的“今天”吗?

    自象限原创 作者|程心 罗辑2023年之前,对于自己的&ldquo;今天&rdquo;,百度也很迷茫。&ldquo;新业务到 2022 年底还是 0,希望 2023 年出来一个 1。&rdquo;这是2022年底,李彦宏
  • 华为HarmonyOS 4升级计划公布:首批34款机型今日开启公测

    华为HarmonyOS 4升级计划公布:首批34款机型今日开启公测

    8月4日消息,今天下午华为正式发布了HarmonyOS 4系统,在更流畅的前提下,还带来了不少新功能,UI设计也有变化,会让手机焕然一新。华为宣布,首批机型将会在
  • 三星折叠屏手机去年销售近1000万台 今年目标定为1500万

    三星折叠屏手机去年销售近1000万台 今年目标定为1500万

    7月29日消息,三星率先发力可折叠手机市场,在全球市场已经取得了非常亮眼的成绩,接下来会进一步巩固和扩大这一优势。三星在推出Galaxy Z Flip5和Galax
  • 三翼鸟智能家居亮相电博会,让用户体验更真实

    三翼鸟智能家居亮相电博会,让用户体验更真实

    2021电博会在青岛国际会展中心开幕中,三翼鸟直接把“家”搬到了现场,成为了展会的一大看点。这也是三翼鸟继9月9日发布了行业首个一站式定制智慧家平台后的
Top
Baidu
map