Source Insight 插件


一提到外掛程序,大家肯定都不陌生,QQ就有很多個版本的去廣告外掛,很多游戲也有用於擴展功能或者作弊的工具,其中很多也是以外掛的形式提供的。外掛和插件的區別在於插件通常依賴於程序的支持,如果程序不支持插件機制,那么就無法為其開發插件,而外掛則不然,它不依賴於程序本身的功能,通常是一個單獨運行的程序,“掛”其它程序的方法就是跨進程代碼注入。如果這個世界的所有軟件都是開放源代碼的,而且沒有那么多的License限制,黑客們可以自由修改代碼發布新功能,那么就不會出現外掛這東西。給別的程序做外掛是一件很麻煩的事情,並不是所有的程序都能夠“容忍”從外部注入的代碼,特別是一些程序存在內部缺陷,按照正常的Windows運行機制注入的功能通常不能達到預期的效果,甚至是造成該程序不能使用,所以如果不是實在沒有別的辦法的話,沒有人會主動使用做外掛的方式一個程序擴展功能。
    
    盡管不願意,但還是有一些程序只能通過外掛來擴展它的功能,本文提到的這個“不得不掛”的程序就是大名鼎鼎的源代碼瀏覽工具:Source Insight。Source Insight是一款....[此處省略介紹性文字字符數(不計空格)1028,非中文單詞126,中文字符或朝鮮語單詞819]。我使用VC很長時間,也許是被VC的“Tabbar”插件慣壞了,所以當我使用不能通過文件標簽切換文件的編輯器的時候就感覺非常不適應,很不幸,“Source Insight”就是這樣的。使用了“Source Insight”一段時間之后,我開始尋求為其添加一個文件標簽欄的方法,“Source Insight”功能強大,可以通過自定義命令擴展它的功能,甚至支持一種類似於C語言語法的宏語言,但是經過一段時間的研究之后,我得結論是只能通過外掛對“Source Insight”的界面進行擴展,添加一個用於文件切換的標簽欄。

    前面已經提到,不是所有的程序都能夠“容忍”外部注入的代碼,我之所以覺得“Source Insight”可以掛一下,是因為“Source Insight”使用的是標准的Windows MDI(多文檔界面)窗口,窗口之前的消息流向簡單且遵循Windows標准機制。於是一個月以后,“Source Insight”的文件標簽外掛:TabSiPlus就誕生了,在研究“Source Insight”和編寫“TabSiPlus”期間積累了一些經驗,留在自己的腦子中只會慢慢遺忘,現在把它們整理成文字和大家一起共享。

    首先介紹一下“TabSiPlus”,它的主要功能就是給“Source Insight”添加一個文件切換標簽欄,這個切換標簽欄對於使用“Source Insight”編寫代碼的人有很大的幫助,先看一些它都給“Source Insight”帶來了哪些變化:



代碼窗口下面多了一個文件標簽欄,菜單也變樣了,還加上了幾個圖標,其實菜單的底色和文字顏色都是可以改變的,文件標簽欄的顏色也是可以改變的,看看:



除此之外,還添加了C/C++文件翻轉的功能,這個可是VA的常用功能,相信大家都不陌生,這個C/C++文件翻轉功能繼承了“Tabbar for Visual C++”插件的多目錄、多擴展名搜索功能:

    從現在開始,我就通過一系列文章介紹“TabSiPlus”是怎樣一步一步的做出來的,也包括對“Source Insight”的研究過程,本篇主要介紹如何找到“Source Insight”。這是一個很重要的問題,如果不能從系統中找到正在運行的“Source Insight”,那么外掛就無從掛起了。查找系統中運行的“Source Insight”程序有很多種方法,可以遍歷系統中的所有進程,然后看看有沒有insight3.exe,並得到這個進程的句柄;也可以通過窗口枚舉,找到有“Source Insight”標志的主窗口,並獲得這個主窗口的句柄。當然還有其他的方法,這里就不一一介紹了,“TabSiPlus”采用窗口枚舉的方法,因為“Source Insight”的主窗口的類名是固定的且標題欄文字很有規律,在任何情況下都有“Source Insight”字樣,便於匹配,其實主要的原因是窗口枚舉方法簡單。

    使用Spy++工具研究“Source Insight”的主窗口,發現其窗口的類名是“si_Frame”,這是一個好兆頭,如果一個窗口的類名是類似於“Afx:400000:0:10011:10:0”就麻煩了,這是MFC主框架窗口類的典型名字,里面的那些數字是諸如進程地址,窗口圖標句柄,鼠標光標句柄格式化成的一個字符串,它是可變的,在某個系統上是一個結果,在另一個系統上可能是另一個結果。再來看看“Source Insight”主窗口的標題文字,發現無論什么情況都包含一個“Source Insight”子串,這對於我們確定這個窗口是否是“Source Insight”主窗口可以起到一個輔助判斷的作用。枚舉窗口使用EnumWindows() API,這個API使用一個回調函數,以下是回調函數的原型:

BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam)

下面是“TabSiPlus”中EnumWindowsProc()回調函數的實現:
BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam)
{
 BOOL bSuccess = TRUE;
 if(hwnd != NULL && IsSourceInsightFrameWnd(hwnd))
 {
  if(lParam)
  {
   HWND *pHwnd = (HWND *)lParam;
   *pHwnd = hwnd;
   bSuccess = FALSE;//已經找到一個Source Insight窗口,退出枚舉
  }
 }

 return bSuccess;
}
這個函數利用lParam參數將窗口句柄傳遞出來,它一次只處理一個“Source Insight”窗口,如果系統中有多個“Source Insight”運行,就會有多個“Source Insight”主窗口,這由外部機制驅動枚舉函數進行多次枚舉,保證對所有的“Source Insight”進行處理。你可能已經看出來這樣存在重復發現的問題了,是的,存在這樣的問題,不過“TabSiPlus”采用一個巧妙的方法解決了這個問題。“TabSiPlus”在Hook一個“Source Insight”窗口之后就在窗口標題欄添加一個“ with TabSiPlus”的標志,這樣就可以區分窗口是否已經處理過了。下面就是判斷一個窗口是否是“Source Insight”窗口的IsSourceInsightFrameWnd()函數:

LPCTSTR lpszSourceInsight = _T("Source Insight");
LPCTSTR lpszSiFrameWndClass = _T("si_Frame");
LPCTSTR lpszTextMark = _T(" with TabSiPlus");

BOOL IsSourceInsightFrameWnd(HWND hWnd)
{
 TCHAR szClassName[128],szTitle[256];
 
 int nRtn = GetClassName(hWnd,szClassName,128);
 if(nRtn == 0)
  return FALSE;

 nRtn = GetWindowText(hWnd,szTitle,256);
 if(nRtn == 0)
  return FALSE;

 //類名是si_Frame,並且窗口標題又含有Source  Insight,可以基本判定是一個Source Insignt窗口
 if((lstrcmp(lpszSiFrameWndClass,szClassName) == 0) && (StrStr(szTitle,lpszSourceInsight) != NULL))
 {
  if(StrStr(szTitle,lpszTextMark) != NULL)//有這個mark說明已經Hook過了,不要再騷擾source insignt窗口了
   return FALSE;

  return TRUE;
 }

 return FALSE;
}

    下面是找到一個“Source  Insight”窗口的調度函數,每次調用一次調度函數可以查詢到一個沒有被Hook過的“Source  Insight”:
HWND FindSourceInsightFrameWindow()
{
 HWND hSiFrmWnd = NULL;
 
 BOOL bRtn = ::EnumWindows(EnumWindowsProc,(LPARAM)&hSiFrmWnd);
 if(!bRtn && hSiFrmWnd != NULL)
  return hSiFrmWnd;
 else
  return NULL;
}

    最后是查找“Source  Insight”窗口並將指定的動態連接庫掛到“Source  Insight”進程中的函數:
//一次試圖查找並Hook一個Source Insighe窗口
BOOL FindAndHookSourceInsightWindow(LPCTSTR lpszHookDll)
{
 BOOL bSuccess = FALSE;
 if(lpszHookDll)
 {
  HWND hSiFrmWnd = FindSourceInsightFrameWindow();
  if(hSiFrmWnd != NULL)
  {
   bSuccess = HookSourceInsightWindow(hSiFrmWnd,lpszHookDll);
  }
 }
 return bSuccess;
}
這個函數中調用了一個重要的函數:HookSourceInsightWindow(),這個函數負責將我們的代碼注入到“Source  Insight”進程中,這涉及到代碼遠程注入的很多細節,關於代碼注入方法將在下一篇:《給Source Insight做個外掛系列之二--將本地代碼注入到Source Insight進程》中介紹,本篇到此結束。

Source Insignt文件標簽外掛:TabSiPlus的下載地址:

http://files.cnblogs.com/zhujudah/TabSiPlus_0_99b2_1510.rar

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM