Skip to content

Instantly share code, notes, and snippets.

@stormouse
Created November 9, 2021 16:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stormouse/5e86467cec8a05c86cfb3f56957e42ab to your computer and use it in GitHub Desktop.
Save stormouse/5e86467cec8a05c86cfb3f56957e42ab to your computer and use it in GitHub Desktop.
Unreliable http request extraction from pcap
void Main()
{
var readerFactory = new PacketReaderFactory();
var packetReader = readerFactory.Create(@"<>.pcap");
IDisplayer displayer = DisplayerFactory.Text(System.Console.Out);
Dictionary<(int, int), HttpRequestCutter> comms = new Dictionary<(int, int), UserQuery.HttpRequestCutter>();
foreach (var packet in packetReader.ReadPackets())
{
Octets octets = packet.ToPDU();
if (octets is EthernetFrame ethFrame)
{
if (ethFrame.Payload is IPv4Packet ipPacket)
{
if (ipPacket.Payload is TCPSegment tcpSegment)
{
//Console.WriteLine(tcpSegment.SourcePort + "->" + tcpSegment.DestinationPort + ":");
//Console.WriteLine(Encoding.UTF8.GetString(tcpSegment.Payload.Bytes.Span));
int tcpDataLength = (ipPacket.TotalLength - (tcpSegment.HeaderLength + ipPacket.HeaderLength) * 4);
if (tcpSegment.DestinationPort == 80 && tcpDataLength > 0)
{
var key = (tcpSegment.SourcePort, tcpSegment.DestinationPort);
if (!comms.ContainsKey(key))
{
comms[key] = new HttpRequestCutter();
}
comms[key].Append(tcpSegment.Payload.Bytes.Span);
}
}
}
}
}
foreach (var kv in comms)
{
kv.Value.Process();
}
}
public class HttpRequestSimple
{
public string HttpHeaderLine;
public List<string> Headers = new List<string>();
public byte[] Body = new byte[0];
}
public class HttpRequestCutter
{
long startPosition = -1;
MemoryStream stream = new MemoryStream();
List<HttpRequestSimple> requests = new List<HttpRequestSimple>();
public void Append(Span<byte> data)
{
if (startPosition == -1 && IsHttpHeader(data))
{
startPosition = stream.Position;
}
stream.Write(data);
}
public void Process()
{
// no HTTP data
if (startPosition < 0) return;
stream.Position = startPosition;
int contentLength;
while ((contentLength = ParseHeaders(stream, out var request)) != 0)
{
ParseBody(stream, contentLength, request);
if (IsHttpHeader(request.HttpHeaderLine))
{
requests.Add(request);
request.Dump();
Encoding.ASCII.GetString(request.Body.Take(20).ToArray()).Dump();
}
}
}
private int ParseHeaders(Stream stream, out HttpRequestSimple request)
{
try
{
var reader = new StreamReader(stream);
long position = stream.Position;
int contentLength = 0;
var line = reader.ReadLine(); position += line.Length + 2;
request = new HttpRequestSimple();
request.HttpHeaderLine = line;
while (line != "")
{
line = reader.ReadLine();
position += line.Length + 2;
if (line != "")
{
request.Headers.Add(line.Trim());
if (line.StartsWith("content-length:", StringComparison.InvariantCultureIgnoreCase))
{
contentLength = int.Parse(line.Split(':')[1].Trim());
}
}
}
stream.Position = position;
return contentLength;
}
catch
{
request = null;
return 0;
}
}
private void ParseBody(Stream stream, int contentLength, HttpRequestSimple request)
{
var reader = new BinaryReader(stream);
request.Body = reader.ReadBytes(contentLength);
}
private byte[] buf = new byte[1024];
private bool IsHttpHeader(string line)
{
try
{
var parts = line.Split(' ');
if (parts.Length == 3 && parts[2].StartsWith("HTTP"))
{
return true;
}
}
catch { }
return false;
}
private bool IsHttpHeader(Span<byte> data)
{
int len = 0;
int m = 1024 < data.Length ? 1024 : data.Length;
for (int i = 0; i < m; i++)
{
buf[i] = data[i];
if (data[i] == (byte)'\n')
{
len = i;
break;
}
}
return len == 0 ? false : IsHttpHeader(Encoding.ASCII.GetString(buf));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment