偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Visual Studio中插件GDIWatch的實(shí)現(xiàn)

開發(fā) 后端
今天我們將要介紹的是一個(gè)GDIWatch插件,主要用于Visual Studio 2005/2008/2010。功能是在一個(gè)類似watch的窗口上顯示被調(diào)試程序的GDI對象的當(dāng)前狀態(tài)。

GDIWatch 是Virgo Software 開發(fā)的一個(gè)for Visual Studio的插件,支持Visual Studio 2005/2008/2010,它的功能主要是在一個(gè)類似watch的窗口上顯示被調(diào)試程序的GDI對象的當(dāng)前狀態(tài),比如HBRUSH的顏色,大小,圖片等等,并且它還能在調(diào)試過程中高亮顯示有變化的項(xiàng)目,方便程序員跟蹤調(diào)試畫圖函數(shù)。

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

(小聲說一下,crack在文中提供了) 

這是官方的截圖:

順便再貼一個(gè) GDIWatch 在 VS2010上使用的效果圖:

感覺還不賴,使用起來也挺方便的,就是拽個(gè)變量到它上面就可以了。

GDIWatch 不是免費(fèi)軟件,作者給了15天的試用期,如果需要繼續(xù)使用就要到官網(wǎng) www.gdiwatch.com 聯(lián)系作者獲取注冊碼。

P.S. 話說前天我在公司正好想上他的網(wǎng)站看看價(jià)錢如何,結(jié)果發(fā)現(xiàn)他的主頁不知出現(xiàn)神馬問題沒法顯示了,囧啊。

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

P.P.P.S. 在15天后我偶爾還想繼續(xù)使用,但是中國國情告訴我,花100多美刀買個(gè)插件是稍微有點(diǎn)貴了的說,而且目前在公司還沒用上VS2010,所以便可恥地嘗試crack,沒想到很好crack的說,稍微改動(dòng)一下居然就搞定了,主要是該作者的防范意識(shí)不夠啊,犯了很多防破解的大忌,給了人家很多線索,有需要的童鞋請猛擊此處下載,適用于1.5.1.254版本,替換原版之前請自行備份以防萬一!

