前阵子读到一篇关于《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 }
代码的注释还是很清楚的,就不解释了。