使用VC++通过远程进程注入来实现HOOK指定进程的某个API

前阵子读到一篇关于《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 }

代码的注释还是很清楚的,就不解释了。

原文地址:https://www.cnblogs.com/ives-lu/p/4759660.html