好了, 言歸正傳,我當(dāng)初之所以找到這個(gè)軟件是因?yàn)榍瓣囎右恢痹趯懏媹D的代碼,本來是想說在網(wǎng)上找個(gè)VC6的插件的(沒辦法,公司還是在用),先是在 CodeProject 上找到一篇某位國人很久以前發(fā)表的文章,可是他居然不是開源的(這不坑爹嗎),而且遠(yuǎn)沒有 GDIWatch 那么方便好用(不給力?。钇婀值氖荂odeProject 居然讓他把文章給發(fā)表上去了(我勒個(gè)去),真是無奈。

不過該作者倒是簡單提到了一下他實(shí)現(xiàn)的方法:

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

最后只找到這個(gè)支持VS2005+的 GDIWatch,于是開始尋思這玩意怎么實(shí)現(xiàn),我想如果不是很復(fù)雜的話說不定可以在閑暇時(shí)間做一個(gè)for VC6的版本出來的說。

我首先思考的是要實(shí)現(xiàn)這樣的插件最重要是要解決哪些問題:

1、最重要的是,必須能夠跨進(jìn)程“訪問”被調(diào)試進(jìn)程的GDI objects,這是當(dāng)然的;

2、必須能跟VS協(xié)調(diào)運(yùn)作,響應(yīng)調(diào)試動(dòng)作并及時(shí)更新GUI,要像VS自己的watch那么好用;

3、必須有界面能顯示GDI objects,這......必須的;

當(dāng)然要完善這個(gè)插件的話,還需要盡量滿足下列條件:

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

2、如GDIWatch那樣支持拖放變量名到GUI上;

3、高亮有變化的內(nèi)容,方便跟蹤;

在定下上面這些條件后,下一步就是逐個(gè)解決問題了。

首先,要獲取GDI對象的屬性,基本是要走這條路:

  1. DWORD GetObjectType(__in  HGDIOBJ h);  
  2. HGDIOBJ GetCurrentObject(__in  HDC hdc,__in  UINT uObjectType);  
  3. int GetObject(__in   HGDIOBJ hgdiobj, __in   int cbBuffer, __out  LPVOID lpvObject); 

然而,GDI對象是基于進(jìn)程的,GDIWatch作為一個(gè)插件,也就是VS的一個(gè)DLL,它如果要拿被調(diào)試進(jìn)程的GDI對象句柄來直接用必然是不行的,

GDI objects 也不在 DuiplicateHandle 這個(gè)API支持的 object handle 的范疇之內(nèi)。

當(dāng)然了,GDI對象畢竟也是數(shù)據(jù),在用戶模式不能做到的,在內(nèi)核模式肯定有奇淫巧計(jì)可以做到,比如說訪問GDI對象表:

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

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

說起來有一款軟件很可能就是這么實(shí)現(xiàn)的,叫做 GDIView,它可以查看指定進(jìn)程當(dāng)前打開的所有GDI objects并顯示其屬性:

不過這些都屬于tricks,不是標(biāo)準(zhǔn)的做法,而且我也不熟悉具體實(shí)現(xiàn)方法,所以只能放棄。

其實(shí),畢竟目標(biāo)進(jìn)程是在被調(diào)試的狀態(tài)下,這還是給了插件解決這個(gè)問題的環(huán)境,或者說至少有一些條件可以被利用。

調(diào)試器是可以有辦法讀寫被調(diào)試進(jìn)程的內(nèi)存的,可以在被調(diào)試進(jìn)程的運(yùn)行空間插入一段代碼讓它執(zhí)行,只要上面提到的 GetObjectType 等API是在被調(diào)試進(jìn)程的領(lǐng)域執(zhí)行的,那么句柄就是有效的,自然能得到所需的結(jié)果。

要讀寫內(nèi)存,必然是這條路:

  1. HANDLE WINAPI OpenProcess(__in  DWORD dwDesiredAccess,  
  2. __in  BOOL bInheritHandle,  __in  DWORD dwProcessId); 
  3. BOOL WINAPI ReadProcessMemory(__in   HANDLE hProcess,  __in   LPCVOID lpBaseAddress, 
  4.  __out  LPVOID lpBuffer,  __in   SIZE_T nSize,  __out  SIZE_T *lpNumberOfBytesRead);  
  5.  
  6. LPVOID WINAPI VirtualAllocEx(__in HANDLE hProcess,  __in_opt LPVOID lpAddress, 
  7.  __in SIZE_T dwSize,  __in DWORD flAllocationType,  __in DWORD flProtect);  
  8.  
  9. BOOL WINAPI WriteProcessMemory(__in   HANDLE hProcess,  __in   LPVOID lpBaseAddress,
  10.   __in   LPCVOID lpBuffer,  __in   SIZE_T nSize,  __out  SIZE_T *lpNumberOfBytesWritten); 

接下來的事情大概是這樣:

設(shè)計(jì)一段代碼,主要做的事情是接受指定的GDI句柄,然后通過 GetObjectType/GetCurrentObject/GetObject 等API去獲取 GDI object 的相關(guān)信息,然后將結(jié)果保存在某個(gè)buffer。

假設(shè)這段代碼是一個(gè)C函數(shù),那么代碼大致是:

  1. typedef struct tagBrushInfo   
  2. {   
  3.    HBRUSH      hBrush;   
  4.    LOGBRUSH    logBrush;   
  5.  }BrushInfo, *PBrushInfo;   
  6.  typedef struct tagPenInfo   
  7. {   
  8.    HPEN        hPen;   
  9.    LOGPEN      logPen;   
  10. }PenInfo, *PPenInfo;   
  11.  typedef struct tagDCInfo   
  12.  {   
  13.    HDC         hDC;   
  14.    BrushInfo   brushInfo;   
  15.     PenInfo     penInfo;   
  16. }DCInfo, *PDCInfo;   
  17. LPVOID GetGDIObjectInfo(HGDIOBJ hGDIObjects)   
  18.  {   
  19.     LPVOID pInfo = NULL;   
  20.    DWORD dwObjType = GetObjectType(hGDIObjects);   
  21.   switch ( dwObjType )   
  22.     {   
  23.   case OBJ_DC:   
  24.     {   
  25.         PDCInfo pDCInfo = new DCInfo;   
  26.          pDCInfo->hDC = (HDC)hGDIObjects;   
  27.         // retrieve the brush info   
  28.        pDCInfo->brushInfo.hBrush = (HBRUSH)GetCurrentObject(pDCInfo->hDC, OBJ_BRUSH);   
  29.       if ( pDCInfo->brushInfo.hBrush )   
  30.        {   
  31. GetObject(pDCInfo->brushInfo.hBrush, sizeof(LOGBRUSH), &pDCInfo->brushInfo.logBrush);   
  32.        }   
  33.         // retrieve the pen info   
  34.          pDCInfo->penInfo.hPen = (HPEN)GetCurrentObject(pDCInfo->hDC, OBJ_PEN);   
  35.       if ( pDCInfo->penInfo.hPen )   
  36.      {   
  37.          GetObject(pDCInfo->penInfo.hPen, sizeof(LOGPEN), &pDCInfo->penInfo.logPen);   
  38.        }   
  39.       pInfo = pDCInfo;   
  40.   }   
  41.      break;   
  42.   case OBJ_BRUSH:   
  43.     if ( hGDIObjects )   
  44.     {   
  45.       PBrushInfo pBrushInfo = new BrushInfo;   
  46.      GetObject(hGDIObjects, sizeof(LOGBRUSH), &pBrushInfo->logBrush);   
  47.       pInfo = pBrushInfo;   
  48.      }   
  49.       break;   
  50.    }   
  51.    return pInfo;   
  52. }  

接下來就是要把 GetGDIObjectInfo 這個(gè)函數(shù)的代碼通過某種方式拷貝到被調(diào)試進(jìn)程中,方法很多,其中一種方法是通過插件內(nèi)實(shí)現(xiàn)一份該函數(shù),然后設(shè)法計(jì)算出函數(shù)體的二進(jìn)制代碼長度,從而將函數(shù)代碼拷貝,一個(gè)具體的例子是 CodeProject 上非常著名的文章 Three Ways to Inject Your Code into Another Process 中:

  1. static DWORD WINAPI ThreadFunc (INJDATA *pData)    
  2. {   
  3.    pData->fnSendMessage( pData->hwnd, WM_GETTEXT,    // Get password   
  4.      sizeof(pData->psText),   
  5.   (LPARAM)pData->psText );     
  6.   return 0;   
  7.  }   
  8.  // This function marks the memory address after ThreadFunc.   
  9. // int cbCodeSize = (PBYTE) AfterThreadFunc - (PBYTE) ThreadFunc.   
  10.  static void AfterThreadFunc (void)   
  11. {   
  12.  }  

可以看出是利用編譯器生成代碼的習(xí)慣,通過一個(gè)額外的空函數(shù) AfterThreadFunc 得到 ThreadFunc 的可能大?。?nCodeSize = AfterThreadFunc - ThreadFunc)。

此外也可以嘗試基于X86匯編指令自行組裝 GetGDIObjectInfo 的二進(jìn)制代碼,不過不是很容易閱讀和維護(hù)代碼。

不過這里還有一個(gè)需要注意的地方,CodeProject 的那篇文章提到了,就是同一個(gè)API的地址在不同進(jìn)程中可能會(huì)被映射到不同的地址上,所以要拷貝的代碼中肯定是不能直接那樣調(diào)用的,LoadLibrary 和 GetProcAddress 就是很好的一個(gè)能得到正確的地址的方法。前面的 GetGDIObjectInfo 函數(shù)還使用了 new operator,也要對應(yīng)修改為API函數(shù)如 VirtualAlloc 等。

在終于把這個(gè)GetGDIObjectInfo函數(shù)的代碼拷貝到目標(biāo)進(jìn)程后,下一步最為重要,就是要設(shè)法讓被調(diào)試進(jìn)程執(zhí)行該函數(shù)。

既然插件已經(jīng)是調(diào)試器的小弟,那么當(dāng)然可以利用debug API來實(shí)現(xiàn),而不必用到 CreateRemoteThread 這樣感覺稍微猥瑣的方法。

VS 應(yīng)該是通過 WaitForDebugEvent 等一系列API來進(jìn)行調(diào)試的,所以可以攔截它,比如在先調(diào)用 SuspendThread 把當(dāng)前進(jìn)程中所有非插件模塊所在線程給暫停掉,然后它的函數(shù)頭部加個(gè) jmp,讓它先跳轉(zhuǎn)到自己的一個(gè)函數(shù),在這個(gè)函數(shù)里,要先進(jìn)行一些邏輯判斷,在適合的時(shí)機(jī)利用 GetThreadContext/SetThreadContext 來操作被調(diào)試進(jìn)程,比如修改eip,然后 ContinueDebugEvent 讓被調(diào)試進(jìn)程執(zhí)行 GetGDIObjectInfo 函數(shù),在取得GDI對象的信息buffer后,拷貝到插件自己的內(nèi)存空間上,調(diào)用 ResumeThread 恢復(fù)所有之前被暫停的線程,最后不要忘了還要跳轉(zhuǎn)回 WaitForDebugEvent 的函數(shù)里。

