前陣子讀到一篇關於《HOOK API入門之Hook自己程序的MessageBoxW》的博客,博客地址:http://blog.csdn.net/friendan/article/details/12222651,感覺寫的很好但這篇博客主要講的是本進程(本程序)的API HOOK那麼如何將DLL注入到遠程進程並進行API HOOK呢,好了廢話不多說直接動手實踐。
創建DLL動態庫(我是在vs2008上實現的)
新建項目
創建一個名為MyDLL(名字隨便)win32項目(我創建的是win32 DLL)點擊確定
選擇下一步
選擇DLL,並點擊完成
完成後到這個界面選擇源文件中的dllmain.cpp如下圖
這樣就已經創建好一個DLL了,創建好了應該在裡面做點什麼吧,那麼下面我們就動手實踐吧:)。
我們這次HOOK的依然是MessageBoxW這個API。
直接上代碼:
1 #include "stdafx.h" 2 3 //原函數類型定義 4 typedef int (WINAPI* MsgBoxW)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType); 5 MsgBoxW OldMsgBoxW = NULL;//指向原函數的指針 6 FARPROC pfOldMsgBoxW; //指向函數的遠指針 7 BYTE OldCode[5]; //原系統API入口代碼 8 BYTE NewCode[5]; //原系統API新的入口代碼(jmp xxxxxxxx) 9 10 HANDLE hProcess = NULL;//本程序進程句柄 11 HINSTANCE hInst = NULL;//API所在的dll文件句柄 12 13 void HookOn(); 14 void HookOff(); 15 int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) 16 { 17 HookOff();//調用原函數之前,記得先恢復HOOK呀,不然是調用不到的 18 //如果不恢復HOOK,就調用原函數,會造成死循環 19 //畢竟調用的還是我們的函數,從而造成堆棧溢出,程序崩潰。 20 21 int nRet = ::MessageBoxW(hWnd, L"哈哈,MessageBoxW被HOOK了", lpCaption, uType); 22 23 HookOn();//調用完原函數後,記得繼續開啟HOOK,不然下次會HOOK不到。 24 25 return nRet; 26 } 27 28 29 30 //開啟鉤子的函數 31 void HookOn() 32 { 33 if ( NULL == hProcess) 34 { 35 return; 36 } 37 38 DWORD dwTemp=0; 39 DWORD dwOldProtect; 40 41 //修改API函數入口前個字節為jmp xxxxxx 42 VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect); 43 WriteProcessMemory(hProcess,pfOldMsgBoxW,NewCode,5,0); 44 VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp); 45 46 } 47 48 //關閉鉤子的函數 49 void HookOff() 50 { 51 if ( NULL == hProcess) 52 { 53 return; 54 } 55 56 DWORD dwTemp=0; 57 DWORD dwOldProtect; 58 59 //恢復API函數入口前個字節 60 VirtualProtectEx(hProcess, pfOldMsgBoxW, 5, PAGE_READWRITE, &dwOldProtect); 61 WriteProcessMemory(hProcess, pfOldMsgBoxW, OldCode, 5, 0); 62 VirtualProtectEx(hProcess, pfOldMsgBoxW, 5, dwOldProtect, &dwTemp); 63 } 64 65 //獲取API函數入口前個字節 66 //舊入口前個字節保存在前面定義的字節數組BYTE OldCode[5] 67 //新入口前個字節保存在前面定義的字節數組BYTE NewCode[5] 68 void GetApiEntrance() 69 { 70 71 //獲取原API入口地址 72 HMODULE hmod = ::LoadLibrary( L"User32.dll" ); 73 OldMsgBoxW = (MsgBoxW)::GetProcAddress(hmod, "MessageBoxW"); 74 pfOldMsgBoxW = (FARPROC)OldMsgBoxW; 75 76 if (NULL == pfOldMsgBoxW) 77 { 78 MessageBox(NULL, L"獲取原API入口地址出錯", L"error!", 0); 79 return; 80 } 81 82 // 將原API的入口前個字節代碼保存到OldCode[] 83 _asm 84 { 85 lea edi,OldCode //獲取OldCode數組的地址,放到edi 86 mov esi,pfOldMsgBoxW //獲取原API入口地址,放到esi 87 cld //方向標志位,為以下兩條指令做准備 88 movsd //復制原API入口前個字節到OldCode數組 89 movsb //復制原API入口第個字節到OldCode數組 90 } 91 92 93 NewCode[0]=0xe9;//實際上xe9就相當於jmp指令 94 95 //獲取MyMessageBoxW的相對地址,為Jmp做准備 96 //int nAddr= UserFunAddr –SysFunAddr - (我們定制的這條指令的大小); 97 //Jmp nAddr; 98 //(我們定制的這條指令的大小), 這裡是,個字節嘛 99 _asm 100 { 101 lea eax,MyMessageBoxW //獲取我們的MyMessageBoxW函數地址 102 mov ebx,pfOldMsgBoxW //原系統API函數地址 103 sub eax,ebx //int nAddr= UserFunAddr –SysFunAddr 104 sub eax,5 //nAddr=nAddr-5 105 mov dword ptr [NewCode+1],eax //將算出的地址nAddr保存到NewCode後面個字節 106 //注:一個函數地址占個字節 107 } 108 109 110 //填充完畢,現在NewCode[]裡的指令相當於Jmp MyMessageBoxW 111 //既然已經獲取到了Jmp MyMessageBoxW 112 //現在該是將Jmp MyMessageBoxW寫入原API入口前個字節的時候了 113 //知道為什麼是個字節嗎? 114 //Jmp指令相當於xe9,占一個字節的內存空間 115 //MyMessageBoxW是一個地址,其實是一個整數,占個字節的內存空間 116 //int n=0x123; n占個字節和MyMessageBoxW占個字節是一樣的 117 //1+4=5,知道為什麼是個字節了吧 118 HookOn(); 119 } 120 121 BOOL APIENTRY DllMain( HMODULE hModule, 122 DWORD ul_reason_for_call, 123 LPVOID lpReserved 124 ) 125 { 126 switch (ul_reason_for_call) 127 { 128 case DLL_PROCESS_ATTACH: 129 { 130 DWORD dwPid=::GetCurrentProcessId(); 131 hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid); 132 GetApiEntrance(); 133 } 134 break; 135 case DLL_THREAD_ATTACH: 136 break; 137 case DLL_THREAD_DETACH: 138 break; 139 case DLL_PROCESS_DETACH: 140 HookOff(); 141 break; 142 } 143 return TRUE; 144 }
其中:
DWORD dwPid=::GetCurrentProcessId();
hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
這兩行的作用是得到被注入DLL進程的進程句柄。
好了DLL庫部分已經解決,那讓我看看遠程注入DLL到指定進程部分的代碼吧。
在開始之前最好將DLL先編譯出來,以便在下面的代碼中使用。
我創建的win32控制台應用程序來測試。
直接上代碼:)。
1 // exe.cpp : 定義控制台應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <windows.h> 6 #include <TlHelp32.h> 7 #include <iostream> 8 #include <time.h> 9 10 BOOL InjectDllToRemoteProcess(const char* lpDllName, const char* lpPid, const char* lpProcName); 11 12 int main(int argc, char* argv[]) 13 { 14 PROCESS_INFORMATION pi; 15 STARTUPINFO si; 16 memset(&si,0,sizeof(si)); 17 si.cb=sizeof(si); 18 si.wShowWindow=SW_SHOW; 19 si.dwFlags=STARTF_USESHOWWINDOW; 20 BOOL fRet=CreateProcess(_T("C:\\Users\\Administrator\\Desktop\\遠程進程注入與HOOKapi例子\\exe\\ASD.exe"),NULL,NULL,FALSE ,NULL,NULL,NULL,NULL,&si,&pi); 21 //創建一個進程,這個進程可以是你自己寫的MFC程序。 22 23 if (!fRet) 24 { 25 //創建進程失敗 26 MessageBoxW(NULL,L"創建進程失敗",L"error",MB_OK); 27 28 } 29 30 BOOL isInject = InjectDllToRemoteProcess("C:\\Users\\Administrator\\Desktop\\遠程進程注入與HOOKapi例子\\exe\\MyDLL.dll", NULL , "ASD.exe"); 31 // C:\\Users\\Administrator\\Desktop\\遠程進程注入與HOOKapi例子\\exe\\MyDLL.dll這個的DLL的路徑 32 // ASD.exe是要注入的進程名,可以寫一個MFC對話框程序在上面添加個按鈕點擊按鈕彈出MessageBox看看你的MessageBox是不是被HOOK住了 33 34 if (!isInject) 35 { 36 //注入遠程進程失敗 37 MessageBoxW(NULL,L"注入遠程進程失敗",L"error",MB_OK); 38 } 39 40 while(1) 41 { 42 43 } 44 45 return 0; 46 } 47 //進程快照(枚舉各進程) 48 BOOL GetPidByProcessName(LPCTSTR lpszProcessName , DWORD &dwPid) 49 { 50 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 51 if ( INVALID_HANDLE_VALUE == hSnapshot ) 52 { 53 return FALSE; 54 } 55 56 PROCESSENTRY32 pe; 57 pe.dwSize = sizeof(PROCESSENTRY32); 58 if ( !Process32First(hSnapshot, &pe) ) 59 { 60 ::CloseHandle(hSnapshot); 61 return FALSE; 62 } 63 64 while ( Process32Next(hSnapshot, &pe) ) 65 { 66 if ( !_stricmp(lpszProcessName, pe.szExeFile) ) 67 { 68 ::CloseHandle(hSnapshot); 69 dwPid = pe.th32ProcessID; 70 return TRUE; 71 } 72 } 73 74 ::CloseHandle(hSnapshot); 75 return FALSE; 76 } 77 78 /********************************************************************************************************/ 79 80 //注入DLL到遠程進程 81 BOOL InjectDllToRemoteProcess(const char* lpDllName, const char* lpPid, const char* lpProcName) 82 { 83 DWORD dwPid = 0; 84 if (NULL == lpPid || 0 == strlen(lpPid)) 85 { 86 if (NULL != lpProcName && 0 != strlen(lpProcName)) 87 { 88 if (!GetPidByProcessName(lpProcName, dwPid)) 89 { 90 return FALSE; 91 } 92 } 93 else 94 { 95 return FALSE; 96 } 97 } 98 else 99 { 100 dwPid = atoi(lpPid); 101 } 102 103 104 //根據Pid得到進程句柄(注意必須權限) 105 HANDLE hRemoteProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, dwPid); 106 if (INVALID_HANDLE_VALUE == hRemoteProcess) 107 { 108 return FALSE; 109 } 110 111 //計算DLL路徑名需要的內存空間 112 DWORD dwSize = (1 + lstrlenA(lpDllName)) * sizeof(char); 113 114 //使用VirtualAllocEx函數在遠程進程的內存地址空間分配DLL文件名緩沖區,成功返回分配內存的首地址. 115 LPVOID lpRemoteBuff = (char *)VirtualAllocEx(hRemoteProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE); 116 if (NULL == lpRemoteBuff) 117 { 118 CloseHandle(hRemoteProcess); 119 return FALSE; 120 } 121 122 //使用WriteProcessMemory函數將DLL的路徑名復制到遠程進程的內存空間,成功返回TRUE. 123 DWORD dwHasWrite = 0; 124 BOOL bRet = WriteProcessMemory(hRemoteProcess, lpRemoteBuff, lpDllName, dwSize, &dwHasWrite); 125 if (!bRet || dwHasWrite != dwSize) 126 { 127 VirtualFreeEx(hRemoteProcess, lpRemoteBuff, dwSize, MEM_COMMIT); 128 CloseHandle(hRemoteProcess); 129 return FALSE; 130 } 131 132 //創建一個在其它進程地址空間中運行的線程(也稱:創建遠程線程),成功返回新線程句柄. 133 //注意:進程句柄必須具備PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE,和PROCESS_VM_READ訪問權限 134 DWORD dwRemoteThread = 0; 135 //LPTHREAD_START_ROUTINE pfnLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA"); 136 //HANDLE hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, pfnLoadLibrary, lpRemoteBuff, 0, &dwRemoteThread); 137 HANDLE hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, lpRemoteBuff, 0, &dwRemoteThread); 138 if (INVALID_HANDLE_VALUE == hRemoteThread) 139 { 140 VirtualFreeEx(hRemoteProcess, lpRemoteBuff, dwSize, MEM_COMMIT); 141 CloseHandle(hRemoteProcess); 142 return FALSE; 143 } 144 145 //注入成功釋放句柄 146 WaitForSingleObject(hRemoteThread, INFINITE); 147 CloseHandle(hRemoteThread); 148 CloseHandle(hRemoteProcess); 149 150 151 //補充:卸載過程(有bug) 152 //准備卸載之前注入的Dll 153 //DWORD dwHandle, dwID; 154 //LPVOID pFunc = GetModuleHandleA; //獲得在遠程線程中被注入的Dll的句柄 155 //HANDLE hThread = CreateRemoteThread(hRemoteProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, lpRemoteBuff, 0, &dwID); 156 //WaitForSingleObject(hThread, INFINITE); 157 //GetExitCodeThread(hThread, &dwHandle); //線程的結束碼即為Dll模塊兒的句柄 158 //CloseHandle(hThread); 159 //pFunc = FreeLibrary; 160 //hThread = CreateRemoteThread(hThread, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, (LPVOID)dwHandle, 0, &dwID); //將FreeLibraryA注入到遠程線程中去卸載Dll 161 //WaitForSingleObject(hThread, INFINITE); 162 //CloseHandle(hThread); 163 //CloseHandle(hRemoteProcess); 164 165 return TRUE; 166 }
代碼的注釋還是很清楚的,就不解釋了。