HOOK大法實現不修改程式程式碼給程式新增功能

hemeinvyiqiluoben發表於2018-02-08
【文章標題】: HOOK大法實現不修改程式程式碼給程式新增功能
【文章作者】: 0x18c0
【軟體名稱】: Scylla
【使用工具】: OD、Stub_PE、ResHacker
【版權宣告】: 本文原創於0x18c0, 轉載請註明作者並保持文章的完整, 謝謝!

菜鳥第一次發帖,寫的不好的地方請各位多多包含

本來是第三次培訓的作業,要給Scylla加上彈窗
@Kido 老師在上課的時候也演示了,沒什麼難度,但是按照上課的方法來搞,程式一執行就彈個視窗,太粗暴,強迫症完全不能忍好吧,所以我想在視窗上加個按鈕,等點選按鈕的時候再彈出視窗,這樣就感覺友好多了。
 
這麼做其實也不難,方法就是找到視窗的過程函式,然後改寫指令讓程式先跳到我們自己寫的函式裡,最後再跳轉回原視窗過程函式
比如@蘇紫方璇 大牛這篇帖子的方法,無原始碼給程式新增功能-記事本標題新增當前時間
但是,這麼搞太麻煩了,需要自己定位視窗過程,還要各種修改指令實現跳轉,完全不適合我這種懶人
但是,懶人有懶人的方法,俗話說的好,懶是推動科技進步的根本動力——0x18c0
這裡我已經忍不住要高喊一句——HOOK大法好!



先介紹一下方法,我修改了Scylla的匯入表,新增了一個我自己編寫的DLL,然後在DLL的DllMain函式裡下訊息hook,每當按鈕被按下時,WM_COMMAND訊息就會被hook住,從而彈出視窗。



基本方法介紹完了,下面介紹一下HOOK和DLL的基本知識
一、HOOK
        Hook,字面意思就是鉤子,是windows系統提供給開發者用來改變windows訊息處理流程的程式設計介面。hook也分好幾種,拿下面我要用到的訊息hook來舉例,每當我們按下一個視窗上的按鈕時,系統就會捕獲到一個WM_COMMAND訊息,訊息會被Windows系統傳遞給軟體提前編寫好的一個函式,這個函式叫做視窗過程函式,過程函式會根據不同的訊息做出不同的處理。但是當我們想在訊息被作業系統傳遞給過程函式處理之前先處理怎麼辦呢?這個時候就要用到訊息hook了,作業系統提供給了開發者改變訊息的能力,我們只需要呼叫相關的API,在相應的訊息上設定hook鉤子,並且告訴作業系統當訊息被鉤子鉤住的時候因該怎麼辦,作業系統就會按照我們的意願來處理訊息。
        比如我們hook按鈕被按下的訊息WM_COMMAND,並且告訴作業系統,當鉤子被觸發的時候把訊息傳遞給我自己編寫的函式HookMSG,於是我們每次按下按鈕,鉤子都會被觸發,並且作業系統會自動
呼叫HookMSG函式。
二、DLL
        DLL,動態連結庫,可以匯出變數和函式供其他程式呼叫,也可以包含資原始檔,DLL有一個DllMain函式,每當DLL被連結時都會被呼叫,並且不同的呼叫原因可以有不同的處理辦法。所以我們用Stub_PE將一個DLL新增到Scylla的倒入表裡,每次Scylla執行時作業系統都回自動載入我們新增的DLL,並且執行DllMain裡的程式碼,所以我們在DllMain裡寫上我們的hook程式碼,那麼每次Scylla執行都會被自動hook了,這樣就達到了我們的目的。



方法和基本原理都說了,下面就應該開始實戰
一、新增按鈕資源
首先得給Scylla加一個按鈕,用ResHacker開啟Scylla,在對話方塊上要新增按鈕的地方右鍵——>insert control,出現新增控制元件的視窗
 
選擇控制元件型別為BUTTON,caption這裡填寫需要顯示在按鈕上的字,最後別忘了給按鈕新增ID,ID需要和其他控制元件不同以免衝突,這裡我填寫1099
,點選ok後介面上了出現我們新增的按鈕,接下來不要忘了編譯和儲存
 


二、編寫DLL
接下來我們編寫DLL,我電腦上只有vs2013,所以我就用它了,你們也可以用其他編譯器,只要可以編寫Windows系統上的DLL就可以。
1.新建工程
選擇win32專案,填寫工程名,點選確定
 
選擇DLL和空專案,點選完成,專案建立完成,然後新增main.cpp到工程
 

2.編寫程式碼
首先編寫DllMain函式
[C++] 純文字檢視 複製程式碼
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:     //dll載入時執行
        {
                g_hModule = hModule;     //儲存控制程式碼到全域性變數
                hhk = StartHook();
                break;
        }
        case DLL_PROCESS_DETACH:     //dll解除安裝時執行
        {
                EndHook();
                break;
        }
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
                break;
        }
        return TRUE;
}

