.NET借助虚拟网卡实现一个简单异地组网工具

net · 浏览次数 : 0

小编点评

本文档主要介绍了如何创建一个简易的个人内网穿透工具,通过将虚拟网卡数据封装到socket代理中,并使用Tun2socks实现。最后,文档还提供了一个基本的打洞客户端和服务器代码示例。 1. **需求**:为了解决无法直接访问国外服务器的问题,作者决定研究并创建一个适用于Windows平台的内网穿透工具。 2. **技术选型**:通过研究,作者选择了基于Tun2socks的方案,并学习了其工作原理。 3. **简易实现**:为了更深入地理解和掌握Tun2socks的实现原理,作者手动实现了一个简易的基于Tun/Tap的核心代码。 4. **驱动程序获取**:在安装过程中,作者采用了一种方法来获取驱动程序,并确保能够启动TAP设备。 5. **网络配置**:通过调用`DeviceIoControl`函数,作者更改了虚拟网卡的网络设置,使其能够与其他计算机进行通信。 6. **文件操作**:文档中提到了文件操作,这可能是指在构建工具时处理的一些涉及文件读写和管理的操作。 7. **组网优势**:通过使用简易的组网工具,用户可以实现远程桌面和其他网络通信功能。 8. **测试与优化**:文档还提供了客户端的代码链接和相关说明,以便他人可以测试该工具的性能和使用情况。

正文

由于工作需要,经常需要远程客户的服务器,但是并不是所有服务器都能开外网端口,使用向日葵等软件终究还是不太方便,于是找了很多工具,包括zerotier 等,但是由于服务器在国外等有时候还不同,

于是开始自己想办法研究一个属于自己的组网工具,最后找到snltty大佬的 https://github.com/snltty/p2p-tunnel ,学习后发现是基于tun2socks实现的,

tun2socks 的优点是 把虚拟网卡的数据都打包到socket代理了,但是过滤了ping (ICmp)的包,他自行返回了 成功,这不是我要的效果

于是看了一下tun2socks 的实现,是基于tun/tap实现的,于是研究了一下,手动基于tun/tap实现了一个简易的

