Wintun Echo Demo引言虚拟专用网络VNP和隧道技术的核心往往涉及虚拟网卡驱动。Wintun 是 WireGuard 官方推出的轻量级 TUN 驱动专为 Windows 平台设计允许用户态程序直接读写 IP 数据包。本文将以开源项目Wintun Echo Demo为例剖析其技术原理包括如何动态加载驱动、创建虚拟网卡、配置网络参数以及实现数据包回显。项目地址https://github.com/liulilittle/Wintun-Echo-DemoWintun 基础Wintun 是一个内核态的 NDIS 虚拟网卡驱动它在系统中创建一个虚拟网络接口所有发往该接口的 IP 数据包都会被传递到用户态程序通过环形缓冲区同时用户态程序也可以将数据包写入环形缓冲区由驱动从接口发送出去。这种设计实现了“TUN”模式三层隧道非常适合构建 VNP 客户端、代理工具等。项目整体架构Wintun Echo Demo 的主要工作流程如下动态加载wintun.dll并获取所有 API 函数指针。创建或打开一个名为MyTun的虚拟网卡。通过 Windows 网络配置 APIiphlpapi.h和netsh设置接口的 IPv4/IPv6 地址、MTU、默认路由和 DNS 服务器。启动一个 Wintun 会话创建接收线程循环读取从接口收到的数据包并原样写回回显。注册控制台事件处理器在收到 CtrlC 等退出信号时清理资源并退出。技术原理1. 动态加载 wintun.dll 与函数指针绑定由于 Wintun 以动态链接库形式提供为避免静态链接带来的兼容性问题程序使用LoadLibraryEx在运行时加载 DLL并通过GetProcAddress获取每个导出函数的地址。这种方法允许程序适配不同版本的 wintun.dll且无需修改链接配置。HMODULE WintunLoadLibraryExW(Lwintun.dll,NULL,LOAD_LIBRARY_SEARCH_APPLICATION_DIR|LOAD_LIBRARY_SEARCH_SYSTEM32);GET_PROC(WintunCreateAdapter);// ...2. 创建/打开虚拟网卡Wintun 提供两个函数WintunCreateAdapter用于创建新适配器WintunOpenAdapter用于打开已存在的同名适配器。程序首先尝试打开现有适配器若失败则创建新适配器。创建时可以指定一个固定的 GUID这样多次运行会复用同一适配器实例避免反复创建删除。g_AdapterWintunOpenAdapter(LMyTun);if(!g_Adapter){g_AdapterWintunCreateAdapter(LMyTun,LDemo,AdapterGuid,NULL);}3. 配置网络接口配置网络接口是 VNP 程序的关键步骤包括设置 IP 地址、MTU、路由和 DNS。IP 地址配置使用iphlpapi.h中的CreateUnicastIpAddressEntry函数。程序先通过inet_pton将字符串 IP 转换为二进制格式然后填充MIB_UNICASTIPADDRESS_ROW结构体指定接口 LUID 和子网掩码长度最后调用 API 添加地址。InitializeUnicastIpAddressEntry(addressRow);addressRow.InterfaceLuid*luid;addressRow.Address.Ipv4.sin_familyAF_INET;addressRow.Address.Ipv4.sin_addripAddr;addressRow.OnLinkPrefixLengthmask;CreateUnicastIpAddressEntry(addressRow);MTU 设置MTU最大传输单元通过SetIpInterfaceEntry设置。程序先调用GetIpInterfaceEntry获取当前接口的配置修改NlMtu字段后写回。默认路由添加默认路由使虚拟网卡成为默认网关所有流量进入隧道。使用CreateIpForwardEntry2函数目标前缀为0.0.0.0/0IPv4或::/0IPv6下一跳指向虚拟网卡自身的地址。route.DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr0;route.DestinationPrefix.PrefixLength0;route.NextHop.Ipv4.sin_addr.S_un.S_addrgatewayIp;CreateIpForwardEntry2(route);DNS 配置DNS 配置较为复杂因为iphlpapi.h没有直接的 API 可以设置接口的 DNS 服务器列表。项目中采用调用netsh命令的方式通过system函数执行以下命令netsh interface ip set dns nameMyTun sourcestatic addrnone netsh interface ip add dns nameMyTun addr8.8.8.8 index1同时支持 IPv4 和 IPv6 的 DNS 配置。这种方式虽然简单但依赖 netsh 的存在且执行命令会弹出控制台窗口可改进为使用DnsFlushResolverCache和注册表操作但作为示例足够清晰。4. 数据包回显线程Wintun 会话创建后程序进入数据包处理循环。Wintun 使用环形缓冲区在用户态和内核态之间交换数据提供了高效的批量读写能力。获取读事件WintunGetReadWaitEvent返回一个事件句柄当有数据包到达时变为有信号状态。等待多个事件使用WaitForMultipleObjects同时等待读事件和退出事件实现响应式退出。读取数据包WintunReceivePacket从环形缓冲区取出一个数据包返回指针和大小。注意读取后必须调用WintunReleaseReceivePacket释放缓冲区槽位。回写数据包WintunAllocateSendPacket分配发送缓冲区memcpy复制数据最后WintunSendPacket提交发送。回显逻辑非常简单将收到的包原封不动地发送回去。这导致对虚拟网卡 IP 的 ping 请求会被响应实现“假在线”效果。BYTE*sendPacketWintunAllocateSendPacket(session,packetSize);if(sendPacket){memcpy(sendPacket,packet,packetSize);WintunSendPacket(session,sendPacket);}WintunReleaseReceivePacket(session,packet);5. 资源清理与退出机制为了优雅退出程序创建了一个手动重置事件g_QuitEvent作为全局退出信号。接收线程通过WaitForMultipleObjects同时等待读事件和退出事件。当用户按下 CtrlC 时控制台事件处理器将g_Running置为 false并设置g_QuitEvent线程检测到退出事件后自动结束。主线程在退出信号触发后按照创建的反序清理资源等待接收线程结束。结束 Wintun 会话WintunEndSession。删除适配器WintunDeleteAdapter并关闭句柄WintunCloseAdapter。卸载 DLL 和清理 Winsock。备注Wintun Echo Demo 是一个极佳的学习示例它完整展示了如何在 Windows 上利用 Wintun 驱动创建虚拟网卡、配置网络参数并实现用户态数据包处理。通过剖析其代码开发者可以快速掌握虚拟网卡编程的核心技术为构建更复杂的 VNP 或网络工具打下基础。项目源码清晰、注释丰富非常适合入门到中级开发者学习。欢迎访问 GitHub 仓库获取完整代码https://github.com/liulilittle/Wintun-Echo-Demo
Wintun Echo Demo
Wintun Echo Demo引言虚拟专用网络VNP和隧道技术的核心往往涉及虚拟网卡驱动。Wintun 是 WireGuard 官方推出的轻量级 TUN 驱动专为 Windows 平台设计允许用户态程序直接读写 IP 数据包。本文将以开源项目Wintun Echo Demo为例剖析其技术原理包括如何动态加载驱动、创建虚拟网卡、配置网络参数以及实现数据包回显。项目地址https://github.com/liulilittle/Wintun-Echo-DemoWintun 基础Wintun 是一个内核态的 NDIS 虚拟网卡驱动它在系统中创建一个虚拟网络接口所有发往该接口的 IP 数据包都会被传递到用户态程序通过环形缓冲区同时用户态程序也可以将数据包写入环形缓冲区由驱动从接口发送出去。这种设计实现了“TUN”模式三层隧道非常适合构建 VNP 客户端、代理工具等。项目整体架构Wintun Echo Demo 的主要工作流程如下动态加载wintun.dll并获取所有 API 函数指针。创建或打开一个名为MyTun的虚拟网卡。通过 Windows 网络配置 APIiphlpapi.h和netsh设置接口的 IPv4/IPv6 地址、MTU、默认路由和 DNS 服务器。启动一个 Wintun 会话创建接收线程循环读取从接口收到的数据包并原样写回回显。注册控制台事件处理器在收到 CtrlC 等退出信号时清理资源并退出。技术原理1. 动态加载 wintun.dll 与函数指针绑定由于 Wintun 以动态链接库形式提供为避免静态链接带来的兼容性问题程序使用LoadLibraryEx在运行时加载 DLL并通过GetProcAddress获取每个导出函数的地址。这种方法允许程序适配不同版本的 wintun.dll且无需修改链接配置。HMODULE WintunLoadLibraryExW(Lwintun.dll,NULL,LOAD_LIBRARY_SEARCH_APPLICATION_DIR|LOAD_LIBRARY_SEARCH_SYSTEM32);GET_PROC(WintunCreateAdapter);// ...2. 创建/打开虚拟网卡Wintun 提供两个函数WintunCreateAdapter用于创建新适配器WintunOpenAdapter用于打开已存在的同名适配器。程序首先尝试打开现有适配器若失败则创建新适配器。创建时可以指定一个固定的 GUID这样多次运行会复用同一适配器实例避免反复创建删除。g_AdapterWintunOpenAdapter(LMyTun);if(!g_Adapter){g_AdapterWintunCreateAdapter(LMyTun,LDemo,AdapterGuid,NULL);}3. 配置网络接口配置网络接口是 VNP 程序的关键步骤包括设置 IP 地址、MTU、路由和 DNS。IP 地址配置使用iphlpapi.h中的CreateUnicastIpAddressEntry函数。程序先通过inet_pton将字符串 IP 转换为二进制格式然后填充MIB_UNICASTIPADDRESS_ROW结构体指定接口 LUID 和子网掩码长度最后调用 API 添加地址。InitializeUnicastIpAddressEntry(addressRow);addressRow.InterfaceLuid*luid;addressRow.Address.Ipv4.sin_familyAF_INET;addressRow.Address.Ipv4.sin_addripAddr;addressRow.OnLinkPrefixLengthmask;CreateUnicastIpAddressEntry(addressRow);MTU 设置MTU最大传输单元通过SetIpInterfaceEntry设置。程序先调用GetIpInterfaceEntry获取当前接口的配置修改NlMtu字段后写回。默认路由添加默认路由使虚拟网卡成为默认网关所有流量进入隧道。使用CreateIpForwardEntry2函数目标前缀为0.0.0.0/0IPv4或::/0IPv6下一跳指向虚拟网卡自身的地址。route.DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr0;route.DestinationPrefix.PrefixLength0;route.NextHop.Ipv4.sin_addr.S_un.S_addrgatewayIp;CreateIpForwardEntry2(route);DNS 配置DNS 配置较为复杂因为iphlpapi.h没有直接的 API 可以设置接口的 DNS 服务器列表。项目中采用调用netsh命令的方式通过system函数执行以下命令netsh interface ip set dns nameMyTun sourcestatic addrnone netsh interface ip add dns nameMyTun addr8.8.8.8 index1同时支持 IPv4 和 IPv6 的 DNS 配置。这种方式虽然简单但依赖 netsh 的存在且执行命令会弹出控制台窗口可改进为使用DnsFlushResolverCache和注册表操作但作为示例足够清晰。4. 数据包回显线程Wintun 会话创建后程序进入数据包处理循环。Wintun 使用环形缓冲区在用户态和内核态之间交换数据提供了高效的批量读写能力。获取读事件WintunGetReadWaitEvent返回一个事件句柄当有数据包到达时变为有信号状态。等待多个事件使用WaitForMultipleObjects同时等待读事件和退出事件实现响应式退出。读取数据包WintunReceivePacket从环形缓冲区取出一个数据包返回指针和大小。注意读取后必须调用WintunReleaseReceivePacket释放缓冲区槽位。回写数据包WintunAllocateSendPacket分配发送缓冲区memcpy复制数据最后WintunSendPacket提交发送。回显逻辑非常简单将收到的包原封不动地发送回去。这导致对虚拟网卡 IP 的 ping 请求会被响应实现“假在线”效果。BYTE*sendPacketWintunAllocateSendPacket(session,packetSize);if(sendPacket){memcpy(sendPacket,packet,packetSize);WintunSendPacket(session,sendPacket);}WintunReleaseReceivePacket(session,packet);5. 资源清理与退出机制为了优雅退出程序创建了一个手动重置事件g_QuitEvent作为全局退出信号。接收线程通过WaitForMultipleObjects同时等待读事件和退出事件。当用户按下 CtrlC 时控制台事件处理器将g_Running置为 false并设置g_QuitEvent线程检测到退出事件后自动结束。主线程在退出信号触发后按照创建的反序清理资源等待接收线程结束。结束 Wintun 会话WintunEndSession。删除适配器WintunDeleteAdapter并关闭句柄WintunCloseAdapter。卸载 DLL 和清理 Winsock。备注Wintun Echo Demo 是一个极佳的学习示例它完整展示了如何在 Windows 上利用 Wintun 驱动创建虚拟网卡、配置网络参数并实现用户态数据包处理。通过剖析其代码开发者可以快速掌握虚拟网卡编程的核心技术为构建更复杂的 VNP 或网络工具打下基础。项目源码清晰、注释丰富非常适合入门到中级开发者学习。欢迎访问 GitHub 仓库获取完整代码https://github.com/liulilittle/Wintun-Echo-Demo