使用绑定或连接重定向

Windows 筛选平台 (WFP) 的连接/绑定重定向功能使应用程序层强制实施 (ALE) 标注驱动程序能够检查和重定向连接。

Windows 7 和更高版本中提供此功能。

注意 WFP 驱动程序示例中的 ClassifyFunctions_ProxyCallouts.cpp 模块包括演示连接/绑定重定向的代码。

WFP 连接重定向标注将重定向应用程序的连接请求,以便应用程序连接到代理服务,而不是原始目标。 代理服务有两个套接字:一个用于重定向的原始连接,一个用于新的代理出站连接。

WFP 重定向记录是 WFP 必须在 FWPM_LAYER_ALE_AUTH_CONNECT_REDIRECT_V4 层和 FWPM_LAYER_ALE_AUTH_CONNECT_REDIRECT_V6 层的出站代理连接上设置的不透明数据的缓冲区,因此重定向连接和原始连接在逻辑上是相关的。

绑定重定向层仅支持更改流的本地地址和端口。 连接重定向层不支持此功能。

用于重定向的层

标注驱动程序可以在以下层执行重定向,这些层称为“重定向层”:

  • FWPM_LAYER_ALE_BIND_REDIRECT_V4 (FWPS_LAYER_ALE_BIND_REDIRECT_V4)

  • FWPM_LAYER_ALE_BIND_REDIRECT_V6 (FWPS_LAYER_ALE_BIND_REDIRECT_V6)

  • FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 (FWPS_LAYER_ALE_CONNECT_REDIRECT_V4)

  • FWPM_LAYER_ALE_CONNECT_REDIRECT_V6 (FWPS_LAYER_ALE_CONNECT_REDIRECT_V6)

执行重定向的层决定了更改的效果。 连接层的更改仅影响要连接的流。 绑定层的更改会影响正在使用该套接字的所有连接。

重定向层仅可用于 Windows 7 及更高版本的 Windows。 支持这些层分类的标注驱动程序必须使用 FwpsCalloutRegister1 或更高版本进行注册,而不是较旧的 FwpsCalloutRegister0 函数。

重要

 重定向不适用于所有类型的网络流量。 以下列表显示了重定向支持的数据包类型:

  • TCP
  • UDP
  • 没有标头包含选项的原始 UDPv4
  • 原始 ICMP

执行重定向

若要重定向连接,标注驱动程序必须获取 TCP 4 元组信息的可写副本,根据需要对其进行更改,然后应用更改。 提供了一组新函数来用于获取可写层数据,并通过引擎应用这些数据。 标注驱动程序可以选择在 classifyFn 函数中以内联方式进行更改,也可以以异步方式在另一个函数中进行更改。

实施重定向的标注驱动程序必须使用 classifyFn1 或更高版本,而不是 classifyFn0,作为其分类标注函数。 若要使用 classifyFn1 或更高版本,必须调用 FwpsCalloutRegister1 或更高版本来注册标注,而不是较旧的 FwpsCalloutRegister0

