引子
前些日子由于项目要求,在网上到处找资料,于无意中发现了 CodeProject 上的一篇很老的文章,文章标题为:
Three Ways to Inject Your Code into Another Process
这篇文章呢,出来很久咯,还是 03 年的文章了,可惜我弄底层弄得时间不久哦,不然应该早就看过这篇大作了,
由于是大作,而且出来的又久了,自然在网上也就到处流传咯,
所以也有人将这篇文章翻译成了中文版的,下面给出这篇大作的两个链接:
中文版:http://www.vckbase.com/document/viewdoc/?id=1886
英文版:http://www.codeproject.com/KB/threads/winspy.aspx
然后呢,这边由于老大给弄了蛮多好书过来了,其中一本就是所谓的骇客之类的东西,
虽然是繁体的,但是知识点都很不错哦,所以也拿过来看了看,就发现其中对这个远程线程的注入有很多的介绍,
而且貌似前些年的很多病毒或者木马就是通过这屁东西来隐藏的,
看着看着就来劲了,而后呢,自己就根据书中的思路,
然后再结合自己的理解,将理解整理出了代码,然后就出了这篇文章咯 !
然后注意一点的是,在 CodeProject 上的那篇文章中介绍了三种注入代码技术,
第一种就是众所周知的 Hook 了;
第二种是直接将所要执行的代码全部拷贝到宿主进程中,即代码远程注入技术;
第三种则是 DLL 的远程注入技术了,其通过在宿主进程加载自己写的另外的一个 DLL 来实现注入;
然后在我的这篇博文中,我也只是总结前人的思想,然后再加入我自己的立即,
同时由于 Hook 太常见了,常见得不行了,所以我并不会介绍 Hook 了,而只介绍后面的两种方式。
代码远程注入技术
Demo 的效果:
创建的项目为 RemoteThreadCode,即远程注入代码,其实现的功能是当运行 RemoteThreadCode.exe 时,
会在 Explorer.exe 进程中创建一个线程,而这个创建的线程功能实现很简单,
就是弹出一个消息框即 OK !
Demo 的效果展示:
当双击执行 RemoteThreadCode.exe 时,则会注入一个线程到 Explorer.exe 中
当点击确定后,注入到 Explorer.exe 中的线程执行完毕,从而 WaitForSingleObject 等待成功 !
基本思路以及所对应的代码:
1. 提升进程权限,如果权限不够的话,很容易造成 OpenProcess 失败;
1: //=====================================================================================//
2: //Name: bool AdjustProcessTokenPrivilege() //
3: // //
4: //Descripion: 提升当前进程权限 //
5: //=====================================================================================//
6: bool AdjustProcessTokenPrivilege()
7: {
8: LUID luidTmp;
9: HANDLE hToken;
10: TOKEN_PRIVILEGES tkp;
11:
12: if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
13: {
14: OutputDebugString("AdjustProcessTokenPrivilege OpenProcessToken Failed ! \n");
15:
16: return false;
17: }
18:
19: if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidTmp))
20: {
21: OutputDebugString("AdjustProcessTokenPrivilege LookupPrivilegeValue Failed ! \n");
22:
23: CloseHandle(hToken);
24:
25: return FALSE;
26: }
27:
28: tkp.PrivilegeCount = 1;
29: tkp.Privileges[0].Luid = luidTmp;
30: tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
31:
32: if(!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
33: {
34: OutputDebugString("AdjustProcessTokenPrivilege AdjustTokenPrivileges Failed ! \n");
35:
36: CloseHandle(hToken);
37:
38: return FALSE;
39: }
40: return true;
41: }
2. 确定你的宿主进程,即你所要注入代码的进程,这个其实很好办,你要是不想你的木马或者病毒被别个一下子就结束了的话,
最好是选择系统要想运行,则必须开启的那种进程,比如资源管理器进程 Explorer.exe,
Windows 子系统进程 csrss.exe 等等,但是这里注意的是,我注入 System 进程的时候造成了失败哦,
所以最好还是别拿 System 做实验,而且如果你注入失败了的话,是会造成宿主进程崩溃的,
等下一不小心把 System 进程给弄崩溃了就不好了;
1: //=====================================================================================//
2: //Name: bool ProcessIsExplorer(DWORD dwProcessId) //
3: // //
4: //Descripion: 判定一个进程是否为 Explorer 进程 //
5: //=====================================================================================//
6: bool ProcessIsExplorer(DWORD dwProcessId)
7: {
8: HANDLE hProcess;
9:
10: hProcess = NULL;
11:
12: hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
13: if(NULL == hProcess)
14: {
15: OutputErrorMessage("ProcessIsExplorer - OpenProcess Failed , Error Code Is %d , Error Message Is %s !");
16:
17: return FALSE;
18: }
19:
20: DWORD dwNameLen;
21: TCHAR pathArray[MAX_PATH];
22: ZeroMemory(pathArray, MAX_PATH);
23:
24: dwNameLen = 0;
25: dwNameLen = GetModuleFileNameEx(hProcess, NULL, pathArray, MAX_PATH);
26: if(dwNameLen == 0)
27: {
29: CloseHandle(hProcess);
30:
31: return FALSE;
32: }
33:
34: TCHAR exeNameArray[MAX_PATH];
35: ZeroMemory(exeNameArray, MAX_PATH);
36: _tsplitpath(pathArray, NULL, NULL, exeNameArray, NULL);
37:
38: string str1 = exeNameArray;
39: if((str1.compare("Explorer") == 0) || (str1.compare("explorer") == 0))
40: {
41: CloseHandle(hProcess);
42:
43: return TRUE;
44: }
45:
46: return FALSE;
47: }
3. 打开宿主进程了(我这里打开的是 Explorer.exe 进程),思路是首先变量当前系统下运行的所有的进程,
然后遍历获取到得所有的进程的 PID,再调用 ProcessIsExplorer 函数来判断这个进程是否为 Explorer.exe 进程,
如果是则记录下这个进程的 PID 就可以了,这样就获得了 Explorer.exe 进程的 PID 了,
再通过 OpenProcess 来打开这个 Explorer.exe 进程就 OK 了;
1: //提升当前进程的权限
2: AdjustProcessTokenPrivilege();
3:
4: //第一个参数为用来保存所有的进程 ID
5: //第二个参数则是第一个参数的字节数
6: //第三个参数则是写入 dwProcess 数组的字节数
7: EnumProcesses(dwProcess, sizeof(dwProcess), &dwNeeded);
8:
9: //找到 explorer.exe 进程的 ID
10: dwExplorerId = 0;
11: for(int i = 0; i < dwNeeded / sizeof(DWORD); i++)
12: {
13: if(0 != dwProcess[i])
14: {
15: if(ProcessIsExplorer(dwProcess[i]))
16: {
17: dwExplorerId = dwProcess[i];
18: break;
19: }
20: }