第2篇:Windows 上劫持流量的 N 种姿势,以及我为何选了最痛的那种

📅 2026/6/30 23:37:40
第2篇:Windows 上劫持流量的 N 种姿势,以及我为何选了最痛的那种
第2篇Windows 上劫持流量的 N 种姿势以及我为何选了最痛的那种一、选择的难题挑餐厅的时候选择多是一种幸福。做技术选型的时候选择多是一种诅咒。“在 Windows 上劫持网络流量”——这个需求听起来简单但等你真的开始研究怎么做你会发现面前的选项多到令人窒息。每一种方案都有人推荐每一种方案都有人骂每一种方案的文档都看似完整但你一旦上手就发现写文档的人肯定没自己写过代码。我花了大概两周试了五种方案蓝屏了四次最终选了最难的。今天这篇就是把五种方案掰开揉碎了讲——不是为了让你也花两周是为了让你花二十分钟看完这篇然后直接跳到正确答案。但我得先说清楚评价标准。我们做的是WiFi 热点 透明代理的组合。关键约束是流量会经过 Windows ICSInternet Connection Sharing而 ICS 在二层数据链路层就把流量 forward 走了。任何在三层网络层及以上工作的工具根本看不到这些流量。这就像一个安检门装在了机场出发大厅——但乘客全部走地下通道绕过去了。记住这个约束。后面会反复提到。二、方案一LSP —— 一场 DLL 地狱LSP 全称 Layered Service Provider原理是在 Winsock 层插一个 DLL所有 socket 调用都要经过你。不需要写驱动注册一下就行。听着很美。实际上是个定时炸弹。你的 DLL 会被加载到每一个使用 Winsock 的进程里。浏览器、IM、杀毒软件、Windows Update——全部。如果你的 DLL 有个 bug——在DllMain里做了不该做的事或者在某个回调里死循环了——不是挂一个进程是挂所有进程。你连上网搜解决方案都搜不了因为浏览器也被你搞挂了。然后微软在 Windows 8 之后基本把 LSP 判了死刑。新的 API 不经过 LSP只经过 WFP。一半的流量你根本看不到。最致命的是LSP 在应用层。它只能看到某个程序决定发出去的包。我们要做的是软路由——从网卡层面拦截所有流量不管它来自哪个程序、哪台设备。LSP 根本不在同一个赛道上。结论淘汰。连候选都不算。三、方案二WFP —— 微软的亲儿子但不是我们需要的儿子WFP 全称 Windows Filtering PlatformVista 时代引入。微软的防火墙就是用 WFP 实现的。WFP 的架构很强大网络栈的不同层次都有垫片Shim你可以在任意一层注册呼出Callout检查、修改、阻断经过的包。而且不需要写完整的内核驱动——WFP 的引擎在内核态但你的过滤逻辑可以通过 BFEBase Filtering Engine在用户态管理。但 WFP 有两个致命问题。第一WFP 是三层及以上的工具。ICS 在二层就把 WiFi Direct 的流量 forward 走了WFP 根本看不见这些包。这就像你家门口装了一个监控摄像头但小偷从地下车库的通道进来了——摄像头什么都没拍到。第二WFP 的工作模式是审查不是篡改。你可以检查一个包然后决定放行或丢弃但你很难做到拦截一个 TCP SYN 然后把它重定向到本地的另一个端口。WFP 有 callout redirect但能力受限不适合做完整的透明代理。结论好东西但不适合我们这个场景。四、方案三TUN/TAP 虚拟网卡 —— Linux 的遗产Windows 的鸡肋TUN/TAP 是 VPN 软件的经典方案。创建一个虚拟网卡操作系统以为它是真的但实际上所有数据都被你的用户态程序接管了。OpenVPN、WireGuard 用的都是这个原理。在 Windows 上也有 TUN/TAP 的实现——OpenVPN 的tap-windows驱动是最出名的。你可以创建一个虚拟的以太网适配器给它配一个 IP然后用ReadFile/WriteFile来收发以太网帧。问题在哪WiFi Direct 模式下ICS 自动接管了二层转发。流量根本不经过你的虚拟网卡。你想从虚拟网卡上拿到流量需要自己做一个二层转发——比如把 WiFi Direct 适配器和虚拟网卡之间桥接起来或者自己写一个二层转发逻辑。等你把这些都写完了你会发现这跟你直接深入二层接管流量有什么区别而且你多了一层虚拟网卡的数据拷贝性能还更差。这就好比你想去隔壁城市买了张高铁票但先骑了半小时共享单车去高铁站到了又打了一辆车。为什么不直接打车呢结论可以做但绕了一圈回到原点。五、方案四WinDivert —— 用户态抓包神器可惜管不了二层WinDivert 是一个很酷的项目。它基于 WFP提供了一个简洁的用户态 APIHANDLE handleWinDivertOpen(outbound and tcp.DstPort 80,...);WinDivertRecv(handle,packet,packetLen,addr,addrLen);// 修改 packet...WinDivertSend(handle,packet,packetLen,addr,addrLen);API 简洁得像 Python文档清晰示例丰富。如果我只是想写一个简单的HTTP 请求拦截器WinDivert 绝对是首选。但 WinDivert 最大的问题不是性能——虽然每个包都要从内核拷到用户态再拷回去性能天花板确实存在——最大的问题是它只能做三层的 forward。回想一下前面说的Windows ICS 在二层就把流量 forward 走了。WinDivert 基于 WFP工作在第三层及以上。WiFi Direct AP 的流量经过 ICS 的二层转发WinDivert 根本拦截不到。这就好比你派了一个交警在三楼路口执勤但所有车辆都在二楼就被调度走了。交警站在三楼什么都看不见。结论简单场景的神器WiFi Direct 透明代理场景下完全没用。六、方案五WinPkFilter —— 在最底层拦路抢劫WinPkFilter 的全名是 Windows Packet Filter Kit是一个 NDIS 驱动开发包。它的核心组件包括一个 NDIS 协议驱动Protocol Driver和一个 NDIS 轻量过滤器LightWeight Filter。它工作在第二层——在网卡驱动和 TCP/IP 协议栈之间。所有的包不管是从网线进来的还是从协议栈出去的都要经过它。更重要的是——它在 ICS 的二层转发之下。ICS 的流量也要经过 NDIS 层所以 WinPkFilter 能看到 ICS forward 的包。有了这种能力透明代理水到渠成手机发来的 TCP SYN → WinPkFilter 拦截 → 送到用户态程序重定向到本地端口 → 从 NAT 缓存拿真实目标 → 发起新的 TCP 连接两个连接的数据流桥接——所有 TCP 状态机由操作系统帮你维护这就是硬核二字的含义。你要在最底层工作跟 NDIS 驱动打交道处理 IOCTL、管理数据包缓冲区——但换来的是完全的控制权和最高的性能。代价是它是五种方案里最难的。驱动安装需要管理员权限调试需要内核调试器。写错一行代码——比如在中断上下文访问了分页内存——系统蓝屏没有商量。但软路由不就是这样吗要么在最底层硬刚要么别做。七、一张对比表结束今天的纠结方案工作层能看到ICS转发的包能做透明代理开发难度LSP应用层❌❌低WFP3-7层❌❌高TUN/TAP2层(虚拟)❌(需要自己转发)⚠️ 绕路中WinDivert3层❌❌低WinPkFilter2层✅✅极高选最后一个不是因为自虐是因为它是唯一正确的答案。本文是《从0到1编写一个硬核软路由》系列的的第二篇。上一篇第1篇我为什么要花3000个小时写一个轮子 | 下一篇第3篇全景架构图