Skip to content

Commit

Permalink
Add network sniffer feature
Browse files Browse the repository at this point in the history
  • Loading branch information
jlucansky committed Jul 24, 2020
1 parent 93b8498 commit 2d3d985
Show file tree
Hide file tree
Showing 30 changed files with 1,956 additions and 31 deletions.
22 changes: 18 additions & 4 deletions Source/Swiddler/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
using Swiddler.ViewModels;
using Swiddler.Views;
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;

namespace Swiddler
Expand Down Expand Up @@ -36,10 +35,25 @@ protected override void OnStartup(StartupEventArgs e)

EnsureUserFolders();

if (e.Args.FirstOrDefault()?.Equals("-nonew", StringComparison.OrdinalIgnoreCase) == true)
Task.Run(() => Firewall.Instance.GrantAuthorizationToSelf());

var firstArg = e.Args.FirstOrDefault();

if (firstArg?.Equals("-nonew", StringComparison.OrdinalIgnoreCase) == true)
{
new MainWindow().Show();
}
else if (firstArg?.Equals("-settings", StringComparison.OrdinalIgnoreCase) == true && e.Args.Length == 2)
{
var cs = ConnectionSettings.Deserialize(File.ReadAllBytes(e.Args[1]));
var session = cs.CreateSession();
if (session != null)
{
var mainWindow = new MainWindow();
mainWindow.Show();
mainWindow.AddSessionAndStart(session);
}
}
else
{
Rect rect = Rect.Empty;
Expand Down
2 changes: 1 addition & 1 deletion Source/Swiddler/Channels/MonitorChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ void NewCapturedPacked(CapturedPacket capPacket)
static string GetSessionName(CapturedPacket capPacket)
{
if (capPacket.LocalEndPoint != null && capPacket.RemoteEndPoint != null)
return $"{capPacket.Protocol.ToString().Substring(0, 1)} :{ capPacket.LocalEndPoint.Port } -> {capPacket.RemoteEndPoint}";
return $"{capPacket.Protocol.ToString().Substring(0, 1)} :{ capPacket.LocalEndPoint.Port } > {capPacket.RemoteEndPoint}";
else
return $"0x{capPacket.Handle:X8}";
}
Expand Down
240 changes: 232 additions & 8 deletions Source/Swiddler/Channels/SnifferChannel.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,240 @@
using System;
using Swiddler.Common;
using Swiddler.DataChunks;
using Swiddler.NetworkSniffer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Windows;

namespace Swiddler.Channels
{
class SnifferChannel
public class SnifferChannel : Channel, IDisposable
{
/*
https://github.com/proxytype/NetworkSnifferLib
public IPAddress LocalAddress { get; set; }
public Tuple<IPEndPoint, ProtocolType>[] CaptureFilter { get; set; }

*/

private Socket socket;

readonly byte[] RCVALL_ON = new byte[4] { 1, 0, 0, 0 }; // promiscuous mode
readonly byte[] RCVALL_IPLEVEL = new byte[4] { 3, 0, 0, 0 };

private readonly byte[] buffer = new byte[0x10000];

private readonly PacketReassembly reassembly = new PacketReassembly();

private readonly Dictionary<ConnectionKey, Mediator> connections = new Dictionary<ConnectionKey, Mediator>();

class ConnectionKey
{
public IPEndPoint LocalEP, RemoteEP;

public ConnectionKey(RawPacket raw, IPAddress localIP)
{
if (raw.Source.Address.Equals(localIP))
{
LocalEP = raw.Source;
RemoteEP = raw.Destination;
}
else
{
LocalEP = raw.Destination;
RemoteEP = raw.Source;
}
}

public override bool Equals(object obj)
{
if (!(obj is ConnectionKey other)) return false;

if (LocalEP.Equals(other.LocalEP) && RemoteEP.Equals(other.RemoteEP))
return true;
if (RemoteEP.Equals(other.LocalEP) && LocalEP.Equals(other.RemoteEP))
return true;

return false;
}

public override int GetHashCode()
{
var sh = LocalEP.GetHashCode();
var dh = RemoteEP.GetHashCode();
if (sh < dh) return sh ^ dh; else return dh ^ sh;
}
}

private class Mediator : Channel
{
public ConnectionKey EP { get; set; }
public bool IsServer { get; set; }
public Mediator(Session session) : base(session) { }
protected override void OnReceiveNotification(Packet packet) => throw new NotImplementedException();
public void Send(Packet packet) => NotifyObservers(packet); // write to session UI
}

public SnifferChannel(Session session) : base(session) { }

protected override void StartOverride()
{
var ip = LocalAddress;

foreach (var item in CaptureFilter)
{
reassembly.Filter.Add(item.Item1, item.Item2);
}

try
{
if (ip.AddressFamily == AddressFamily.InterNetworkV6)
{
// IPv6 support is experimental - currently doesn't work because IP headers are not captured
socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Raw, ProtocolType.Raw);
socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.HeaderIncluded, true);
}
else
{
try
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.AccessDenied)
{
StartAsAdmin();
throw;
}
}

socket.Bind(new IPEndPoint(ip, 0));
socket.IOControl(IOControlCode.ReceiveAll, RCVALL_IPLEVEL, null);

BeginReceive();
}
catch (Exception ex)
{
HandleError(ex);
}
}