若要执行内联重定向,标注驱动程序必须在其 classifyFn 实施中执行以下步骤:

  1. 调用 FwpsRedirectHandleCreate0 以获取可用于重定向 TCP 连接的句柄。 此句柄应缓存并用于所有重定向。 (对于 Windows 7 和更低版本,可省略此步骤。)

  2. 在 Windows 8 和更高版本中,必须在标注驱动程序中使用 FwpsQueryConnectionRedirectState0 函数查询连接的重定向状态。 必须执行此操作,才能防止无限重定向。

  3. 调用 FwpsAcquireClassifyHandle0 以获取将用于后续函数调用的句柄。

  4. 调用 FwpsAcquireWritableLayerDataPointer0 以获取调用 classifyFn 的层的可写数据结构。 将 writableLayerData 输出参数转换为与层对应的结构,即 FWPS_BIND_REQUEST0FWPS_CONNECT_REQUEST0

    从 Windows 8 开始,如果标注驱动程序正在重定向到本地服务,则必须调用 FwpsRedirectHandleCreate0,以填写 FWPS_CONNECT_REQUEST0 结构的 localRedirectHandle 成员,以便使本地代理正常工作。

  5. 根据需要更改层数据:

    1. 将原始目标保存在本地重定向上下文中,如以下示例所示:

      FWPS_CONNECT_REQUEST* connectRequest = redirectContext->connectRequest;
      // Replace "..." with your own redirect context size
      connectRequest->localRedirectContextSize = ...;
      // Store original destination IP/Port information in the localRedirectContext member
      connectRequest->localRedirectContext =    ExAllocatePoolWithTag(…);
      
    2. 修改远程地址,如以下示例所示:

      // Ensure we don't need to worry about crossing any of the TCP/IP stack's zones
      if(INETADDR_ISANY((PSOCKADDR)&(connectRequest->localAddressAndPort)))
      {
         INETADDR_SETLOOPBACK((PSOCKADDR)&(connectRequest->remoteAddressAndPort));
      }
      else
      {
         INETADDR_SET_ADDRESS((PSOCKADDR)&(connectRequest->remoteAddressAndPort),
                               INETADDR_ADDRESS((PSOCKADDR)&(connectRequest->localAddressAndPort)));
      }
      INETADDR_SET_PORT((PSOCKADDR)&connectRequest->remoteAddressAndPort,
                        RtlUshortByteSwap(params->proxyPort));
      
    3. 如果标注驱动程序正在重定向到本地服务,则应在 FWPS_CONNECT_REQUEST0 结构的 localRedirectTargetPID 成员中设置本地代理 PID。

    4. 如果标注驱动程序正在重定向到本地服务,则应在 FWPS_CONNECT_REQUEST0 结构的 localRedirectHandle 成员中设置 FwpsRedirectHandleCreate0 返回的重定向句柄。

  6. 调用 FwpsApplyModifiedLayerData0 以应用对数据所做的更改。

  7. 在代理服务(可能处于用户模式或内核模式)中,必须查询重定向记录和上下文,如以下示例所示:

    BYTE* redirectRecords;
    BYTE redirectContext[CONTEXT_SIZE];
    listenSock = WSASocket(…);
    result = bind(listenSock, …);
    result = listen(listenSock, …);
    clientSock = WSAAccept(listenSock, …);
    // opaque data to be set on proxy connection
    result = WSAIoctl(clientSock,
                      SIO_QUERY_WFP_CONNECTION_REDIRECT_RECORDS,
                      redirectRecords, …);
    // callout allocated data, contains original destination information
    result = WSAIoctl(clientSock,
                      SIO_QUERY_WFP_CONNECTION_REDIRECT_CONTEXT,
                      redirectContext, …);
    // extract original destination IP and port from above context
    
  8. 在代理服务(可能处于用户模式或内核模式)中,必须在代理连接套接字上设置重定向记录,如以下示例所示,以创建新的出站套接字:

    proxySock = WSASocket(…);
    result = WSAIoctl(
                 proxySock,
                 SIO_SET_WFP_CONNECTION_REDIRECT_RECORDS,
                 redirectRecords, …);
    
  9. 调用 FwpsReleaseClassifyHandle0 以释放在步骤 2 中获取的分类句柄。

  10. 调用 FwpsRedirectHandleDestroy0 以销毁在步骤 1 中获取的句柄。

若要以异步方式执行重定向,标注驱动程序必须执行以下步骤:

  1. 调用 FwpsRedirectHandleCreate0 以获取可用于重定向 TCP 连接的句柄。 (对于 Windows 7 和更低版本,可省略此步骤。)

  2. 在 Windows 8 和更高版本中,必须在标注驱动程序中使用 FwpsQueryConnectionRedirectState0 函数查询连接的重定向状态。

  3. 调用 FwpsAcquireClassifyHandle0 以获取将用于后续函数调用的句柄。 此步骤以及步骤 2 和步骤 3 在标注驱动程序的 classifyFn 标注函数中执行。

  4. 调用 FwpsPendClassify0 以将分类置于挂起状态,如以下示例所示:

    FwpsPendClassify(
            redirectContext->classifyHandle,
            0,
            &redirectContext->classifyOut);
    classifyOut->actionType = FWP_ACTION_BLOCK;
    classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
    

注意