DllMain函式的ul_reason_for_call指明瞭函式被呼叫的原因,DLL_PROCESS_ATTACH指DLL被呼叫,DLL_PROCESS_DETACH指DLL被解除安裝,而case DLL_THREAD_ATTACH和case DLL_THREAD_DETACH分別表示執行緒被建立和銷燬。我們在dll載入時呼叫StartHook函式,dll解除安裝時呼叫EndHook函式,下面我們看看這兩個函式
[C++] 純文字檢視 複製程式碼
?
1
2
3
4
5
6
7
8
9
HHOOK StartHook()
{
        return SetWindowsHookEx(WH_CALLWNDPROC, HookProc, g_hModule, GetCurrentThreadId());//設定hook,型別為WH_CALLWNDPROC,過程函式為HookProc,hook當前執行緒
}
 
BOOL EndHook()
{
        return UnhookWindowsHookEx(hhk);     //結束hook
}

這兩個函式只是很簡單的封裝了倆個API,重點在於SetWindowsHookEx的引數設定,由於我們hook的是WM_COMMAND訊息,所以我們選擇hook型別為WH_CALLWNDPROC,表明當視窗過程函式被呼叫時觸發hook,並且我們是hook當前執行緒,hook過程函式在dll裡,所以第三個引數填dll模組控制程式碼或者NULL都行,第四個參賽則直接利用GetCurrentThreadId()獲取當前執行緒id
這裡有一篇文章介紹hook的型別,寫的很不錯,有興趣的可以看看http://blog.csdn.net/whatday/article/details/8006225
重點還在hook過程函式,我們需要在這裡過濾出我們需要的訊息,並編寫功能程式碼
[C++] 純文字檢視 複製程式碼
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
        if (nCode >= 0)
        {
                CWPSTRUCT* cwps = (CWPSTRUCT*)lParam;
 
                if (WM_COMMAND == cwps->message)
                {
                        INT wmId = LOWORD(cwps->wParam);
                        if (wmId == 1099)
                                DialogBoxParam(g_hModule, (LPCTSTR)IDD_DIALOG, NULL, DlgProc, NULL);
                }
        }
 
        return CallNextHookEx(hhk, nCode, wParam, lParam);
}

由於我們只需要新增按鈕彈窗功能,所以我們只過濾WM_COMMAND訊息,前面我們新增按鈕資源的時候id填寫的是1099,這裡就派上用場了。
這裡我呼叫了DialogBoxParam函式來彈出對話方塊,彈出對話方塊需要新增對話方塊資源,並且編寫過程函式。
選擇vs2013的選單項->專案->新增資源->Dialog->新建
 
vs中出現我們新建的對話方塊資源
 
我就不編輯資源了,直接開始編寫對話方塊的過程函式,這裡我沒有新增任何功能程式碼
[C++] 純文字檢視 複製程式碼
?
01
02
03
04
05
06
07
08
09
10
BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
        switch (msg)
        {
        case WM_CLOSE:
                EndDialog(hDlg, NULL);
        }
 
        return FALSE;
}

到此為止,程式碼編寫工作就完成了,但是這個dll沒有一個匯出函式,我們沒法把它加到Scylla的匯入表裡,不過我們可以寫一個空的匯出函式
[C++] 純文字檢視 複製程式碼
?
1
2
3
4
__declspec(dllexport) void EmptyFunc()
{
        //Do nothing
}

3.編譯dll
接下來我們編譯dll,將工具條上的編譯選項設為release,點選編譯。
 
如果你用的也是vs2013,這裡需要修改兩個地方的設定,否則編譯出來的dll在xp上不能執行
VS2013選單->專案->屬性->配置屬性->常規->平臺工具集->Visual Studio 2013 - Windows XP (v120_xp)
VS2013選單->專案->屬性->配置屬性->c/c++->程式碼生成->執行庫->多執行緒 (/MT)
在專案資料夾裡找到HookMSG.dll,拷貝到Scylla目錄下。

三、修改Scylla匯入表
Stub_PE載入Scylla,選擇“函式”選項卡,右鍵新增函式,選擇HookMSG.dll,選擇EmptyFunc函式,確定新增並儲存。
 



執行一下看結果,點選about按鈕,對話方塊成功彈出,大功告成。
 


寫在最後:
其實整個過程沒有什麼技術含量,懂Windows程式設計的人看一眼就懂,但是作為新手弄這些東西還是有點難,整個過程當中我也是遇到各種問題,不過結果總算是好的
發這篇帖子的目的是希望與大家共勉,同時分享一下成功的喜悅,寫的不好的地方希望大家多多包涵
最後感謝論壇提供這個學習的機會,也感謝各位講師@Hmily @Kido 的指導
同時附上HookMSG.dll的完整原始碼,連結:http://pan.baidu.com/s/1c0GvFOW  密碼: v7um
ps:其實Scylla是開源軟體,想要漢化或者改介面的可以下載原始碼自己編譯,https://github.com/NtQuery/Scylla

相關文章