這里我們要用到上篇中的那個(gè) msg.dll
下面的代碼還是使用的統(tǒng)一的寬字符版本,已經(jīng)在VS2010下編譯通過。
首先上代碼,然后慢慢解釋:
- #include <windows.h>
- #include <TlHelp32.h>
-
- BOOL LoadRemoteDll(DWORD dwProcessId, LPTSTR lpszLibName)
- {
- BOOL bResult = FALSE;
- HANDLE hProcess = NULL;
- HANDLE hThread = NULL;
- PSTR pszLibFileRemote = NULL;
- DWORD cch;
- PTHREAD_START_ROUTINE pfnThreadRrn;
- __try
- {
- //獲得想要注入代碼的進(jìn)程的句柄
- hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
- if (NULL == hProcess)
- __leave;
- //計(jì)算DLL路徑名需要的字節(jié)數(shù)
- cch = 2 * (1 + lstrlen(lpszLibName));
- //在遠(yuǎn)程線程中為路徑名分配空間
- pszLibFileRemote = (PSTR)VirtualAllocEx(hProcess, NULL, cch, MEM_COMMIT, PAGE_READWRITE);
-
- if (pszLibFileRemote == NULL)
- __leave;
- //將DLL的路徑名復(fù)制到遠(yuǎn)程進(jìn)程的地址空間
- if (!WriteProcessMemory(hProcess, (PVOID)pszLibFileRemote, (PVOID)lpszLibName, cch, NULL))
- __leave;
- //獲得LoadLibraryA在Kernel.dll中得真正地址
- pfnThreadRrn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
- if (pfnThreadRrn == NULL)
- __leave;
-
- hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRrn, (PVOID)pszLibFileRemote, 0, NULL);
- if (hThread == NULL)
- __leave;
- //等待遠(yuǎn)程線程終止
- WaitForSingleObject(hThread, INFINITE);
- bResult = TRUE;
- }
- __finally
- {
- //關(guān)閉句柄
- if (pszLibFileRemote != NULL)
- VirtualFreeEx(hProcess, (PVOID)pszLibFileRemote, 0, MEM_RELEASE);
- if (hThread != NULL)
- CloseHandle(hThread);
- if (hProcess != NULL)
- CloseHandle(hProcess);
- }
- return bResult;
- }
-
- BOOL GetProcessIdByName(LPWSTR 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);
-
- //遍歷進(jìn)程
- hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- if (hSnapshot == INVALID_HANDLE_VALUE)
- return FALSE;
- if (!Process32First(hSnapshot, &ps))
- return FALSE;
-
- do
- {
- //比較進(jìn)程名
- if (lstrcmpi(ps.szExeFile, TEXT("calc.exe")) == 0)
- {
- //找到了
- *lpPID = ps.th32ProcessID;
- CloseHandle(hSnapshot);
- return TRUE;
- }
- }
- while (Process32Next(hSnapshot, &ps));
- //沒有找到
- CloseHandle(hSnapshot);
- return FALSE;
- }
-
- //修改進(jìn)程權(quán)限
- BOOL EnablePrivilege(LPWSTR name)
- {
- HANDLE hToken;
- BOOL rv;
- TOKEN_PRIVILEGES priv = {1, {0, 0, SE_PRIVILEGE_ENABLED}};
- LookupPrivilegeValue(0, name, &priv.Privileges[0].Luid);
- OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
- AdjustTokenPrivileges(hToken, FALSE, &priv, sizeof priv, 0, 0);
- rv = GetLastError() == ERROR_SUCCESS;
- CloseHandle(hToken);
- return rv;
- }
-
- int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
- {
- DWORD dwPID;
- //提權(quán),獲取SE_DEBUG_NAME 權(quán)限
- //可以在其他進(jìn)程的內(nèi)存空間中寫入,創(chuàng)建線程
- if (0 != EnablePrivilege(SE_DEBUG_NAME))
- return 0;
- if (!GetProcessIdByName(TEXT("calc.exe"), &dwPID))
- return 0;
- //通過上傳遠(yuǎn)程線程加載dll
- //將msg.dll放置在系統(tǒng)目錄下
- if (!LoadRemoteDll(dwPID, TEXT("msg.dll")))
- return 0;
- return 1;
- }
把上次的dll放到相對目錄下面或者是扔到system32下面。
我們首先從main函數(shù)開始吧:
主要是由三個(gè)部分組成的:
1.提取權(quán)限
- //提權(quán),獲取SE_DEBUG_NAME 權(quán)限
- //可以在其他進(jìn)程的內(nèi)存空間中寫入,創(chuàng)建線程
- if (0 != EnablePrivilege(SE_DEBUG_NAME))
- return 0;
2.遍歷搜索進(jìn)程UID
- if (!GetProcessIdByName(TEXT("calc.exe"), &dwPID))
- return 0;
3.創(chuàng)建遠(yuǎn)程線程
- //通過上傳遠(yuǎn)程線程加載dll
- //將msg.dll放置在系統(tǒng)目錄下
- if (!LoadRemoteDll(dwPID, TEXT("msg.dll")))
- return 0;
前兩個(gè)步驟我們就不具體講了,不是我們的重點(diǎn)。關(guān)鍵是其實(shí)對于進(jìn)程令牌Token自己也不是很理解,不好意思給大家講。。。
關(guān)鍵我們還是來看看第三個(gè)步驟好了。
創(chuàng)建遠(yuǎn)程線程句柄有下面幾個(gè)步驟:
1.獲得想要注入代碼的進(jìn)程的句柄
- hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
2.計(jì)算DLL路徑名需要的字節(jié)數(shù)
- cch = 2 * (1 + lstrlen(lpszLibName));
這里自己對寬字符的處理沒有做好
可以使用wcslen函數(shù)
3.在遠(yuǎn)程線程中為路徑名分配空間
- pszLibFileRemote = (PSTR)VirtualAllocEx(hProcess, NULL, cch, MEM_COMMIT, PAGE_READWRITE);
4.將DLL的路徑名復(fù)制到遠(yuǎn)程進(jìn)程的地址空間
- WriteProcessMemory(hProcess, (PVOID)pszLibFileRemote, (PVOID)lpszLibName, cch, NULL);
5.獲得LoadLibraryA在Kernel.dll中得真正地址
- pfnThreadRrn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
6.最后是最最關(guān)鍵的函數(shù)
- hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRrn, (PVOID)pszLibFileRemote, 0, NULL);
7.當(dāng)然還有掃尾的工作
- //關(guān)閉句柄
- if (pszLibFileRemote != NULL)
- VirtualFreeEx(hProcess, (PVOID)pszLibFileRemote, 0, MEM_RELEASE);
- if (hThread != NULL)
- CloseHandle(hThread);
- if (hProcess != NULL)
- CloseHandle(hProcess);
運(yùn)行一下,發(fā)現(xiàn)成功了!~~~
最后棟哥跟我提醒了幾點(diǎn):
1.關(guān)于進(jìn)程令牌的代碼,有時(shí)候不寫也是可以通過的。這里還有待研究一下。
2.被注入的程序有時(shí)候要放在相對目錄下才會(huì)產(chǎn)生效果。關(guān)于這點(diǎn)自己還沒有實(shí)踐過。有待驗(yàn)證。
3.像explorer.exe有時(shí)候會(huì)無法注入。換個(gè)簡單的calc.exe完全可以?;鸷?,qq全部都可以??赡苁莈xplorer.exe有點(diǎn)特別。
4.第一次遠(yuǎn)程注入可以成功,第二次就不行了,就不能彈出對話框了,貌似進(jìn)程打開之后馬上就關(guān)閉了。個(gè)人以為原因可能處在dll的fdwReason。有時(shí)間嘗試著調(diào)試一下。
自己改了一下,應(yīng)該是這樣的。上面的代碼里面沒有FreeLibrary,那么dll還是在遠(yuǎn)程地址空間里面。第二次想要注入的時(shí)候是不會(huì)發(fā)送DLL_PROCESS_ATTACH 消息的。
DLL_PROCESS_ATTACH消息的發(fā)送是這樣的:
線程調(diào)用LoadLibrary --> Dll是否已經(jīng)被映射到了進(jìn)程的地址空間中 (如果是) --> 遞增DLL的使用計(jì)數(shù) --> 使用計(jì)數(shù)器是否等于1 (如果是) --> 調(diào)用DLL的DllMain并且傳入DLL_PROCESS_ATTACH
所以說,在Dll已經(jīng)被映射到了進(jìn)程地址空間之后,只不過DLL的使用計(jì)數(shù)增加了,如果是2,那么很明顯就不會(huì)調(diào)用DLL的DllMain并且傳入DLL_PROCESS_ATTACH
但是如果我把dll改一下,把彈框代碼寫在DLL_THREAD_ATTACH里面,那么每次創(chuàng)建線程的時(shí)候就都會(huì)收到DLL_THREAD_ATTACH消息了。
關(guān)于遠(yuǎn)程線程注入就先講到這里。當(dāng)然這些都是最簡單的最基本的東西。
自己有時(shí)間去看看核心編程,順便進(jìn)一步了解下操作系統(tǒng)。