關(guān)于運(yùn)用debug API的,最近的 Writing Windows Debugger 系列文章貌似不錯(cuò),我有時(shí)間要看看。

做完上面這些事情后,可以給插件的窗口post 一個(gè)消息,讓它讀取 GetGDIObjectInfo 返回的結(jié)果并更新GUI。

至于BITMAP這個(gè)比較特殊的對象,可以用 CreateDIBSection 這個(gè)API。

可是事情到此還沒完,因?yàn)檫€要寫VC6插件的代碼,還好這個(gè)問題已經(jīng)有一篇非常棒的文章可以參考:Undocumented Visual C++。

最后就是那個(gè)類似watch窗口的屬性列表控件,我沒找到現(xiàn)成的,不過倒是有一個(gè)還不錯(cuò)的封裝類 CPropTree,只是還需要在它的基礎(chǔ)上加不少代碼進(jìn)行增強(qiáng)。

原文鏈接:http://www.cnblogs.com/yonken/archive/2011/01/09/Guess_how_GDIWatch_was_implemented.html

【編輯推薦】 

  1. Visual Studio自定義調(diào)整窗體的兩個(gè)小技巧
  2. Visual Studio 2010中關(guān)于C#的幾點(diǎn)改進(jìn)
  3. Visual Studio 2010及.Net 4新功能一覽
  4. 提高效率 用好Visual Studio 2010自定義代碼段
責(zé)任編輯:彭凡 來源: 博客園
相關(guān)推薦

2010-07-15 08:50:09

SharePointVisual Stud

2025-06-03 07:10:00

開發(fā)插件代碼

2009-11-02 14:16:43

Visual Stud

2011-09-01 09:23:51

Python

2009-12-04 16:57:52

Visual Stud

2009-12-15 17:12:18

Visual Stud

2024-04-19 13:27:43

插件開發(fā)

2010-01-28 09:07:50

Visual Stud

2010-03-11 16:09:38

Visual StudSilverligh

2009-12-03 10:26:38

Visual Stud

2009-12-03 10:38:29

Visual Stud

2009-12-17 09:06:23

Visual Stud

2024-04-26 11:50:34

開發(fā)插件

2012-09-19 10:14:12

Visual Stud

2010-09-25 08:50:00

Visual Stud

2009-09-02 16:21:17

Visual BasiC#語言

2010-03-11 15:23:44

Visual Stud

2009-12-02 09:43:38

Visual Stud

2009-12-03 15:21:24

2010-01-27 10:32:40

Visual Stud
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號