Visual Studio插件GDIWatch实现浅析

  GDIWatch 是Virgo Software 开发的一个for Visual Studio的插件,支持2005/2008/2010,它的功能主要是在一个类似watch的窗口上显示被调试程序的GDI对象的当前状态,比如HBRUSH的颜色,大小,图片等等,并且它还能在调试过程中高亮显示有变化的项目,方便程序员跟踪调试画图函数。

  下载地址: http://www.gdiwatch.com/GDIWatch.msi

  (小声说一下,crack在文中提供了) 

  这是官方的截图:

  顺便再贴一个 GDIWatch 在 VS2010上使用的效果图:

  感觉还不赖,使用起来也挺方便的,就是拽个变量到它上面就可以了。

  GDIWatch 不是免费软件,作者给了15天的试用期,如果需要继续使用就要到官网 www.gdiwatch.com 联系作者获取注册码。

  P.S. 话说前天我在公司正好想上他的网站看看价钱如何,结果发现他的主页不知出现神马问题没法显示了,囧啊。

  P.P.S. 印象中貌似是要100多美刀的样子。

  P.P.P.S. 在15天后我偶尔还想继续使用,但是中国国情告诉我,花100多美刀买个插件是稍微有点贵了的说,而且目前在公司还没用上VS2010,所以便可耻地尝试crack,没想到很好crack的说,稍微改动一下居然就搞定了,主要是该作者的防范意识不够啊,犯了很多防破解的大忌,给了人家很多线索,有需要的童鞋请猛击此处下载,适用于1.5.1.254版本,替换原版之前请自行备份以防万一!

  好了, 言归正传,我当初之所以找到这个软件是因为前阵子一直在写画图的代码,本来是想说在网上找个VC6的插件的(没办法,公司还是在用),先是在 CodeProject 上找到一篇某位国人很久以前发表的文章,可是他居然不是开源的(这不坑爹吗),而且远没有 GDIWatch 那么方便好用(不给力啊),最奇怪的是CodeProject 居然让他把文章给发表上去了(我勒个去),真是无奈。

  不过该作者倒是简单提到了一下他实现的方法:

  The steps to do watch Image is :
  (1)get the selection text by ISelectionText interface
  (2)get the value of selection text by IDebugger interface
  (3)Read the memeory or bitmap data from the debugged process memory space
  (4)show it

  最后只找到这个支持VS2005+的 GDIWatch,于是开始寻思这玩意怎么实现,我想如果不是很复杂的话说不定可以在闲暇时间做一个for VC6的版本出来的说。

  我首先思考的是要实现这样的插件最重要是要解决哪些问题:

  1、最最重要的是,必须能够跨进程“访问”被调试进程的GDI objects,这是当然的;

  2、必须能跟VS协调运作,响应调试动作并及时更新GUI,要像VS自己的watch那么好用;

  3、必须有界面能显示GDI objects,这......必须的;

  当然要完善这个插件的话,还需要尽量满足下列条件:

  1、避免使用undocumented trick,保证兼容性;

  2、如GDIWatch那样支持拖放变量名到GUI上;

  3、高亮有变化的内容,方便跟踪;

  在定下上面这些条件后,下一步就是逐个解决问题了。

  首先,要获取GDI对象的属性,基本是要走这条路:

DWORD GetObjectType(__in  HGDIOBJ h);

HGDIOBJ GetCurrentObject(__in  HDC hdc,__in  UINT uObjectType);

int GetObject(__in   HGDIOBJ hgdiobj, __in   int cbBuffer, __out  LPVOID lpvObject);

  然而,GDI对象是基于进程的,GDIWatch作为一个插件,也就是VS的一个DLL,它如果要拿被调试进程的GDI对象句柄来直接用必然是不行的,

  GDI objects 也不在 DuiplicateHandle 这个API支持的 object handle 的范畴之内。

  当然了,GDI对象毕竟也是数据,在用户模式不能做到的,在内核模式肯定有奇淫巧计可以做到,比如说访问GDI对象表:

http://topic.csdn.NET/t/20031009/14/2337150.html

http://hi.baidu.com/qzccan/blog/item/154b542375171440ac34de08.html

  说起来有一款软件很可能就是这么实现的,叫做 GDIView,它可以查看指定进程当前打开的所有GDI objects并显示其属性:

  不过这些都属于tricks,不是标准的做法,而且我也不熟悉具体实现方法,所以只能放弃。

  其实,毕竟目标进程是在被调试的状态下,这还是给了插件解决这个问题的环境,或者说至少有一些条件可以被利用。

  调试器是可以有办法读写被调试进程的内存的,可以在被调试进程的运行空间插入一段代码让它执行,只要上面提到的 GetObjectType 等API是在被调试进程的领域执行的,那么句柄就是有效的,自然能得到所需的结果。

  要读写内存,必然是这条路:

HANDLE WINAPI OpenProcess(__in  DWORD dwDesiredAccess,  __in  BOOL bInheritHandle,  __in  DWORD dwProcessId);

BOOL WINAPI ReadProcessMemory(__in   HANDLE hProcess,  __in   LPCVOID lpBaseAddress,  __out  LPVOID lpBuffer,  __in   SIZE_T nSize,  __out  SIZE_T *lpNumberOfBytesRead);

LPVOID WINAPI VirtualAllocEx(__in HANDLE hProcess,  __in_opt LPVOID lpAddress,  __in SIZE_T dwSize,  __in DWORD flAllocationType,  __in DWORD flProtect);

BOOL WINAPI WriteProcessMemory(__in   HANDLE hProcess,  __in   LPVOID lpBaseAddress,  __in   LPCVOID lpBuffer,  __in   SIZE_T nSize,  __out  SIZE_T *lpNumberOfBytesWritten);

  接下来的事情大概是这样:

  设计一段代码,主要做的事情是接受指定的GDI句柄,然后通过 GetObjectType/GetCurrentObject/GetObject 等API去获取 GDI object 的相关信息,然后将结果保存在某个buffer。

  假设这段代码是一个C函数,那么代码大致是:

typedef struct tagBrushInfo{    HBRUSH      hBrush;    LOGBRUSH    logBrush;}BrushInfo, *PBrushInfo;typedef struct tagPenInfo{    HPEN		hPen;    LOGPEN      logPen;}PenInfo, *PPenInfo;typedef struct tagDCInfo{    HDC         hDC;    BrushInfo   brushInfo;    PenInfo     penInfo;}DCInfo, *PDCInfo;LPVOID GetGDIObjectInfo(HGDIOBJ hGDIObjects){	LPVOID pInfo = NULL;    DWORD dwObjType = GetObjectType(hGDIObjects);    switch ( dwObjType )    {    case OBJ_DC:        {			PDCInfo pDCInfo = new DCInfo;            pDCInfo->hDC = (HDC)hGDIObjects;            // retrieve the brush info            pDCInfo->brushInfo.hBrush = (HBRUSH)GetCurrentObject(pDCInfo->hDC, OBJ_BRUSH);            if ( pDCInfo->brushInfo.hBrush )            {                GetObject(pDCInfo->brushInfo.hBrush, sizeof(LOGBRUSH), &pDCInfo->brushInfo.logBrush);            }            // retrieve the pen info            pDCInfo->penInfo.hPen = (HPEN)GetCurrentObject(pDCInfo->hDC, OBJ_PEN);            if ( pDCInfo->penInfo.hPen )            {                GetObject(pDCInfo->penInfo.hPen, sizeof(LOGPEN), &pDCInfo->penInfo.logPen);            }            pInfo = pDCInfo;        }        break;    case OBJ_BRUSH:        if ( hGDIObjects )        {			PBrushInfo pBrushInfo = new BrushInfo;            GetObject(hGDIObjects, sizeof(LOGBRUSH), &pBrushInfo->logBrush);			pInfo = pBrushInfo;        }        break;    }    return pInfo;}

NET技术Visual Studio插件GDIWatch实现浅析,转载需保留来源!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。