2
0

DnsInterceptor.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. using DNS.Protocol;
  2. using DNS.Protocol.ResourceRecords;
  3. using FastGithub.Configuration;
  4. using Microsoft.Extensions.Logging;
  5. using PacketDotNet;
  6. using System;
  7. using System.Linq;
  8. using System.Net;
  9. using System.Runtime.InteropServices;
  10. using System.Runtime.Versioning;
  11. using System.Threading;
  12. using WinDivertSharp;
  13. namespace FastGithub.Dns
  14. {
  15. /// <summary>
  16. /// dns拦截器
  17. /// </summary>
  18. [SupportedOSPlatform("windows")]
  19. sealed class DnsInterceptor
  20. {
  21. private const string DNS_FILTER = "udp.DstPort == 53";
  22. private readonly FastGithubConfig fastGithubConfig;
  23. private readonly ILogger<DnsInterceptor> logger;
  24. private readonly TimeSpan ttl = TimeSpan.FromMinutes(2d);
  25. /// <summary>
  26. /// 刷新DNS缓存
  27. /// </summary>
  28. [DllImport("dnsapi.dll", EntryPoint = "DnsFlushResolverCache", SetLastError = true)]
  29. private static extern void DnsFlushResolverCache();
  30. /// <summary>
  31. /// dns投毒后台服务
  32. /// </summary>
  33. /// <param name="fastGithubConfig"></param>
  34. /// <param name="logger"></param>
  35. public DnsInterceptor(
  36. FastGithubConfig fastGithubConfig,
  37. ILogger<DnsInterceptor> logger)
  38. {
  39. this.fastGithubConfig = fastGithubConfig;
  40. this.logger = logger;
  41. }
  42. /// <summary>
  43. /// DNS拦截
  44. /// </summary>
  45. /// <param name="cancellationToken"></param>
  46. public void Intercept(CancellationToken cancellationToken)
  47. {
  48. var handle = WinDivert.WinDivertOpen(DNS_FILTER, WinDivertLayer.Network, 0, WinDivertOpenFlags.None);
  49. if (handle == IntPtr.Zero)
  50. {
  51. return;
  52. }
  53. cancellationToken.Register(hwnd =>
  54. {
  55. WinDivert.WinDivertClose((IntPtr)hwnd!);
  56. DnsFlushResolverCache();
  57. }, handle);
  58. var packetLength = 0U;
  59. var packetBuffer = new byte[ushort.MaxValue];
  60. using var winDivertBuffer = new WinDivertBuffer(packetBuffer);
  61. var winDivertAddress = new WinDivertAddress();
  62. DnsFlushResolverCache();
  63. while (cancellationToken.IsCancellationRequested == false)
  64. {
  65. if (WinDivert.WinDivertRecv(handle, winDivertBuffer, ref winDivertAddress, ref packetLength))
  66. {
  67. try
  68. {
  69. this.ProcessDnsPacket(packetBuffer, ref winDivertAddress, ref packetLength);
  70. }
  71. catch (Exception ex)
  72. {
  73. this.logger.LogWarning(ex.Message);
  74. }
  75. WinDivert.WinDivertHelperCalcChecksums(winDivertBuffer, packetLength, ref winDivertAddress, WinDivertChecksumHelperParam.All);
  76. WinDivert.WinDivertSend(handle, winDivertBuffer, packetLength, ref winDivertAddress);
  77. }
  78. }
  79. }
  80. /// <summary>
  81. /// 处理DNS数据包
  82. /// </summary>
  83. /// <param name="packetBuffer"></param>
  84. /// <param name="winDivertAddress"></param>
  85. /// <param name="packetLength"></param>
  86. private void ProcessDnsPacket(byte[] packetBuffer, ref WinDivertAddress winDivertAddress, ref uint packetLength)
  87. {
  88. var packetData = packetBuffer.AsSpan(0, (int)packetLength).ToArray();
  89. var packet = Packet.ParsePacket(LinkLayers.Raw, packetData);
  90. var ipPacket = (IPPacket)packet.PayloadPacket;
  91. var udpPacket = (UdpPacket)ipPacket.PayloadPacket;
  92. var request = Request.FromArray(udpPacket.PayloadData);
  93. if (request.OperationCode != OperationCode.Query)
  94. {
  95. return;
  96. }
  97. var question = request.Questions.FirstOrDefault();
  98. if (question == null || question.Type != RecordType.A)
  99. {
  100. return;
  101. }
  102. var domain = question.Name;
  103. if (this.fastGithubConfig.IsMatch(domain.ToString()) == false)
  104. {
  105. return;
  106. }
  107. // 反转ip
  108. var destAddress = ipPacket.DestinationAddress;
  109. ipPacket.DestinationAddress = ipPacket.SourceAddress;
  110. ipPacket.SourceAddress = destAddress;
  111. // 反转端口
  112. var destPort = udpPacket.DestinationPort;
  113. udpPacket.DestinationPort = udpPacket.SourcePort;
  114. udpPacket.SourcePort = destPort;
  115. // 设置dns响应
  116. var response = Response.FromRequest(request);
  117. var record = new IPAddressResourceRecord(domain, IPAddress.Loopback, this.ttl);
  118. response.AnswerRecords.Add(record);
  119. udpPacket.PayloadData = response.ToArray();
  120. // 修改数据内容和数据长度
  121. packet.Bytes.CopyTo(packetBuffer, 0);
  122. packetLength = (uint)packet.Bytes.Length;
  123. // 反转方向
  124. if (winDivertAddress.Direction == WinDivertDirection.Inbound)
  125. {
  126. winDivertAddress.Direction = WinDivertDirection.Outbound;
  127. }
  128. else
  129. {
  130. winDivertAddress.Direction = WinDivertDirection.Inbound;
  131. }
  132. this.logger.LogInformation($"已拦截dns查询{domain}并伪造响应内容为{IPAddress.Loopback}");
  133. }
  134. }
  135. }