核心代码

  1  [SupportedOSPlatform("windows")]
  2  public class WinTunDriveHostedService : TunDriveHostedService
  3  {
  4      private readonly static string DriverPath = AppDomain.CurrentDomain.BaseDirectory + "Drivers";
  5      private const string AdapterKey = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}";
  6      private const string ConnectionKey = "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}";
  7 
  8 
  9      public const int TAP_WIN_IOCTL_GET_MAC = 1;
 10      public const int TAP_WIN_IOCTL_GET_VERSION = 2;
 11      public const int TAP_WIN_IOCTL_GET_MTU = 3;
 12      public const int TAP_WIN_IOCTL_GET_INFO = 4;
 13      public const int TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT = 5;
 14      public const int TAP_WIN_IOCTL_SET_MEDIA_STATUS = 6;
 15      public const int TAP_WIN_IOCTL_CONFIG_DHCP_MASQ = 7;
 16      public const int TAP_WIN_IOCTL_GET_LOG_LINE = 8;
 17      public const int TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT = 9;
 18      public const int TAP_WIN_IOCTL_CONFIG_TUN = 10;
 19 
 20      public const uint FILE_ATTRIBUTE_SYSTEM = 0x4;
 21      public const uint FILE_FLAG_OVERLAPPED = 0x40000000;
 22      public const uint METHOD_BUFFERED = 0;
 23      public const uint FILE_ANY_ACCESS = 0;
 24      public const uint FILE_DEVICE_UNKNOWN = 0x22;
 25      public WinTunDriveHostedService(IOptions<TunDriveConfig> tunDriveConfigOptions, ILogger<WinTunDriveHostedService> logger) : base(tunDriveConfigOptions, logger)
 26      {
 27      }
 28      [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
 29      public static extern bool DeviceIoControl(SafeHandle device, uint IoControlCode, IntPtr InBuffer, uint InBufferSize, IntPtr OutBuffer, uint OutBufferSize, ref uint BytesReturned, IntPtr Overlapped);
 30 
 31 
 32      protected override FileStream OpenDrive()
 33      {
 34          var className = InstallOrGetClassNameDrive();
 35          var safeFileHandle = System.IO.File.OpenHandle($@"\\.\\Global\\{className}.tap", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, FileOptions.Asynchronous);
 36          return new FileStream(safeFileHandle, FileAccess.ReadWrite, 1500);
 37      }
 38      protected virtual string InstallOrGetClassNameDrive()
 39      {
 40          using (RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(ConnectionKey))
 41          {
 42              var names = registryKey.GetSubKeyNames();
 43              foreach (var name in names)
 44              {
 45                  using (var connectionRegistryKey = registryKey.OpenSubKey(name).OpenSubKey("Connection"))
 46                  {
 47                      if (connectionRegistryKey != null && connectionRegistryKey.GetValue("Name").ToString() == TunDriveConfig.TunDriveName)
 48                      {
 49                          return name;
 50                      }
 51                  }
 52              }
 53 
 54              Directory.CreateDirectory(DriverPath);
 55              ZipArchive zipArchive = new ZipArchive(typeof(WinTunDriveHostedService).Assembly.GetManifestResourceStream($"RemoteNetwork.{(Environment.Is64BitOperatingSystem ? "amd64" : "i386")}.zip"), ZipArchiveMode.Read);
 56              foreach (ZipArchiveEntry entry in zipArchive.Entries)
 57              {
 58                  entry.ExtractToFile(Path.Combine(DriverPath, entry.FullName), overwrite: true);
 59              }
 60              StartProcess(Path.Combine(DriverPath, "tapinstall.exe"), $"install OemVista.inf TAP0901", "runas", DriverPath);
 61              foreach (var name in registryKey.GetSubKeyNames())
 62              {
 63                  if (!names.Contains(name))
 64                  {
 65                      using (var connectionRegistryKey = registryKey.OpenSubKey(name).OpenSubKey("Connection"))
 66                      {
 67                          if (connectionRegistryKey != null)
 68                          {
 69                              StartProcess("netsh", @$"interface set interface name=""{connectionRegistryKey.GetValue("Name")}"" newname=""{TunDriveConfig.TunDriveName}""");
 70                              return name;
 71                          }
 72                      }
 73                  }
 74              }
 75              return string.Empty;
 76          }
 77      }
 78      private static int ParseIP(string address)
 79      {
 80          byte[] addressBytes = address.Split('.').Select(s => byte.Parse(s)).ToArray();
 81          return addressBytes[0] | (addressBytes[1] << 8) | (addressBytes[2] << 16) | (addressBytes[3] << 24);
 82      }
 83      protected override void ConfigIP(string ip, string netmask)
 84      {
 85          StartProcess("netsh", $"interface ip set address name=\"{TunDriveConfig.TunDriveName}\" source=static addr={ip} mask={netmask} gateway=none");
 86          IntPtr intPtr = Marshal.AllocHGlobal(12);
 87          Marshal.WriteInt32(intPtr, 0, ParseIP(ip));
 88          Marshal.WriteInt32(intPtr, 4, 0);
 89          Marshal.WriteInt32(intPtr, 8,0);
 90          uint lpBytesReturned = 0;
 91          bool result = DeviceIoControl(TunStream.SafeFileHandle, 2228264, intPtr, 12u, intPtr, 12u, ref lpBytesReturned, IntPtr.Zero);
 92          Marshal.FreeHGlobal(intPtr);
 93      }
 94      private static uint CTL_CODE(uint iDeviceType, uint iFunction, uint iMethod, uint iAccess)
 95      {
 96          return ((iDeviceType << 16) | (iAccess << 14) | (iFunction << 2) | iMethod);
 97      }
 98      public override bool ConnectionState(bool connection)
 99      {
100          uint Length = 0;
101          IntPtr cconfig = Marshal.AllocHGlobal(4);
102          Marshal.WriteInt32(cconfig, connection ? 1 : 0);
103 
104          var b = DeviceIoControl(TunStream.SafeFileHandle, CTL_CODE(FILE_DEVICE_UNKNOWN, TAP_WIN_IOCTL_SET_MEDIA_STATUS, METHOD_BUFFERED, FILE_ANY_ACCESS), cconfig, 4, cconfig, 4, ref Length, IntPtr.Zero);
105          StartProcess("netsh", $"netsh interface ipv4 set subinterface \"{TunDriveConfig.TunDriveName}\" mtu=\"1400\" store=persistent");
106          return b;
107      }
108  }

 

