作者:星軌(oRbIt)
E_Mail :inte2000@163.com
一提到外掛程式,大家肯定都不陌生,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的:
點擊下載