如果要面向 Windows 7,则必须在单独的辅助角色函数中执行以下步骤。 如果要面向 Windows 8 或更高版本,则可以从 classifyFn 中执行所有异步重定向步骤,并忽略步骤 5。

  1. 将分类句柄和可写层数据发送到另一个函数进行异步处理。 其余步骤在该函数中执行,而不是在标注驱动程序的 classifyFn 实施中执行。

  2. 调用 FwpsAcquireWritableLayerDataPointer0 以获取调用 classifyFn 的层的可写数据结构。 将 writableLayerData 输出参数转换为与层对应的结构,即 FWPS_BIND_REQUEST0FWPS_CONNECT_REQUEST0

    从 Windows 8 开始,如果标注驱动程序正在重定向到本地,则必须调用 FwpsRedirectHandleCreate0,以填写 FWPS_CONNECT_REQUEST0 结构的 localRedirectHandle 成员,以便使代理正常工作。

  3. 将任何标注特定的上下文信息存储在专用上下文结构中,如以下示例所示:

    redirectContext->classifyHandle = classifyHandle;
    redirectContext->connectRequest = connectRequest;
    redirectContext->classifyOut = *classifyOut; // deep copy
    // store original destination IP, port
    
  4. 根据需要更改层数据。

  5. 调用 FwpsApplyModifiedLayerData0 以应用对数据所做的更改。 如果要在另一个标注进一步修改数据的情况下重新授权,请设置 FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS 标志。

  6. 调用 FwpsCompleteClassify0 以异步方式完成分类操作,如以下示例所示:

    FwpsCompleteClassify(
            redirectContext->classifyHandle,
            0,
            &redirectContext->classifyOut);
    classifyOut->actionType = FWP_ACTION_PERMIT;
    classifyOut->rights |= FWPS_RIGHT_ACTION_WRITE;
    
  7. 调用 FwpsReleaseClassifyHandle0 以释放在步骤 1 中获取的分类句柄。

处理来自多个标注的连接重定向

多个标注驱动程序可能会为同一流启动连接重定向。 执行连接重定向的标注应注意其他请求并相应地进行响应。

每当标注将分类挂起时,都应设置 FWPS_RIGHT_ACTION_WRITE 标志。 标注应测试 FWPS_RIGHT_ACTION_WRITE 标志,以检查标注返回操作的权限。 如果未设置此标志,标注仍可返回 FWP_ACTION_BLOCK 操作,以便否决先前标注返回的 FWP_ACTION_PERMIT 操作。

在 Windows 8 和更高版本中,标注驱动程序必须使用 FwpsQueryConnectionRedirectState0 函数,查询连接的重定向状态(以便查看标注驱动程序或其他标注驱动程序是否已修改它)。 如果标注驱动程序重定向了连接,或者该连接以前由标注驱动程序重定向过,则标注驱动程序不应执行任何操作。 否则,它还应检查本地重定向,如以下示例所示:

FwpsAcquireWritableLayerDataPointer(...,(PVOID*)&connectRequest), ...);
if(connectRequest->previousVersion->modifierFilterId != filterId)
{
    if(connectRequest->previousVersion->localRedirectHandle)
    {
        classifyOut->actionType = FWP_ACTION_PERMIT;
        classifyOut->rights &= FWPS_RIGHT_ACTION_WRITE;
        FwpsApplyModifiedLayerData(
                classifyHandle,
                (PVOID)connectRequest,
                FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS);
    }
}

如果连接到本地代理,则标注驱动程序不应尝试将其重定向。

使用连接重定向的标注驱动程序应在 ALE 授权连接层(FWPS_LAYER_ALE_AUTH_CONNECT_V4FWPS_LAYER_ALE_AUTH_CONNECT_V6)注册,并检查以下两个元数据值,了解设置 FWP_CONDITION_FLAG_IS_CONNECTION_REDIRECTED 标志的指示:

  • FWPS_METADATA_FIELD_LOCAL_REDIRECT_TARGET_PID 包含负责重定向流的进程的进程标识符。

  • FWPS_METADATA_FIELD_ORIGINAL_DESTINATION 包含流的原始目标的地址。

FWPS_CONNECT_REQUEST0 结构包含名为 localRedirectTargetPID 的成员。 若要使任何环回连接重定向有效,必须使用将负责重定向流的进程的 PID 填充此字段。 这是引擎在 ALE 授权连接层上作为 FWPS_METADATA_FIELD_LOCAL_REDIRECT_TARGET_ID 传递的相同数据。

从 Windows 8 开始,代理服务需要使用 WSAIoctl 针对代理服务的原始终结点,发出 SIO_QUERY_WFP_CONNECTION_REDIRECT_RECORDSSIO_QUERY_WFP_CONNECTION_REDIRECT_CONTEXT IOCTL。 此外,必须在新的(代理)套接字上使用 WSAIoctl 签发 SIO_SET_WFP_CONNECTION_REDIRECT_RECORDS IOCTL。

WFP 与版本无关的名称和面向特定版本的 Windows