void StartAsAdmin()
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
if (MessageBox.Show(
"You don’t have permission to create raw sockets.\n\nDo you want to launch Swiddler as Administrator?",
"Access Denied", MessageBoxButton.YesNo, MessageBoxImage.Exclamation) == MessageBoxResult.Yes) Session.StartAsAdmin();
}));
}

void BeginReceive()
{
try
{
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null);
}
catch (Exception ex)
{
HandleError(ex);
}
}

private void ReceiveCallback(IAsyncResult result)
{
try
{
int received = socket.EndReceive(result);

if (received > 0)
{
byte[] data = new byte[received];
Array.Copy(buffer, 0, data, 0, received);

var packets = reassembly.GetPackets(data);

if (packets?.Length > 0)
{
foreach (var raw in packets)
{
var packet = new Packet() { Payload = new byte[raw.DataLength] };
Array.Copy(raw.Buffer, raw.HeaderLength, packet.Payload, 0, raw.DataLength);

// TODO: raw.Dropped

var mediator = GetChild(raw);

packet.LocalEndPoint = mediator.EP.LocalEP;
packet.RemoteEndPoint = mediator.EP.RemoteEP;

if (packet.LocalEndPoint.Equals(raw.Source))
packet.Flow = TrafficFlow.Outbound;
else
packet.Flow = TrafficFlow.Inbound;

mediator.Send(packet);

if (raw.Flags.HasFlag(TCPFlags.RST) || raw.Flags.HasFlag(TCPFlags.FIN))
{
mediator.Session.Stop();
}
}
}
}

BeginReceive();
}
catch (Exception ex)
{
HandleError(ex);
}
}

Mediator GetChild(RawPacket raw)
{
var ep = new ConnectionKey(raw, LocalAddress);
lock (connections)
{
if (connections.TryGetValue(ep, out var mediator) == false)
{
var child = Session.NewChildSession();

Session.Storage.Write(new MessageData() { Text = $"New connection observed {raw.Source} -> :{raw.Destination}", Type = MessageType.Connecting });

mediator = new Mediator(child) { EP = ep };

string protoStr = raw.Protocol.ToString().ToUpperInvariant();

if (raw.Destination.Address.Equals(LocalAddress))
{
child.Name = $"{raw.Source} > :{raw.Destination.Port} - {protoStr}";
mediator.IsServer = true; // when first packet is directed toward local IP, then local EP is probably server
}
else
{
child.Name = $":{raw.Source.Port} > {raw.Destination} - {protoStr}";
}

mediator.Observe(child.SessionChannel); // received packets write to session Log

child.Start(); // immediately set session state to stared

child.ResolveProcessIdAsync(ep.LocalEP);

connections.Add(ep, mediator);
}

return mediator;
}
}

protected override void OnReceiveNotification(Packet packet)
{
throw new NotImplementedException();
}

public void Dispose()
{
socket?.Dispose();
socket = null;
}
}
}
2 changes: 1 addition & 1 deletion Source/Swiddler/Channels/TcpChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ protected override void OnReceiveNotification(Packet packet)
HandleError(ex);
}
}

public virtual void Dispose()
{
NetworkStream?.Dispose();
Expand Down
3 changes: 1 addition & 2 deletions Source/Swiddler/Common/Injector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,7 @@ byte[] content()

static string GetExecutingDirectoryName()
{
var location = new Uri(Assembly.GetEntryAssembly().GetName().CodeBase);
return new FileInfo(location.LocalPath).Directory.FullName;
return new FileInfo(Assembly.GetEntryAssembly().GetLocalPath()).Directory.FullName;
}

string GetVersion()
Expand Down
Loading

0 comments on commit 2d3d985

Please sign in to comment.