liunx 核心代码

 1 public class TunNetWorkFrameHostedService : BackgroundService
 2 {
 3     private  readonly string exchangeHostName = "";
 4     private readonly int P2PPort = 61000;
 5     protected readonly ILogger<TunNetWorkFrameHostedService> _logger;
 6     public static TunNetWorkFrameHostedService Instance { get; private set; }
 7     private readonly UdpClient udpClient;
 8     private readonly System.Net.IPEndPoint remoteEndPoint = new System.Net.IPEndPoint(0, 0);
 9     public TunNetWorkFrameHostedService(ILogger<TunNetWorkFrameHostedService> logger, IOptions<TunDriveConfig> tunDriveConfigOptions)
10     {
11         exchangeHostName = tunDriveConfigOptions.Value.DataExchangeHostName;
12         _logger = logger;
13         Instance = this;
14         udpClient = new UdpClient(0); if (Environment.OSVersion.Platform == PlatformID.Win32NT)
15         {
16             const int SIP_UDP_CONNRESET = -1744830452;
17             udpClient.Client.IOControl(SIP_UDP_CONNRESET, new byte[] { 0, 0, 0, 0 }, null);
18         }
19     }
20 
21 
22     protected override async Task ExecuteAsync(CancellationToken stoppingToken)
23     {
24         udpClient.BeginReceive(ReceiveCallback, udpClient);
25         while (!stoppingToken.IsCancellationRequested)
26         {
27             await udpClient.SendAsync(TunDriveHostedService.Instance.Id, exchangeHostName, P2PPort, stoppingToken).ConfigureAwait(false);
28             await Task.Delay(1000*30, stoppingToken).ConfigureAwait(false);
29         }
30     }
31     void ReceiveCallback(IAsyncResult ar)
32     {
33         System.Net.IPEndPoint remoteEndPoint = new System.Net.IPEndPoint(0, 0);
34         byte[] bytes = null;
35         try
36         {
37 
38             bytes = udpClient.EndReceive(ar, ref remoteEndPoint);
39 
40         }
41         finally
42         {
43             udpClient.BeginReceive(ReceiveCallback, udpClient);
44         }
45         if (bytes.Length == 4)
46         {
47             return;
48         }
49         if (bytes.Length == 5)
50         {
51             if (bytes[0] == 2)
52             {
53                 P2PUDPSocketHostedService.Instance.TestP2P(bytes.Skip(1).ToArray(),false);
54             }
55             return;
56         }
57        
58         TunDriveHostedService.Instance.WriteFrameBuffer(bytes);
59     }
60     public virtual async Task WriteFrameBufferAsync(Memory<byte> buffer, CancellationToken stoppingToken)
61     { 
62         var destId = BitConverter.ToInt32(buffer.Slice(16, 4).ToArray(), 0);
63 
64        var tunNetWorkFrameSend= P2PUDPSocketHostedService.Instance.GetP2PClient(buffer.Slice(16, 4).ToArray());
65         if (tunNetWorkFrameSend != null)
66         {
67             await tunNetWorkFrameSend.SendAsync(buffer, stoppingToken).ConfigureAwait(false);
68             return;
69         }
70         var bytes = new byte[buffer.Length + 8];
71         buffer.Slice(12, 8).CopyTo(bytes);
72         Array.Copy(buffer.ToArray(), 0,bytes,8,buffer.Length);
73         await udpClient.SendAsync(bytes, exchangeHostName, P2PPort, stoppingToken).ConfigureAwait(false);
74         //var destId = BitConverter.ToInt32(buffer.Slice(16, 4).ToArray(), 0);// string.Join(".", buffer.Slice(16, 4).ToArray());// span[16] << 24 | span[17] << 16 | span[18] << 8 | span[19];
75         //var sourceId = BitConverter.ToInt32(buffer.Slice(12, 4).ToArray(), 0);
76         //_logger.LogInformation($"{sourceId} 发送到{destId}");
77     }
78     /// <summary>
79     /// 发送打洞请求
80     /// </summary>
81     /// <param name="destId"></param>
82     /// <param name="stoppingToken"></param>
83     /// <returns></returns>
84     public virtual async Task SendP2PRequestAsync(byte[] destId, CancellationToken stoppingToken)
85     {
86         using (MemoryStream memoryStream = new MemoryStream()) {
87             memoryStream.Write(TunDriveHostedService.Instance.Id);
88             memoryStream.Write(destId);
89             memoryStream.WriteByte(2);
90             memoryStream.Write(TunDriveHostedService.Instance.Id);
91             await udpClient.SendAsync(memoryStream.ToArray(), exchangeHostName, P2PPort, stoppingToken).ConfigureAwait(false);
92         }
93         
94     }
95 }
View Code

 

