程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 使用VC++通過遠程進程注入來實現HOOK指定進程的某個API,vchook

使用VC++通過遠程進程注入來實現HOOK指定進程的某個API,vchook

編輯:C++入門知識

使用VC++通過遠程進程注入來實現HOOK指定進程的某個API,vchook


前陣子讀到一篇關於《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 }

 

代碼的注釋還是很清楚的,就不解釋了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved