GDI对象调试器
能实现以下功能:
- 预览目标进程的GDI对象图样。
- 分析DC属性(包括颜色、点、尺寸、矩形、区域、路径、映射模式、选中的对象等等所有可获得的DC属性;详参图1.1)。
- 查看创建GDI对象时的栈帧;能定位到创建GDI对象的帧,藉此得以轻易的分析GDI对象泄漏并找到发生泄漏的代码。
- 断点;可设立对象访问断点和窗口绘制断点和消息断点(窗口发生某消息时中断)。
- 转储GDI对象到磁盘(如转储位图或DC中选中的位图)。
- 还原GDI对象结构;包括已公开的为API所用的形如 BITMAP、LOGFONT的结构和未公开的执行体中NT GDI对象表存储的GDIOBJECT结构。
- 分析GDI句柄结构;包括对象在NT GDI对象表中的索引、栈对象标记、对象类型等。
- 查看各类GDI对象占用率(GDI对象数量)。
GDI对象调试器附加后对目标进程产生[可能]的影响
HOOK SSDT表中函数无法很好推论用户行为且需要写驱动,考虑PG等等老大难问题。GDI对象调试器大概挂接了近300个操作GDI对象的应用层(API,包括一些未导出的)函数,所以,需要将一部分代码注入到目标进程执行,这会影响目标进程运行环境,以下列出注入后可能对目标进程产生的影响:
- 使用CreateEvent创建一个名为PID:%d_FH_InitializeCompleteEvent的初始化完成事件对象。
- 使用CreateFileMapping在全局名字空间创建一个名为PID:%d_FH_InitializeFailedDescriptBufferName的文件映射对象用于传递初始化失败消息。
- 使用CreateFileMapping在全局名字空间创建一个名为PID:%d_FH_GDIObjectRobberStack的文件映射对象用于向调试进程传递GDI对象。
- 使用CreateFileMapping在全局名字空间创建一个名为PID:%d_FH_GDIObjectRobberOption的文件映射对象用于接收调试进程设置的选项和断点等指令。
- 使用CreateFileMapping在全局名字空间创建一个名为PID:%d_FH_WindowMessageRobberStack的文件映射对象用于向调试进程发送目标进程中某个线程正在发生的消息。
- 抓取模块 初始化过程还会挂接 NtQueryInformationThread和NtTerminateThread用来仿制 TEB 来存储抓取模块 所需要的线程环境块(包括递归检测和消息帧链等),还会挂接InternalCallWinProc函数实现消息劫取,由于InternalCallWinProc函数会被很多线程频繁调用,可能会导致性能下降。
- HOOK后的GDI API函数会在Detour函数内发生额外的转储对象到调试进程操作,如果是创建GDI对象的API还会包括回溯栈帧操作,这些过程可能导致性能大幅下降。
注:
- 此处列举的不代表全部,且将来可能还有扩充,但承诺只使用影响较小的Windows内核对象作为进程间通讯手段,不会使用COM(改变线程模型或暗含线程模型为……的假设)或Socket(初始化为特定版本)等可能改变运行环境的方式。
- 以上对象名称中 PID:%d 处的 %d 代表目标进程的进程ID。
获得GDI对象调试器:
到此 http://download.csdn.net/download/passfuhao/10158251(或老版本下载地址:http://download.csdn.net/download/passfuhao/9910116)下载“GDI对象调试器.rar”文件并解压得到 GDIObjectView 目录,其内包含两个核心文件为:GDIObjectRobber.dll和GDIObjectView.exe,分别为抓取模块 和主程序。用户需要通过GDIObjectView.exe主程序向目标进程注入GDIObjectRobber.dll,随后既可观查到目标进程的GDI对象信息(参考图1.2)。
图1.2
使用GDIObjectView.exe
GDIObjectView 需要命令行参数表示目标进程和调试符号路径,命令行参数如下:
参数列表:
/PID | ProcessId | 指定要附加的进程ID。GDIObjectView将附加到该进程中读取GDI对象。 |
/IM | ImageName | 指定要附加的进程映像名称。GDIObjectView将附加到该进程中读取GDI对象。 |
/CMD | CommandLine | 指定启动新进程所使用的命令行参数。GDIObjectView将以挂起方式启动该进程并在附加GDI对象抓取模块后恢复进程。 |
PDBFiles1..n | MultiplePDBFilesDirectory | 调试符号文件目录(多个目录用 ';' 间隔)。GDIObjectView通过这些目录下的符号文件还原目标进程创建GDI时的栈帧源信息。若该忽略该参数则GDIObjectView从默认目录加载调试符号。(参见注意项第1、2条)。 |
例:
GDIObjectView /IM Notepad.exe C:\SymbolsGDIObjectView /PID 1234 SRV*\Symbols* http://msdl.microsoft.com/download/symbols
GDIObjectView /CMD "C:\window\notepad.exe C:\123.txt" C:\Symbols
GDIObjectView /IM QQ.exe
注意:
1、若忽略 PDBFiles1...n 参数GDIObjectView将从环境变量 _NT_SYMBOL_PATH 和 _NT_ALTERNATE_SYMBOL_PATH 指向的目录搜索符号文件。2、若 PDBFiles1..n 参数指向的符号路径包含网络路径或在忽略 PDBFiles1...n 参数的情况下环境变量 _NT_SYMBOL_PATH 和 _NT_ALTERNATE_SYMBOL_PATH 包含了网络路径,则GDIObjectView将尝试下载符号,这可能导致GDIObjectView加载时间过长。
3、若没有指定正确的调试符号路径,你将只能看到类似 ModuleName.dll!00e090f0 这样的栈帧。
附几张图:
图1.3,配合VS调试时查看创建GDI对象的栈帧:
图1.4,查看创建兼容DC的栈帧:
图1.5,设置断点:
两个已知的BUG
1、以GetDC得到的HDC作实参调用CreateCompatibleDC,然后使用SelectObject向兼容DC中选入一个LoadImage得到的位图会导致创建兼容DC的栈帧丢失。2、忘了。
暂时没时间解决。
有关分析和诊断GDI错误的其它值得参考的工具和文章:
用 Windows XP 的两个强有力的工具在您的代码中检测并堵塞GDI 泄漏:用 Windows XP 的两个强有力的工具在您的代码中检测并堵塞 GDI 泄漏 | Microsoft Learn
Bear:Bear - See GDI/User Object usage of all processes - the sz development
GDIView:GDIView - View GDI handles/resources list and detect GDI leaks
附,工具和源码下载地址:
已编译的工具:
CSDN下载:http://download.csdn.net/download/passfuhao/10158251(无法上传0分资源,需要2积分)
百度网盘下载:GDI对象调试器-附文档.rar_免费高速下载|百度网盘-分享无限制
通用HOOK,如何HOOK近300个函数:
总是需要用同样的话回答如何写近300个detour函数的问题,实际只写了一个detour函数,看这儿:编译时推导函数调用约定、常量性、返回值、参数等函数类型特征_穿女装的程序员的博客-CSDN博客