日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HOOK学习笔记与心得

發布時間:2023/12/4 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HOOK学习笔记与心得 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、??Hook介紹
鉤子(Hook),是Windows消息處理機制的一個平臺,應用程序可以在上面設置子程以監視指定窗口的某種消息,而且所監視的窗口可以是其他進程所創建的。當消息到達后,在目標窗口處理函數之前處理它。鉤子機制允許應用程序截獲處理window消息或特定事件。
?我們知道Windows系統API函數都是被封裝到DLL中,在某個應用程序要調用一個API函數的時候,如果這個函數所在的DLL沒有被加載到本進程中則加載它,然后保存當前環境(各個寄存器和函數調用完后的返回地址等)。接著程序會跳轉到這個API函數的入口地址去執行此處的指令。由此看來,我們想在調用真正的API之前先調用我們的函數,那么可以修改這個API函數的入口處的代碼,使他先跳轉到我們的函數地址,然后在我們的函數最后再調用原來的API函數。
簡單來說HOOK?API?可以理解成對程序將要執行系統函數的一個攔截,?攔截后執行自己寫的代碼以達到完成某種特定的目的,再恢復程序繼續執行,很多PJ中的Patch?機器碼,盜號木馬等都是用這個方法。
二、??Hook?API實戰
OK,既然我們需要實現一個這樣的一個HOOK。當然需要兩樣東西,一是目標程序,一是我們的代碼。
1.??程序:
新建一個dll工程文件目錄如圖:
附件 93152
我們自己新建一個Add.def文件,然后添加到工程中即可,如圖我是添加到Source?Files里。
跟exe有個main或者WinMain入口函數一樣,DLL也有它自己的一個入口函數,就是DllMain。
我們打開dllmain.cpp??代碼如下:

代碼:

// dllmain.cpp : Defines the entry point for the DLL application. #include "stdafx.h"int WINAPI add(int a, int b) {return a + b; }BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved) {return TRUE; }


DEF文件是模塊定義文件,模塊定義?(.def)?文件為鏈接器提供有關被鏈接程序的導出、屬性及其他方面的信息。生成?DLL?時,.def?文件最有用。由于存在可代替模塊定義語句使用的鏈接器選項,通常不需要?.def?文件。也可以將?__declspec(dllexport)?用作指定導出函數的手段。在鏈接器階段可以使用?/DEF(指定模塊定義文件)鏈接器選項調用?.def?文件。如果生成的?.exe?文件沒有導出,使用?.def?文件將使輸出文件較大并降低加載速度。
??在VC++中,生成DLL可以不使用.def文件。只需要在VC++的函數定義前要加__declspec(dllexport)修飾就可以了。但是使用__declspec(dllexport)和使用.def文件是有區別的。如果DLL是提供給VC++用戶使用的,你只需要把編譯DLL時產生的.lib提供給用戶,它可以很輕松地調用你的DLL。但是如果你的DLL是供其他程序如VB、delphi,以及.NET用戶使用的,那么會產生一個小麻煩。因為VC++對于__declspec(dllexport)聲明的函數會進行名稱轉換,如下面的函數:
__declspec(dllexport)?int?__stdcallIsWinNT()
會轉換為IsWinNT@0,這樣你在VB中必須這樣聲明:
Declare?Function?IsWinNT?Lib?"my.dll"?Alias?"IsWinNT@0"?()?As?Long
@的后面的數由于參數類型不同而可能不同。這顯然不太方便。所以如果要想避免這種轉換,就要使用.def文件方式。
EXPORTS后面的數可以不給,系統會自動分配一個數。對于VB、PB、Delphi用戶,通常使用按名稱進行調用的方式,這個數關系不大,但是對于使用.lib鏈接的VC程序來說,不是按名稱進行調用,而是按照這個數進行調用的,所以最好給出。
??.def?文件中的第一條?LIBRARY?語句不是必須的,但LIBRARY?語句后面的?DLL?的名稱必須正確,即與生成的動態鏈接庫的名稱必須匹配。此語句將?.def?文件標識為屬于?DLL。鏈接器將此名稱放到?DLL?的導入庫中。
EXPORTS?語句列出名稱,可能的話還會列出?DLL?導出函數的序號值。通過在函數名的后面加上?@?符和一個數字,給函數分配序號值。當指定序號值時,序號值的范圍必須是從?1?到?N,其中?N?是?DLL?導出函數的個數。
LIBRARY?BTREE
EXPORTS
Insert?@1
Delete?@2
Member?@3
Min?@4
如果使用?MFC?DLL?向導創建?MFC?DLL,則向導將為您創建主干?.def?文件并將其自動添加到項目中。添加要導出到此文件的函數名。對于非?MFC?DLL,必須親自創建?.def?文件并將其添加到項目中。
如果導出?C++?文件中的函數,必須將修飾名放到?.def?文件中,或者通過使用外部“C”定義具有標準?C?鏈接的導出函數。如果需要將修飾名放到?.def?文件中,則可以通過使用?DUMPBIN?工具或?/MAP?鏈接器選項來獲取修飾名。請注意,編譯器產生的修飾名是編譯器特定的。如果將?Visual?C++?編譯器產生的修飾名放到?.def?文件中,則鏈接到?DLL?的應用程序必須也是用相同版本的?Visual?C++?生成的,這樣調用應用程序中的修飾名才能與?DLL?的?.def?文件中的導出名相匹配。
因此def文件代碼如下:

代碼:

LIBRARY Add DESCRIPTION "ADD LA" EXPORTS add @1;
void CMFCApplication5Dlg::OnBnClickedButton1() {// TODO: Add your control notification handler code hereHINSTANCE hAddDll = NULL;typedef int (WINAPI*AddProc)(int a, int b);//函數原型定義 AddProc add;if (hAddDll == NULL){hAddDll = ::LoadLibrary(_T("Win32DLL.dll"));//加載dll }add = (AddProc)::GetProcAddress(hAddDll, "add");//獲取函數add地址 int a = 123;int b = 456;int c = add(a, b); CString tem;tem.Format(_T("%d+%d=%d"), a, b, c);AfxMessageBox(tem);}
代碼:

// Hook.cpp : Defines the initialization routines for the DLL. #include "stdafx.h" #include "Hook.h"#ifdef _DEBUG #define new DEBUG_NEW #endif //變量定義 #pragma data_seg("SHARED") //不同Instance共享的該變量 static HHOOK hhk = NULL; //鼠標鉤子句柄 static HINSTANCE hinst = NULL; //本dll的實例句柄 (hook.dll) #pragma data_seg() #pragma comment(linker, "/section:SHARED,rws") //以上的變量為共享 CString temp; //用于顯示錯誤的臨時變量 bool bHook = false; //是否Hook了函數 bool m_bInjected = false; //是否對API進行了Hook BYTE OldCode[5]; //原程序API入口代碼 BYTE NewCode[5]; //新跳轉的API代碼 (jmp xxxx) typedef int (WINAPI*AddProc)(int a, int b);//add.dll中的add函數定義 AddProc add; //add.dll中的add函數 HANDLE hProcess = NULL; //所處進程的句柄 FARPROC pfadd; //指向add函數的遠指針 DWORD dwPid; //所處進程ID //end of 變量定義 // CHookAppBEGIN_MESSAGE_MAP(CHookApp, CWinApp) END_MESSAGE_MAP()//鼠標鉤子過程,什么事情也不做,目的是注入dll到程序中 LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {return CallNextHookEx(hhk, nCode, wParam, lParam); } //開啟鉤子的函數 void HookOn() {ASSERT(hProcess != NULL);DWORD dwTemp = 0;DWORD dwOldProtect;//將內存保護模式改為可寫,老模式保存入dwOldProtect VirtualProtectEx(hProcess, pfadd, 5, PAGE_READWRITE, &dwOldProtect);//將所屬進程中add()的前5個字節改為Jmp Myadd WriteProcessMemory(hProcess, pfadd, NewCode, 5, 0);//將內存保護模式改回為dwOldProtect VirtualProtectEx(hProcess, pfadd, 5, dwOldProtect, &dwTemp);bHook = true; } //關閉鉤子的函數 void HookOff()//將所屬進程中add()的入口代碼恢復 {ASSERT(hProcess != NULL);DWORD dwTemp = 0;DWORD dwOldProtect;VirtualProtectEx(hProcess, pfadd, 5, PAGE_READWRITE, &dwOldProtect);WriteProcessMemory(hProcess, pfadd, OldCode, 5, 0);VirtualProtectEx(hProcess, pfadd, 5, dwOldProtect, &dwTemp);bHook = false; } //然后,寫我們自己的Myadd()函數 int WINAPI Myadd(int a, int b) {//截獲了對add()的調用,我們給a,b都加上一定的數a = a + 987;b = b + 654;HookOff();//關掉Myadd()鉤子防止死循環 int ret;ret = add(a, b);HookOn();//開啟Myadd()鉤子 return ret; } //好,最重要的HOOK函數: void Inject() {if (m_bInjected == false){ //保證只調用1次 m_bInjected = true;//獲取add.dll中的add()函數 HMODULE hmod = ::LoadLibrary(_T("Win32DLL.dll"));add = (AddProc)::GetProcAddress(hmod, "add");pfadd = (FARPROC)add;if (pfadd == NULL){AfxMessageBox(L"cannot locate add()");}// 將add()中的入口代碼保存入OldCode[] _asm{lea edi, OldCodemov esi, pfaddcldmovsdmovsb}NewCode[0] = 0xe9;//實際上0xe9就相當于jmp指令 //獲取Myadd()的相對地址 _asm{lea eax, Myaddmov ebx, pfaddsub eax, ebxsub eax, 5mov dword ptr[NewCode + 1], eax}//填充完畢,現在NewCode[]里的指令相當于Jmp Myadd HookOn(); //可以開啟鉤子了 } }CHookApp::CHookApp() {} CHookApp theapp;//鼠標鉤子安裝函數: BOOL InstallHook() {hhk = ::SetWindowsHookEx(WH_MOUSE, MouseProc, hinst, 0);return true; }//卸載鼠標鉤子函數 void UninstallHook() {::UnhookWindowsHookEx(hhk); }//在dll實例化中獲得一些參數 BOOL CHookApp::InitInstance() {CWinApp::InitInstance();//獲得dll 實例,進程句柄 hinst = ::AfxGetInstanceHandle();DWORD dwPid = ::GetCurrentProcessId();hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, dwPid);//調用注射函數 Inject();return TRUE; }

值得一提的是很多人都是改API開頭的5個字節,但是現在很多殺毒軟件用這樣的方法檢查API是否被HOOK,或其他病毒木馬在你之后又改了前5個字節,這樣就會互相覆蓋,最后一個HOOK?API的操作才是有效的。掛鉤的方法很多,這里只是最基礎的一種。希望大家多多補充。多多指正。
了解好以下的問題能夠更好的提升自己的HOOK水平:
1.CPU指令長度問題,在32位系統里,一條JMP/CALL指令的長度是5個字節,因此你只有替換API里超過5個字節長度的機器碼(或者替換幾條指令長度加起來是5字節的指令),否則會影響被更改的小于5個字節的機器碼后面的數條指令,甚至程序流程會被打亂,產生不可預料的后果;
2.參數問題,為了訪問原API的參數,你要通過EBP或ESP來引用參數,因此你要非常清楚你的HOOK代碼里此時的EBP/ESP的值是多少;
3.時機的問題,有些HOOK必須在API的開頭,有些必須在API的尾部,比如HOOK?CreateFilaA(),如果你在API尾部HOOK?API,那么此時你就不能寫文件,甚至不能訪問文件;HOOK?RECV(),如果你在API頭HOOK,此時還沒有收到數據,你就去查看RECV()的接收緩沖區,里面當然沒有你想要的數據,必須等RECV()正常執行后,在RECV()的尾部HOOK,此時去查看RECV()的緩沖區,里面才有想要的數據;
4.上下文的問題,有些HOOK代碼不能執行某些操作,否則會破壞原API的上下文,原API就失效了;
5.同步問題,在HOOK代碼里盡量不使用全局變量,而使用局部變量,這樣也是模塊化程序的需要;
6.最后要注意的是,被替換的CPU指令的原有功能一定要在HOOK代碼的某個地方模擬實現。

總結

以上是生活随笔為你收集整理的HOOK学习笔记与心得的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。