以下是远程桌面的效果

客户端运行

 打洞成功

 测速

 

代码地址

https://github.com/hn-lyf/RemoteNetwork

测试客户端

 

 https://files.cnblogs.com/files/dotnet-org-cn/linux-x64.zip?t=1717937932&download=true

https://files.cnblogs.com/files/dotnet-org-cn/win-x64.zip?t=1717937932&download=true

https://files.cnblogs.com/files/dotnet-org-cn/win-x86.zip?t=1717937932&download=true

与.NET借助虚拟网卡实现一个简单异地组网工具相似的内容:

.NET借助虚拟网卡实现一个简单异地组网工具

由于工作需要,经常需要远程客户的服务器,但是并不是所有服务器都能开外网端口,使用向日葵等软件终究还是不太方便,于是找了很多工具,包括zerotier 等,但是由于服务器在国外等有时候还不同, 于是开始自己想办法研究一个属于自己的组网工具,最后找到snltty大佬的 https://github.co

【.NET项目分享】免费开源的静态博客生成工具EasyBlog,5分钟拥有自己的博客

EasyBlog 说明 本博客系统通过构建工具生成纯静态的博客网站,借助GitHub Pages,你可以在5分钟内免费拥有个人博客。 它具有以下特点 生成纯静态网站,访问速度极快 使用markdown格式来编写博客内容 基于git代码管理来存储你的博客 使用CI工具来自动化部署你的博客站点 效果展示

开发日志:Kylin麒麟操作系统部署ASP.NET CORE

需求场景: 我需要部署的项目是在Windows上开发的,目标框架为.net core 6.0 因此我们需要先在kylin上部署项目运行所需要的环境。 借助百度词条,先看看Kylin是什么: 服务器资源: 查看系统版本 cat /etc/kylin-release cat /proc/version

【JS】await异常捕获,这样做才完美

文章关注JavaScript中async/await的异常处理,指出未捕获异常的潜在风险。1)使用try-catch,虽全面但冗余;2)借助Promise的catch,减少冗余; 3) 利用await-to-js库简化异常处理

Linux系统上创建.NET6项目(通过命令行(CTL)方式)

前言 平时大家创建项目基本上都是借助开发工具创建,比如visual studio,visual studio code,今天我们在Linux系统上,通过命令行的形式创建.NET6项目。 版本介绍 系统版本:Ubuntu22.04 SDK版本:.NET6 安装.NET环境 # 升级系统 sudo ap

.NET8中的Microsoft.Extensions.Http.Resilience库

接上一篇,https://www.cnblogs.com/vipwan/p/18129361 借助Aspire中新增的Microsoft.Extensions.ServiceDiscovery库,我们可以很容易的做到服务发现,那么服务节点的熔断限流以及重试等弹性机制.NET是怎么处理的呢? 比如下图

YiShaAdmin:一款基于.NET Core Web + Bootstrap的企业级快速开发框架

前言 今天大姚给大家分享一款基于.NET Core Web + Bootstrap的企业级快速后台开发框架、权限管理系统,代码简单易懂、界面简洁美观(基于MIT License开源,免费可商用):YiShaAdmin。 项目官方介绍 YiShaAdmin 基于.NET Core Web开发,借鉴了很

[转帖]详解redis-cli 命令

https://www.jb51.net/article/265208.htm 这篇文章主要介绍了redis-cli 命令详解,主要包括命令使用及使用info命令获取服务器的信息,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下 命令使用: 1 redis-c

[转帖]详解redis-cli 命令

https://www.jb51.net/article/265208.htm 这篇文章主要介绍了redis-cli 命令详解,主要包括命令使用及使用info命令获取服务器的信息,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下 命令使用: 1 redis-c

C#/.NET这些实用的编程技巧你都会了吗?

DotNet Exercises介绍 DotNetGuide专栏C#/.NET/.NET Core编程常用语法、算法、技巧、中间件、类库练习集,配套详细的文章教程讲解,助你快速掌握C#/.NET/.NET Core各种编程常用语法、算法、技巧、中间件、类库等等。 GitHub开源地址:https:/