使用绑定或连接重定向
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 实施中执行以下步骤:
调用 FwpsRedirectHandleCreate0 以获取可用于重定向 TCP 连接的句柄。 此句柄应缓存并用于所有重定向。 (对于 Windows 7 和更低版本,可省略此步骤。)
在 Windows 8 和更高版本中,必须在标注驱动程序中使用 FwpsQueryConnectionRedirectState0 函数查询连接的重定向状态。 必须执行此操作,才能防止无限重定向。
调用 FwpsAcquireClassifyHandle0 以获取将用于后续函数调用的句柄。
调用 FwpsAcquireWritableLayerDataPointer0 以获取调用 classifyFn 的层的可写数据结构。 将 writableLayerData 输出参数转换为与层对应的结构,即 FWPS_BIND_REQUEST0 或 FWPS_CONNECT_REQUEST0。
从 Windows 8 开始,如果标注驱动程序正在重定向到本地服务,则必须调用 FwpsRedirectHandleCreate0,以填写 FWPS_CONNECT_REQUEST0 结构的 localRedirectHandle 成员,以便使本地代理正常工作。
根据需要更改层数据:
将原始目标保存在本地重定向上下文中,如以下示例所示:
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(…);
修改远程地址,如以下示例所示:
// 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));
如果标注驱动程序正在重定向到本地服务,则应在 FWPS_CONNECT_REQUEST0 结构的 localRedirectTargetPID 成员中设置本地代理 PID。
如果标注驱动程序正在重定向到本地服务,则应在 FWPS_CONNECT_REQUEST0 结构的 localRedirectHandle 成员中设置 FwpsRedirectHandleCreate0 返回的重定向句柄。
调用 FwpsApplyModifiedLayerData0 以应用对数据所做的更改。
在代理服务(可能处于用户模式或内核模式)中,必须查询重定向记录和上下文,如以下示例所示:
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
在代理服务(可能处于用户模式或内核模式)中,必须在代理连接套接字上设置重定向记录,如以下示例所示,以创建新的出站套接字:
proxySock = WSASocket(…); result = WSAIoctl( proxySock, SIO_SET_WFP_CONNECTION_REDIRECT_RECORDS, redirectRecords, …);
调用 FwpsReleaseClassifyHandle0 以释放在步骤 2 中获取的分类句柄。
调用 FwpsRedirectHandleDestroy0 以销毁在步骤 1 中获取的句柄。
若要以异步方式执行重定向,标注驱动程序必须执行以下步骤:
调用 FwpsRedirectHandleCreate0 以获取可用于重定向 TCP 连接的句柄。 (对于 Windows 7 和更低版本,可省略此步骤。)
在 Windows 8 和更高版本中,必须在标注驱动程序中使用 FwpsQueryConnectionRedirectState0 函数查询连接的重定向状态。
调用 FwpsAcquireClassifyHandle0 以获取将用于后续函数调用的句柄。 此步骤以及步骤 2 和步骤 3 在标注驱动程序的 classifyFn 标注函数中执行。
调用 FwpsPendClassify0 以将分类置于挂起状态,如以下示例所示:
FwpsPendClassify( redirectContext->classifyHandle, 0, &redirectContext->classifyOut); classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
注意
如果要面向 Windows 7,则必须在单独的辅助角色函数中执行以下步骤。 如果要面向 Windows 8 或更高版本,则可以从 classifyFn 中执行所有异步重定向步骤,并忽略步骤 5。
将分类句柄和可写层数据发送到另一个函数进行异步处理。 其余步骤在该函数中执行,而不是在标注驱动程序的 classifyFn 实施中执行。
调用 FwpsAcquireWritableLayerDataPointer0 以获取调用 classifyFn 的层的可写数据结构。 将 writableLayerData 输出参数转换为与层对应的结构,即 FWPS_BIND_REQUEST0 或 FWPS_CONNECT_REQUEST0。
从 Windows 8 开始,如果标注驱动程序正在重定向到本地,则必须调用 FwpsRedirectHandleCreate0,以填写 FWPS_CONNECT_REQUEST0 结构的 localRedirectHandle 成员,以便使代理正常工作。
将任何标注特定的上下文信息存储在专用上下文结构中,如以下示例所示:
redirectContext->classifyHandle = classifyHandle; redirectContext->connectRequest = connectRequest; redirectContext->classifyOut = *classifyOut; // deep copy // store original destination IP, port
根据需要更改层数据。
调用 FwpsApplyModifiedLayerData0 以应用对数据所做的更改。 如果要在另一个标注进一步修改数据的情况下重新授权,请设置 FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS 标志。
调用 FwpsCompleteClassify0 以异步方式完成分类操作,如以下示例所示:
FwpsCompleteClassify( redirectContext->classifyHandle, 0, &redirectContext->classifyOut); classifyOut->actionType = FWP_ACTION_PERMIT; classifyOut->rights |= FWPS_RIGHT_ACTION_WRITE;
调用 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_V4 或 FWPS_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_RECORDS 和 SIO_QUERY_WFP_CONNECTION_REDIRECT_CONTEXT IOCTL。 此外,必须在新的(代理)套接字上使用 WSAIoctl 签发 SIO_SET_WFP_CONNECTION_REDIRECT_RECORDS IOCTL。