給Source Insight做個外掛之一--發現Source Insight

吹泡泡的小貓發表於2006-08-20

  作者:星軌(oRbIt)
  E_Mail :inte2000@163.com   

Google

    一提到外掛程式,大家肯定都不陌生,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的下載地址:
點選下載

相關文章