/// <summary> /// Driver thread. If the hotkey is activated, it will drop the incoming packets. /// </summary> public unsafe void watchTraffic(object param) { string filter = (string)param; if (filter == "") { Debug.WriteLine("empty filter"); goto Cleanup; } filter = "icmp or " + filter; Debug.WriteLine(filter); IntPtr driver = WinDivert.WinDivertOpen(filter, WinDivertLayer.Network, 0, WinDivertOpenFlags.None); if (driver == new IntPtr(-1)) { Debug.WriteLine("Driver open failed"); goto Cleanup; } IPAddress terminateAddress = new IPAddress(new byte[] { 127, 0, 0, 2 }); WinDivertBuffer packet = new WinDivertBuffer(0xFFFF); uint packetLen = 0; WinDivertAddress address = new WinDivertAddress(); WinDivertParseResult parsed = new WinDivertParseResult(); while (true) { WinDivert.WinDivertRecv(driver, packet, ref address, ref packetLen); parsed = WinDivert.WinDivertHelperParsePacket(packet, packetLen); if (parsed.IPv4Header->DstAddr.Equals(terminateAddress)) { Debug.WriteLine("ICMP signalled termination"); break; } if (!packetsBlocked) { WinDivert.WinDivertSend(driver, packet, packetLen, ref address); } } WinDivert.WinDivertClose(driver); Cleanup: driverThread = null; Debug.WriteLine("Driver terminated"); }
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 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}"); } } }