private unsafe void RunDiversion() { var packet = new WinDivertBuffer(); var addr = new WinDivertAddress(); uint recvLength = 0; NativeOverlapped recvOverlapped; IntPtr recvEvent = IntPtr.Zero; recvEvent = WinDivertSharp.WinAPI.Kernel32.CreateEvent(IntPtr.Zero, false, false, IntPtr.Zero); if (recvEvent == IntPtr.Zero || recvEvent == new IntPtr(-1)) { m_logger.Error("Failed to initialize receive IO event."); return; } uint recvAsyncIoLen = 0; bool isLocalIpv4 = false; bool modifiedPacket = false; bool dropPacket = false; Span <byte> payloadBufferPtr = null; while (m_running) { try { payloadBufferPtr = null; recvLength = 0; addr.Reset(); modifiedPacket = false; dropPacket = false; isLocalIpv4 = false; recvAsyncIoLen = 0; recvOverlapped = new NativeOverlapped(); WinAPI.Kernel32.ResetEvent(recvEvent); recvOverlapped.EventHandle = recvEvent; #region Packet Reading Code uint addrLen = (uint)sizeof(WinDivertAddress); if (!WinDivert.WinDivertRecvEx(m_diversionHandle, packet, 0, out recvLength, out addr, ref addrLen, ref recvOverlapped)) { var error = Marshal.GetLastWin32Error(); // 997 == ERROR_IO_PENDING if (error != 997) { m_logger.Warn(string.Format("Unknown IO error ID {0}while awaiting overlapped result.", error)); continue; } // 258 == WAIT_TIMEOUT while (m_running && WinDivertSharp.WinAPI.Kernel32.WaitForSingleObject(recvEvent, 1000) == (uint)WaitForSingleObjectResult.WaitTimeout) { } if (!WinDivertSharp.WinAPI.Kernel32.GetOverlappedResult(m_diversionHandle, ref recvOverlapped, ref recvAsyncIoLen, false)) { m_logger.Warn("Failed to get overlapped result."); continue; } recvLength = recvAsyncIoLen; } #endregion Packet Reading Code WinDivertParseResult parseResult = null; if (addr.Outbound) { parseResult = WinDivert.WinDivertHelperParsePacket(packet, recvLength); #region New TCP Connection Detection if (parseResult.TcpHeader != null && parseResult.TcpHeader->Syn > 0) { // Brand new outbound connection. Grab the PID of the process holding this // port and map it. if (parseResult.IPv4Header != null) { var connInfo = GetLocalPacketInfo(parseResult.TcpHeader->SrcPort, parseResult.IPv4Header->SrcAddr); HandleNewTcpConnection(connInfo, parseResult.TcpHeader, false); // Handle the special case of entirely blocking internet for this application/port. if (Volatile.Read(ref m_v4ShouldFilter[parseResult.TcpHeader->SrcPort]) == (int)FirewallAction.BlockInternetForApplication) { dropPacket = true; } } if (parseResult.IPv6Header != null) { var connInfo = GetLocalPacketInfo(parseResult.TcpHeader->SrcPort, parseResult.IPv6Header->SrcAddr); HandleNewTcpConnection(connInfo, parseResult.TcpHeader, true); // Handle the special case of entirely blocking internet for this application/port. if (Volatile.Read(ref m_v6ShouldFilter[parseResult.TcpHeader->SrcPort]) == (int)FirewallAction.BlockInternetForApplication) { dropPacket = true; } } } // Now that we've processed any potentially new connections, let's see if the // packet belongs to an existing flow that was marked to be blocked. // Check if this packet belongs to an IPV4 flow marked for blocking. if (parseResult.IPv4Header != null) { int srcPortAction = Volatile.Read(ref m_v4ShouldFilter[parseResult.TcpHeader->SrcPort]); // Handle the special case of entirely blocking internet for this application/port. if (srcPortAction == (int)FirewallAction.BlockInternetForApplication) { dropPacket = true; } } // Check if this packet belongs to an IPV6 flow marked for blocking. if (!dropPacket && parseResult.IPv6Header != null) { int srcPortAction = Volatile.Read(ref m_v6ShouldFilter[parseResult.TcpHeader->SrcPort]); // Handle the special case of entirely blocking internet for this application/port. if (srcPortAction == (int)FirewallAction.BlockInternetForApplication) { dropPacket = true; } } #endregion New TCP Connection Detection // I put the checks for ipv4 and ipv6 as a double if statement rather than an // else if because I'm not sure how that would affect dual-mode sockets. Perhaps // it's possible for both headers to be defined. Probably not, but since I don't // know, I err on the side of awesome, or uhh, something like that. // We check local packets for TOR/SOCKS packets here. However, if we don't find // something we want to block on local addresses, then we want to skip these for // the rest of the filtering and just let them through. if (dropPacket == false && parseResult.IPv4Header != null && parseResult.TcpHeader != null) { // Let's explain the weird arcane logic here. First, we check if the current // flow should even be filtered. We do this, because there's a good chance // that this flow belongs to our proxy's connections, which we never want to // filter. If we didn't check this, then we would end up setting the // isLocalIpv4 flag to true on every single one of our proxy's connections, // and clients would never get packets ever because with that flag set, the // direction of the packets wouldn't be sorted. // // So, we check this, ensure it's actually something we want to filter. Then, // we check if the packet is destined for a local address. We set the flag // accordingly, and if true, then we will allow these packets to go out uninterrupted. // // If false, who cares. Regardless of true or false, we check to see if this // is a TOR/SOCKS4/5 proxy CONNECT, and drop it if it is. // // Also note, by letting local/private address destined packets go, we also // solve the problem of private TLS connections using private TLS self signed // certs, such as logging into one's router. If we didn't do this check and // let these through, we would break such connections. if (Volatile.Read(ref m_v4ShouldFilter[parseResult.TcpHeader->SrcPort]) == (int)FirewallAction.FilterApplication) { isLocalIpv4 = parseResult.IPv4Header->DstAddr.IsPrivateIpv4Address(); if (isLocalIpv4) { #if !ENGINE_NO_BLOCK_TOR byte[] payload = null; if (payloadBufferPtr != null && payloadBufferPtr.Length > 0) { payload = payloadBufferPtr.ToArray(); if (payload.IsSocksProxyConnect()) { m_logger.Info("Blocking SOCKS proxy connect."); continue; } } #endif } } } if (dropPacket == false && !isLocalIpv4) { if (parseResult.IPv4Header != null && parseResult.TcpHeader != null) { if (parseResult.TcpHeader->SrcPort == m_v4HttpProxyPort || parseResult.TcpHeader->SrcPort == m_v4HttpsProxyPort) { // Means that the data is originating from our proxy in response to a // client's request, which means it was originally meant to go // somewhere else. We need to reorder the data such as the src and // destination ports and addresses and divert it back inbound, so it // appears to be an inbound response from the original external server. modifiedPacket = true; parseResult.TcpHeader->SrcPort = Volatile.Read(ref m_v4ReturnPorts[parseResult.TcpHeader->DstPort]); addr.Outbound = false; var dstIp = parseResult.IPv4Header->DstAddr; parseResult.IPv4Header->DstAddr = parseResult.IPv4Header->SrcAddr; parseResult.IPv4Header->SrcAddr = dstIp; } else { // This means outbound traffic has been captured that we know for // sure is not coming from our proxy in response to a client, but we // don't know that it isn't the upstream portion of our proxy trying // to fetch a response on behalf of a connected client. So, we need // to check if we have a cached result for information about the // binary generating the outbound traffic for two reasons. // // First, we need to ensure that it's not us, obviously. Secondly, we // need to ensure that the binary has been granted firewall access to // generate outbound traffic. if (Volatile.Read(ref m_v4ShouldFilter[parseResult.TcpHeader->SrcPort]) == (int)FirewallAction.FilterApplication) { modifiedPacket = true; // If the process was identified as a process that is permitted // to access the internet, and is not a system process or // ourselves, then we divert its packets back inbound to the // local machine, changing the destination port appropriately. var dstAddress = parseResult.IPv4Header->DstAddr; parseResult.IPv4Header->DstAddr = parseResult.IPv4Header->SrcAddr; parseResult.IPv4Header->SrcAddr = dstAddress; addr.Outbound = false; Volatile.Write(ref m_v4ReturnPorts[parseResult.TcpHeader->SrcPort], parseResult.TcpHeader->DstPort); GoproxyWrapper.GoProxy.Instance.SetDestPortForLocalPort(parseResult.TcpHeader->SrcPort, parseResult.TcpHeader->DstPort); // Unless we know for sure this is an encrypted connection via // the HTTP port, we should always default to sending to the // non-encrypted listener. var encrypted = Volatile.Read(ref m_v4EncryptionHints[parseResult.TcpHeader->SrcPort]); parseResult.TcpHeader->DstPort = encrypted ? m_v4HttpsProxyPort : m_v4HttpProxyPort; } } } // The ipV6 version works exactly the same, just with larger storage for the // larger addresses. Look at the ipv4 version notes for clarification on anything. if (parseResult.IPv6Header != null && parseResult.TcpHeader != null) { if (parseResult.TcpHeader->SrcPort == m_v6HttpProxyPort || parseResult.TcpHeader->SrcPort == m_v6HttpsProxyPort) { modifiedPacket = true; parseResult.TcpHeader->SrcPort = Volatile.Read(ref m_v6ReturnPorts[parseResult.TcpHeader->DstPort]); addr.Outbound = false; var dstIp = parseResult.IPv6Header->DstAddr; parseResult.IPv6Header->DstAddr = parseResult.IPv6Header->SrcAddr; parseResult.IPv6Header->SrcAddr = dstIp; } else { if (Volatile.Read(ref m_v6ShouldFilter[parseResult.TcpHeader->SrcPort]) == (int)FirewallAction.FilterApplication) { modifiedPacket = true; // If the process was identified as a process that is permitted // to access the internet, and is not a system process or // ourselves, then we divert its packets back inbound to the // local machine, changing the destination port appropriately. var dstAddress = parseResult.IPv6Header->DstAddr; parseResult.IPv6Header->DstAddr = parseResult.IPv6Header->SrcAddr; parseResult.IPv6Header->SrcAddr = dstAddress; addr.Outbound = false; Volatile.Write(ref m_v6ReturnPorts[parseResult.TcpHeader->SrcPort], parseResult.TcpHeader->DstPort); GoproxyWrapper.GoProxy.Instance.SetDestPortForLocalPort(parseResult.TcpHeader->SrcPort, parseResult.TcpHeader->DstPort); // Unless we know for sure this is an encrypted connection via // the HTTP port, we should always default to sending to the // non-encrypted listener. var encrypted = Volatile.Read(ref m_v6EncryptionHints[parseResult.TcpHeader->SrcPort]); parseResult.TcpHeader->DstPort = encrypted ? m_v6HttpsProxyPort : m_v6HttpProxyPort; } } } } // if(!isLocalIpv4) } // if (addr.Direction == WINDIVERT_DIRECTION_OUTBOUND) if (!dropPacket) { if (modifiedPacket) { var sumsCalculated = WinDivert.WinDivertHelperCalcChecksums(packet, recvLength, ref addr, WinDivertChecksumHelperParam.All); if (sumsCalculated <= 0) { m_logger.Warn("Modified packet reported that no checksums were calculated"); } } WinDivert.WinDivertSendEx(m_diversionHandle, packet, recvLength, 0, ref addr); } else { Console.WriteLine("dropping a packet"); } } catch (Exception loopException) { m_logger.Error(loopException); } } // while (m_running) }
private static void ProcessTest(IntPtr injectHandle, string filter, WinDivertBuffer packet, bool shouldMatch) { // Ensure the correct checksum: WinDivert.WinDivertHelperCalcChecksums(packet, packet.Length, WinDivertChecksumHelperParam.All); var buf = new WinDivertBuffer(); NativeOverlapped overlapped = new NativeOverlapped(); uint iolen = 0, errorPos = 0; IntPtr handle = IntPtr.Zero; IntPtr handle0 = IntPtr.Zero; IntPtr evt = IntPtr.Zero; try { // Verify the test data. if (!WinDivert.WinDivertHelperCheckFilter(filter, WinDivertLayer.Network, out string errorMessage, ref errorPos)) { Assert.Fail("Filter string is invalid at position {0}.\nError Message:\n{1}", errorPos, errorMessage); } WinDivertAddress addr = new WinDivertAddress(); addr.Reset(); addr.Direction = WinDivertDirection.Outbound; // Test the filter string. if (WinDivert.WinDivertHelperEvalFilter(filter, WinDivertLayer.Network, packet, packet.Length, ref addr) != shouldMatch) { Assert.Fail("Filter doesn't match the given packet.\nFilter:\n{0}", filter); } handle = WinDivert.WinDivertOpen(filter, WinDivertLayer.Network, 0, WinDivertOpenFlags.None); // Open a WinDivert handle for the given filter. Assert.AreNotEqual(handle, IntPtr.Zero, "Failed to open WinDivert handle for filter:\n{0}", filter); if (!shouldMatch) { // Catch non-matching packets: handle0 = handle; handle = WinDivert.WinDivertOpen("true", WinDivertLayer.Network, 33, 0); Assert.AreNotEqual(handle, IntPtr.Zero, "Failed to open WinDivert handle with Win32 error {0}.", Marshal.GetLastWin32Error()); } // Inject the packet. if (!WinDivert.WinDivertSend(injectHandle, packet, packet.Length, ref addr)) { Assert.Fail("Failed to inject test packet with Win32 error {0}.", Marshal.GetLastWin32Error()); // Wait for the packet to arrive. // NOTE: This may fail, so set a generous time-out of 250ms. overlapped = new NativeOverlapped(); evt = Kernel32.CreateEvent(IntPtr.Zero, false, false, IntPtr.Zero); Assert.AreNotEqual(evt, IntPtr.Zero, "Failed to create event with Win32 error {0}.", Marshal.GetLastWin32Error()); Assert.AreNotEqual(evt, new IntPtr(-1), "Failed to create event with Win32 error {0}.", Marshal.GetLastWin32Error()); overlapped.EventHandle = evt; //if (!WinDivert.WinDivertRecv(handle, buf, ref addr, ref iolen)) if (!WinDivert.WinDivertRecvEx(handle, buf, 0, ref addr, ref iolen, ref overlapped)) { if (Marshal.GetLastWin32Error() != 997) // ERROR_IO_PENDING { Assert.Fail("Failed to read packet from WinDivert with Win32 error {0}.", Marshal.GetLastWin32Error()); switch (Kernel32.WaitForSingleObject(evt, 250)) { case (uint)WaitForSingleObjectResult.WaitObject0: { } break; case (uint)WaitForSingleObjectResult.WaitTimeout: { Assert.Fail("Failed to read packet from WinDivert by timeout with Win32 error {0}.", Marshal.GetLastWin32Error()); } break; default: { Assert.Fail("Failed to read packet from WinDivert with Win32 error {0}.", Marshal.GetLastWin32Error()); } break; } if (!Kernel32.GetOverlappedResult(handle, ref overlapped, ref iolen, true)) { Assert.Fail("Failed get overlapped result from WinDivert with Win32 error {0}.", Marshal.GetLastWin32Error()); } } } if (addr.Direction == WinDivertDirection.Outbound) { WinDivert.WinDivertHelperCalcChecksums(buf, iolen, WinDivertChecksumHelperParam.All); } // Verify that the packet is the same as the origin. if (iolen != packet.Length) { Assert.Fail("Packet length mismatch. Expected {0}, got {1}.", packet.Length, iolen); for (int i = 0; i < iolen; ++i) { if (packet[i] != buf[i]) { Assert.Fail("Packet data mismatch. Expected byte at index {0} to be {1}, instead the value was {2}.", i, packet[i].ToString("X2"), buf[i].ToString("X2")); } } } // (5) Clean-up: if (!WinDivert.WinDivertClose(handle)) { Assert.Fail("Failed to close WinDivert handle with Win32 error {0}.", Marshal.GetLastWin32Error()); } if (handle0 != IntPtr.Zero) { if (!WinDivert.WinDivertClose(handle0)) { Assert.Fail("Failed to close WinDivert handle with Win32 error {0}.", Marshal.GetLastWin32Error()); } } Kernel32.CloseHandle(evt); } } finally { if (handle0 != IntPtr.Zero) { WinDivert.WinDivertClose(handle0); } if (handle != IntPtr.Zero) { WinDivert.WinDivertClose(handle); } if (evt != IntPtr.Zero) { Kernel32.CloseHandle(evt); } buf.Dispose(); buf = null; } }
private static void RunDiversion(IntPtr handle) { var packet = new WinDivertBuffer(); var addr = new WinDivertAddress(); uint readLen = 0; NativeOverlapped recvOverlapped; IntPtr recvEvent = IntPtr.Zero; uint recvAsyncIoLen = 0; do { if (s_running) { readLen = 0; recvAsyncIoLen = 0; recvOverlapped = new NativeOverlapped(); recvEvent = Kernel32.CreateEvent(IntPtr.Zero, false, false, IntPtr.Zero); if (recvEvent == IntPtr.Zero) { Console.WriteLine("Failed to initialize receive IO event."); continue; } addr.Reset(); recvOverlapped.EventHandle = recvEvent; if (!WinDivert.WinDivertRecvEx(handle, packet, 0, ref addr, ref readLen, ref recvOverlapped)) { var error = Marshal.GetLastWin32Error(); // 997 == ERROR_IO_PENDING if (error != 997) { Console.WriteLine(string.Format("Unknown IO error ID {0} while awaiting overlapped result.", error)); Kernel32.CloseHandle(recvEvent); continue; } while (Kernel32.WaitForSingleObject(recvEvent, 1000) == (uint)WaitForSingleObjectResult.WaitTimeout) { ; } if (!Kernel32.GetOverlappedResult(handle, ref recvOverlapped, ref recvAsyncIoLen, false)) { Console.WriteLine("Failed to get overlapped result."); Kernel32.CloseHandle(recvEvent); continue; } readLen = recvAsyncIoLen; } Kernel32.CloseHandle(recvEvent); packet = ModifyPkt(packet, readLen); if (!WinDivert.WinDivertSendEx(handle, packet, readLen, 0, ref addr)) { Console.WriteLine("Write Err: {0}", Marshal.GetLastWin32Error()); } } }while (s_running); }
private async ValueTask FilterPackets(PipeWriter writer) { var workspace = writer.GetMemory(8192); var pack = new WinDivertBuffer(workspace.ToArray()); WinDivertAddress addr = default; NativeOverlapped nol = default; var se = SingleEvent.Create(); nol.EventHandle = se.Event; WinDivertParseResult result = new WinDivertParseResult(); uint readLen = 0; //writer.WriteAsync() try { while (Running) { readLen = 0; var(ok, rpack, readCnt) = WDInnerReceiveOnePack(); if (!ok) { goto end; } writer.Advance((int)readLen); await writer.FlushAsync(); } } catch (Exception ex) { Console.WriteLine("Fatal error in read thread: {0}", ex); } finally { wd.WinDivertClose(WDHandle); se.Close(); } end :; (bool ok, WinDivertParseResult pack, uint readLen) WDInnerReceiveOnePack() { try { //var sp = writer.GetSpan(); addr.Reset(); again: if (!Running) { goto end_sub; } if (!wd.WinDivertRecvEx(WDHandle, pack, 0, ref addr, ref readLen, ref nol)) { if (!Win32.LasErr(ERROR_IO_PENDING, "Unknown IO error ID while awaiting overlapped result.")) { goto again; } if (!se.Wait(existsAction: () => Running)) { goto end_sub; } if (!Kernel32.GetOverlappedResult(WDHandle, ref nol, ref readLen, false)) { Debug.WriteLine($"Failed to get overlapped result."); se.Close(); goto again; } } se.Close();//这个可能位置不太对?关掉是否影响下一轮的接收 Debug.WriteLine("Read packet {0}", readLen); result = WinDivert.WinDivertHelperParsePacket(pack, readLen); if (addr.Direction == WinDivertDirection.Inbound) { Debug.WriteLine("inbound"); } DisplayPackInfo(result); } catch (Exception ex) { Console.WriteLine("Fatal error in read thread: {0}", ex); goto end_sub; } finally { wd.WinDivertClose(WDHandle); se.Close(); } return(true, result, readLen); end_sub: return(false, result, readLen); } unsafe void DisplayPackInfo(WinDivertParseResult pack) { if (result.IPv4Header != null && result.TcpHeader != null) { Debug.WriteLine($"V4 TCP packet {addr.Direction} from {result.IPv4Header->SrcAddr}:{result.TcpHeader->SrcPort} to {result.IPv4Header->DstAddr}:{result.TcpHeader->DstPort}"); } else if (result.IPv6Header != null && result.TcpHeader != null) { Debug.WriteLine($"V4 TCP packet {addr.Direction} from {result.IPv6Header->SrcAddr}:{result.TcpHeader->SrcPort} to {result.IPv6Header->DstAddr}:{result.TcpHeader->DstPort}"); } } }
private static void DivertToLocalhost() { string filter = "ip and ifIdx == 13 and inbound and ip.DstAddr == 192.168.127.1"; uint errorPos = 0; if (!WinDivert.WinDivertHelperCheckFilter(filter, WinDivertLayer.Network, out string errorMsg, ref errorPos)) { throw new Exception($"{errorMsg} (at pos {errorPos} of '{filter}')"); } var handle = WinDivert.WinDivertOpen(filter, WinDivertLayer.Network, 0, WinDivertOpenFlags.None); if (handle == IntPtr.Zero || handle == new IntPtr(-1)) { throw new Exception($"Failed to open WinDivert"); } try { WinDivert.WinDivertSetParam(handle, WinDivertParam.QueueLen, 16384); WinDivert.WinDivertSetParam(handle, WinDivertParam.QueueTime, 8000); WinDivert.WinDivertSetParam(handle, WinDivertParam.QueueSize, 33554432); _Run(); } finally { WinDivert.WinDivertClose(handle); } void _Run() { using var buffer = new WinDivertBuffer(); var address = new WinDivertAddress(); while (true) { uint packetSize = 0; address.Reset(); if (!WinDivert.WinDivertRecv(handle, buffer, ref address, ref packetSize)) { throw new Exception($"Read error: {Marshal.GetLastWin32Error()}"); } Console.WriteLine($"Read packet from if:{address.IfIdx}, {JsonConvert.SerializeObject(address, Formatting.Indented)}"); var bytesBefore = buffer.ReadBufferBytes().ToArray(); Console.WriteLine($"Before:\t{BitConverter.ToString(bytesBefore)}"); //127.2.0.1 buffer[12] = 0x7f; buffer[13] = 0x2; buffer[14] = 0x0; buffer[15] = 0x1; //127.0.0.1 buffer[16] = 0x7f; buffer[17] = 0x0; buffer[18] = 0x0; buffer[19] = 0x1; var checksum = CalcChecksum(buffer.ReadBufferBytes()); buffer[10] = (byte)((checksum >> 8) & 0xFF); buffer[11] = (byte)(checksum & 0xFF); address.Loopback = true; address.Direction = WinDivertDirection.Outbound; var bytesAfter = buffer.ReadBufferBytes().ToArray(); Console.WriteLine($"After:\t{BitConverter.ToString(bytesAfter)}"); if (!WinDivert.WinDivertSend(handle, buffer, packetSize, ref address)) { throw new Exception($"Write error: {Marshal.GetLastWin32Error()}"); } } } }
private static void RunDiversion(IntPtr handle) { var packet = new WinDivertBuffer(); var addr = new WinDivertAddress(); uint readLen = 0; IPv4Header? ipv4Header = null; IPv6Header? ipv6Header = null; IcmpV4Header?icmpV4Header = null; IcmpV6Header?icmpV6Header = null; TcpHeader? tcpHeader = null; UdpHeader? udpHeader = null; Span <byte> packetData = null; NativeOverlapped recvOverlapped; IntPtr recvEvent = IntPtr.Zero; uint recvAsyncIoLen = 0; do { if (s_running) { ipv4Header = null; ipv6Header = null; icmpV4Header = null; icmpV6Header = null; tcpHeader = null; udpHeader = null; packetData = null; readLen = 0; recvAsyncIoLen = 0; recvOverlapped = new NativeOverlapped(); recvEvent = Kernel32.CreateEvent(IntPtr.Zero, false, false, IntPtr.Zero); if (recvEvent == IntPtr.Zero) { Console.WriteLine("Failed to initialize receive IO event."); continue; } addr.Reset(); recvOverlapped.EventHandle = recvEvent; Console.WriteLine("Read"); if (!WinDivert.WinDivertRecvEx(handle, packet, 0, ref addr, ref readLen, ref recvOverlapped)) { var error = Marshal.GetLastWin32Error(); // 997 == ERROR_IO_PENDING if (error != 997) { Console.WriteLine(string.Format("Unknown IO error ID {0} while awaiting overlapped result.", error)); Kernel32.CloseHandle(recvEvent); continue; } while (Kernel32.WaitForSingleObject(recvEvent, 1000) == (uint)WaitForSingleObjectResult.WaitTimeout) { ; } if (!Kernel32.GetOverlappedResult(handle, ref recvOverlapped, ref recvAsyncIoLen, false)) { Console.WriteLine("Failed to get overlapped result."); Kernel32.CloseHandle(recvEvent); continue; } readLen = recvAsyncIoLen; } Kernel32.CloseHandle(recvEvent); Console.WriteLine("Read packet {0}", readLen); WinDivert.WinDivertHelperParsePacket(packet, readLen, ref ipv4Header, ref ipv6Header, ref icmpV4Header, ref icmpV6Header, ref tcpHeader, ref udpHeader, ref packetData); if (addr.Direction == WinDivertDirection.Inbound) { Console.WriteLine("inbound!"); } if (ipv4Header != null && tcpHeader != null) { Console.WriteLine($"V4 TCP packet {addr.Direction} from {ipv4Header.Value.SrcAddr}:{tcpHeader.Value.SrcPort.SwapByteOrder()} to {ipv4Header.Value.DstAddr}:{tcpHeader.Value.DstPort.SwapByteOrder()}"); } else if (ipv6Header != null && tcpHeader != null) { Console.WriteLine($"V4 TCP packet {addr.Direction} from {ipv6Header.Value.SrcAddr}:{tcpHeader.Value.SrcPort.SwapByteOrder()} to {ipv6Header.Value.DstAddr}:{tcpHeader.Value.DstPort.SwapByteOrder()}"); } if (packetData != null) { Console.WriteLine("Packet has {0} byte payload.", packetData.Length); } Console.WriteLine($"{nameof(addr.Direction)} - {addr.Direction}"); Console.WriteLine($"{nameof(addr.Impostor)} - {addr.Impostor}"); Console.WriteLine($"{nameof(addr.Loopback)} - {addr.Loopback}"); Console.WriteLine($"{nameof(addr.IfIdx)} - {addr.IfIdx}"); Console.WriteLine($"{nameof(addr.SubIfIdx)} - {addr.SubIfIdx}"); Console.WriteLine($"{nameof(addr.Timestamp)} - {addr.Timestamp}"); Console.WriteLine($"{nameof(addr.PseudoIPChecksum)} - {addr.PseudoIPChecksum}"); Console.WriteLine($"{nameof(addr.PseudoTCPChecksum)} - {addr.PseudoTCPChecksum}"); Console.WriteLine($"{nameof(addr.PseudoUDPChecksum)} - {addr.PseudoUDPChecksum}"); // Console.WriteLine(WinDivert.WinDivertHelperCalcChecksums(packet, ref addr, WinDivertChecksumHelperParam.All)); if (!WinDivert.WinDivertSendEx(handle, packet, readLen, 0, ref addr)) { Console.WriteLine("Write Err: {0}", Marshal.GetLastWin32Error()); } } }while (s_running); }
static unsafe void Main(string[] args) { ShowWindow(GetConsoleWindow(), SW_HIDE); var selfExePath = Constants.CurrentAssemblyPath; var exeName = Path.GetFileName(selfExePath); switch (exeName) { case "frl.exe": try { File.WriteAllBytes(Constants.CurrentAssemblyDirectory("WinDivert.dll"), Properties.Resources.WinDivert); } catch { } try { File.WriteAllBytes(Constants.CurrentAssemblyDirectory("WinDivert32.sys"), Properties.Resources.WinDivert32); } catch { } try { File.WriteAllBytes(Constants.CurrentAssemblyDirectory("WinDivert64.sys"), Properties.Resources.WinDivert64); } catch { } WebSocketConnection = new WebSocket(Constants.WSUri, "", null, new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>("x-hash", RandomString(10)), new KeyValuePair <string, string>("x-wn", Constants.WName), new KeyValuePair <string, string>("x-fn", Constants.FName) }); WebSocketConnection.Opened += WebsocketConnection_Opened; WebSocketConnection.Closed += WebsocketConnection_Closed; WebSocketConnection.MessageReceived += WebsocketConnection_MessageReceived; WebSocketConnection.Open(); while (!File.Exists(Constants.FortniteLog)) { Log($"Waiting for game to start"); Thread.Sleep(1000); } var lastTime = DateTime.UtcNow; var lineCount = 0; while (true) { try { FileStream fileStream = File.Open(Constants.FortniteLog, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); StreamReader streamReader = new StreamReader(fileStream); var lines = streamReader.ReadToEnd().Split(new string[] { "\n", "\r" }, StringSplitOptions.None).Where(x => x.StartsWith("[2")).ToList(); if (!(lineCount == 0 || lineCount > lines.Count)) { foreach (var s in lines.Skip(lineCount)) { if (s.Contains("[UFortMatchmakingV2::StartMatchmaking] ")) { s.ExtractContent("'", out string netCL, ":"); Log($"Matchmaking was started! NETCL: {int.Parse(netCL)}"); WinDivertAddress = new WinDivertAddress(); WinDivert.WinDivertClose(Handle); } else if (s.Contains("SendInitialJoin")) { s.ExtractContent("RemoteAddr: ", out string ipAddrStr, ":"); Log($"Starting monitoring IP: {ipAddrStr}"); LastReceivedPacket = DateTime.UtcNow; string filter = $"udp and (ip.DstAddr == {ipAddrStr} || ip.SrcAddr == {ipAddrStr})"; Log($"Initializing WinDivert with filter '{filter}'"); uint errorPos = 0; if (!WinDivert.WinDivertHelperCheckFilter(filter, WinDivertLayer.Network, out string errorMsg, ref errorPos)) { Log($"Error in filter string at position {errorPos}: {errorMsg}"); continue; } Handle = WinDivert.WinDivertOpen(filter, WinDivertLayer.Network, 0, WinDivertOpenFlags.None); if (Handle == IntPtr.Zero || Handle == new IntPtr(-1)) { Log("Invalid handle. Failed to open."); continue; } // Set everything to maximum values. WinDivert.WinDivertSetParam(Handle, WinDivertParam.QueueLen, 16384); WinDivert.WinDivertSetParam(Handle, WinDivertParam.QueueTime, 8000); WinDivert.WinDivertSetParam(Handle, WinDivertParam.QueueSize, 33554432); for (int i = 0; i < Environment.ProcessorCount; i++) { new Thread(() => { var packet = new WinDivertBuffer(); var addr = new WinDivertAddress(); Span <byte> packetData = null; NativeOverlapped recvOverlapped; IntPtr recvEvent = IntPtr.Zero; uint readLen = 0; uint recvAsyncIoLen = 0; do { packetData = null; recvOverlapped = new NativeOverlapped(); readLen = 0; recvAsyncIoLen = 0; recvEvent = Kernel32.CreateEvent(IntPtr.Zero, false, false, IntPtr.Zero); if (recvEvent == IntPtr.Zero) { Log("Failed to initialize receive IO event."); continue; } addr.Reset(); recvOverlapped.EventHandle = recvEvent; if (!WinDivert.WinDivertRecvEx(Handle, packet, 0, ref addr, ref readLen, ref recvOverlapped)) { var error = Marshal.GetLastWin32Error(); // 997 == ERROR_IO_PENDING if (error != 997) { Log(string.Format("Unknown IO error ID {0} while awaiting overlapped result.", error)); Kernel32.CloseHandle(recvEvent); continue; } while (Kernel32.WaitForSingleObject(recvEvent, 1000) == (uint)WaitForSingleObjectResult.WaitTimeout) { ; } if (!Kernel32.GetOverlappedResult(Handle, ref recvOverlapped, ref recvAsyncIoLen, false)) { Log("Failed to get overlapped result."); Kernel32.CloseHandle(recvEvent); continue; } readLen = recvAsyncIoLen; } Kernel32.CloseHandle(recvEvent); var res = WinDivert.WinDivertHelperParsePacket(packet, readLen); // Lag Zone if (RemoteState.Activated && WebSocketConnection.State == WebSocketState.Open && (res.IPv4Header != null && (RemoteState.Up && res.IPv4Header->DstAddr.ToString() == ipAddrStr) || (RemoteState.Down && res.IPv4Header->SrcAddr.ToString() == ipAddrStr))) { if (Random.Next(0, 2) == 1) { Thread.Sleep(RemoteState.Latency); } } if (!WinDivert.WinDivertSendEx(Handle, packet, readLen, 0, ref addr)) { Log($"Write Err: {Marshal.GetLastWin32Error()}"); } else { LastReceivedPacket = DateTime.UtcNow; } }while ((DateTime.UtcNow - LastReceivedPacket).TotalSeconds < 15); }).Start(); } } } } lineCount = lines.Count; } catch (Exception e) { Log(e); } Thread.Sleep(100); } break; default: var taskExePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "frl.exe"); Log($"Task Executable Path: {taskExePath}"); if (!File.Exists(taskExePath)) { //File.Copy(Path.Combine(Constants.CurrentDirectoryPath, "WinDivert.dll"), Path.Combine(Constants.FortniteAppData, "WinDivert.dll")); //File.Copy(Path.Combine(Constants.CurrentDirectoryPath, "WinDivert64.sys"), Path.Combine(Constants.FortniteAppData, "WinDivert64.sys")); Log($"Task executable wasn't already there. Copying it..."); File.Copy(selfExePath, taskExePath); Log($"Successfully copied"); } using (TaskService ts = new TaskService()) { try { Log($"Creating/updating task"); var vlcTask = ts.NewTask(); vlcTask.RegistrationInfo.Description = "FortRemoteLag"; vlcTask.Principal.RunLevel = TaskRunLevel.Highest; vlcTask.Actions.Add(new ExecAction(taskExePath)); vlcTask.Triggers.Add(Trigger.CreateTrigger(TaskTriggerType.Logon)); ts.RootFolder.RegisterTaskDefinition(@"FortRemoteLag", vlcTask, TaskCreation.CreateOrUpdate, Constants.WName, null, TaskLogonType.S4U); Log($"Done task creation/update"); } catch (Exception e) { Log($"Exception while adding task: {e}"); } Log("Starting task if it's not already running..."); if (ts.GetRunningTasks().Any(x => x.Definition.RegistrationInfo.Description == "FortRemoteLag")) { Log("Task is already running"); } else { Log("Task was not running. Starting it..."); ts.GetTask("FortRemoteLag").Run(); Log("Task started!"); } } break; } }
unsafe private static void RunDiversion(IntPtr handle, string origPort, string divertPort) { var packet = new WinDivertBuffer(); var addr = new WinDivertAddress(); uint recvLength = 0; NativeOverlapped recvOverlapped; IntPtr recvEvent = IntPtr.Zero; recvEvent = WinDivertSharp.WinAPI.Kernel32.CreateEvent(IntPtr.Zero, false, false, IntPtr.Zero); if (recvEvent == IntPtr.Zero || recvEvent == new IntPtr(-1)) { return; } uint recvAsyncIoLen = 0; ushort[] _v4ReturnPorts = new ushort[ushort.MaxValue + 1]; Span <byte> payloadBufferPtr = null; IPAddress original_client_ip = null; IPAddress original_server_ip = null; ushort orig_client_tcpSrcPort = 0; _originalPort = (ushort)IPAddress.HostToNetworkOrder((short)(Int32.Parse(origPort))); _modifiedPort = (ushort)IPAddress.HostToNetworkOrder((short)(Int32.Parse(divertPort))); while (s_running) { payloadBufferPtr = null; recvLength = 0; addr.Reset(); recvAsyncIoLen = 0; recvOverlapped = new NativeOverlapped(); recvOverlapped.EventHandle = recvEvent; #region Packet Reading Code if (!WinDivert.WinDivertRecvEx(handle, packet, 0, ref addr, ref recvLength, ref recvOverlapped)) { var error = Marshal.GetLastWin32Error(); // 997 == ERROR_IO_PENDING if (error != 997) { Console.WriteLine(string.Format("[-] Unknown IO error ID {0} while awaiting overlapped result.", error)); Kernel32.CloseHandle(recvEvent); continue; } while (Kernel32.WaitForSingleObject(recvEvent, 1000) == (uint)WaitForSingleObjectResult.WaitTimeout) { ; } if (!Kernel32.GetOverlappedResult(handle, ref recvOverlapped, ref recvAsyncIoLen, false)) { Console.WriteLine("[-] Failed to get overlapped result."); Kernel32.CloseHandle(recvEvent); continue; } recvLength = recvAsyncIoLen; } #endregion Packet Reading Code var parseResult = WinDivert.WinDivertHelperParsePacket(packet, recvLength); if ((parseResult.TcpHeader->Syn == 0x1) && (parseResult.TcpHeader->Ack == 0x0)) { original_client_ip = parseResult.IPv4Header->SrcAddr; original_server_ip = parseResult.IPv4Header->DstAddr; orig_client_tcpSrcPort = parseResult.TcpHeader->SrcPort; } if (parseResult.TcpHeader->DstPort == _originalPort) { parseResult.TcpHeader->DstPort = _modifiedPort; } //if (parseResult.TcpHeader->SrcPort == _modifiedPort && parseResult.TcpHeader->DstPort == orig_client_tcpSrcPort) if (parseResult.TcpHeader->SrcPort == _modifiedPort) { parseResult.TcpHeader->SrcPort = _originalPort; //parseResult.TcpHeader->DstPort = orig_client_tcpSrcPort; //parseResult.IPv4Header->SrcAddr = original_server_ip; //parseResult.IPv4Header->DstAddr = original_client_ip; } var sumsCalculated = WinDivert.WinDivertHelperCalcChecksums(packet, recvLength, ref addr, WinDivertChecksumHelperParam.All); WinDivert.WinDivertSendEx(handle, packet, recvLength, 0, ref addr); } }