使用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
6 #include
7 #include
8 #include
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 }
