A hálózati forgalom figyelésére több alkalmazás is a rendelkezésre áll, ilyen a Wireshark, a Tcpdump, stb. A WebElektronikán is megjelent két olyan alkalmazás, amelyek Python nyelven készültek, és a hálózati forgalom figyelhető meg úgy, hogy az adatokat a hálózati kártyáról olvassuk ki.
Most azt nézzük meg, hogy C# nyelven hogyan tudunk *cap/*.pcap filo-kat olvasni. Ezekben a file-ban taláhatók meg a hálózati forgalom adatai (pl.: időpont, protokoll, adat, stb.).
A különböző, a cikk írásakor felhasznált *.cap/*.pcap file-okat itt érjük el:
https://wiki.wireshark.org/SampleCaptures
Ahhoz, hogy egyszerűen, kényelmesen tudjunk írni pcap file analizálásra alkalmas programot C# nyelven, le kell töltenünk a kökvetkező elérhetőségen a PcapPacket.cs és a PcapFile.cs file-okat:
http://www.timpinkawa.net/misc/easypcap.html
Indítsuk el a Visual Studio 2019-et, és hozzunk létre egy Console projektet. Ezután a projektünkbe vegyük fel a két letöltött C# file-t, és adjuk hozzá a két file-nak a névterét a Program osztályhoz (1. ábra).
1. ábra A projekt felépítése
Ezután módosítsuk a Main() metódus tartalmát a következő példának megfelelően, majd a wireshrok.org oldalról töltsük le a "http.cap" tesztfile-t.
class Program
{
static void Main(string[] args)
{
PcapFile pf = new PcapFile("http.cap");
foreach (var item in pf)
{
Console.WriteLine("Seconds: {0}, uSec: {1}, Length: {2}", item.Seconds, item.Microseconds, item.Data.Length);
//Console.WriteLine("-----");
foreach (var itemData in item.Data)
{
//Console.Write(itemData.ToString("X") + " ");
Console.Write(Convert.ToChar(itemData));
}
Console.WriteLine("\n-------------------------");
}
}
}
Ezután futtassuk az alkalmazásunkat debug módban, és a következő eredményt kapjuk (2. ábra).
2. ábra A "HTTP" file tartalma, láthatók a HTML kódok
Módosítsuk most a foreach ciklus tartalmát a következő részlet szerint.
foreach (var itemData in item.Data)
{
Console.Write(itemData.ToString("X") + " ");
//Console.Write(Convert.ToChar(itemData));
}
Futtassuk újra az alkalmazásunkat debug módban. és a HTML kódok "text" helyett hexadecimálisan kerülnek megjelenítésre (3. ábra).
3. ábra A "HTTP" protokoll hexadecimális megjelenítése
Bővítsük most az alkalmazásunkat úgy, hogy ne csak a HTTP protokollt használó adatokat tudjuk megjeleníteni a különböző *.cap file-okból, hanem az "ARP"-ot, "TCP"-t, "UDP"-t, stb. Ehhez különböző metódusokat kell írni, amelyeket egy másik osztályban, a "protocols" osztályban kerülnek implementálásra. A wireshark.org oldalról több példa file-t töltöttünk le, amelyeket úgy tudunk külön-külön kipróbálni, hogy többször példányosítjuk "pf" néven a "PcapFile" osztályt különböző file megadásával, de csak egyet nem kommentelünk ki.
A foreach ciklust kiegészítettük feltételes szerkezetekkel, amelyek segítségével megvizsgáljuk a file-ból kiolvasott adott protokollt (pl.: ARP, IP, stb), illetve, ha a kérdéses protokoll az IP, akkor az milyen protokollt tartalmaz (ICMP, TCP, UDP). Ehhez az EtherType mezőnek a tartalmát kell megvizsgálnunk.
class Program
{
static void Main(string[] args)
{
//PcapFile pf = new PcapFile("arp-storm.pcap");
//PcapFile pf = new PcapFile("http.cap");
//PcapFile pf = new PcapFile("vn-top5-n2-nc-udp1.pcap");
PcapFile pf = new PcapFile("hp-erm-1.cap");
foreach (var item in pf)
{
Console.WriteLine("\n-----------------------");
Console.WriteLine("Packet: " + item.Seconds + "\t" + item.Microseconds + "\t" + item.Data.Length);
// EtherType = ARP (0x0806)
if (item.Data[12] == 8 && item.Data[13] == 6)
protocols.protocolsARP(item.Data);
// EtherType = IPv4 (0x0800)
if (item.Data[12] == 8 && item.Data[13] == 0)
{
protocols.IPv4(item.Data);
// ICMP, 1)
if (item.Data[23] == 1)
protocols.protocolsICMP(item.Data);
// TCP, 6)
if (item.Data[23] == 6)
protocols.protocolsTCP(item.Data);
// UDP, 17)
if (item.Data[23] == 17)
protocols.protocolsUDP(item.Data);
}
}
}
}
Ahhoz, hogy megértsük a feltételes szerkezetekben lévő számokat (0x0800, 0x0806), meg kell ismernünk az Ethernet frame felépítését, amelyet a következő ábrán találunk.
4. ábra Ethernet frame felépítése, az "EtherType" mezőben találjuk meg a protokollok típusát
Ha az EtherType értéke 0x0806, akkor ARP protokollt kell kiértékelnünk, ha 0x0800, akkor az IP protokoll fejlécének a felépítését kell figyelembe vennünk, amikor a feldolgozandó hálózati csomagot dolgozunk fel.
Ha az EtherType értéke 0x0806, akkor a Program osztályban meghívásra kerül a protocols osztály protocolsARP() metódusa, ahol az ARP fejlécének (5. ábra) megfelelően "szétszedjük" a kiolvasott hálózati adatot.
5. ábra Az ARP protokoll fejléce
Viszont ha az EtherType értéke 0x0800, akkor az adott file-ból kiolvasott csomag IP protokollt tartalmaz. Az IP header tartalmaz egy "protocols" mezőt, ahol megtaláljuk az alkalmazott konkrét protokollt. Ha ennek a mezőnek az értéke "1", akkor ICMP protokoll fejlécének (6. ábra) a felépítését kell ismernünk ahhoz, hogy az elkapott hálózati adatforgalmat analizálni tudjuk.
6. ábra Az ICMP protokoll fejléce
A "protocols" osztályban találjuk meg azokat a metódusokat, amelyeknek a segítségével a különböz protokollok fejléceinek a kiértékelése történik.
class protocols
{
public static void IPv4(byte[] tomb)
{
Console.WriteLine("IPv4");
Console.WriteLine(" Total Length: {0}{1}", tomb[16], tomb[17]);
Console.WriteLine(" Time To Live: {0}", tomb[22]);
Console.WriteLine(" Protocol: {0}", tomb[23]);
Console.WriteLine(" Header Checksum: {0}{1}", tomb[24].ToString("X"), tomb[25].ToString("X"));
Console.WriteLine(" Source IP Addr.: {0}.{1}.{2}.{3}", tomb[26], tomb[27], tomb[28], tomb[29]);
Console.WriteLine(" Destination IP Addr.: {0}.{1}.{2}.{3}", tomb[30], tomb[31], tomb[32], tomb[33]);
}
public static void protocolsARP(byte[] tomb)
{
Console.WriteLine("ARP");
Console.WriteLine(" Hardver Type: {0}{1}",tomb[14], tomb[15]);
Console.WriteLine(" Protocol Type: {0}{1}", tomb[16], tomb[17]);
Console.WriteLine(" Hardware Addr. Length: {0}", tomb[18]);
Console.WriteLine(" Protocol Addr. Length: {0}", tomb[19]);
Console.WriteLine(" Sender Hardware Addr.: {0}:{1}:{2}:{3}:{4}:{5}", tomb[22], tomb[23], tomb[24], tomb[25], tomb[26], tomb[27]);
Console.WriteLine(" Sender IP Addr.: {0}:{1}:{2}:{3}", tomb[28], tomb[29], tomb[30], tomb[31]);
Console.WriteLine(" Target Hardware Addr.: {0}:{1}:{2}:{3}:{4}:{5}", tomb[32], tomb[33], tomb[34], tomb[35], tomb[36], tomb[37]);
Console.WriteLine(" Target IP Addr.: {0}:{1}:{2}:{3}", tomb[38], tomb[39], tomb[40], tomb[41]);
}
public static void protocolsTCP(byte[] tomb)
{
int SP = tomb[34] * 256 + tomb[35];
int DP = tomb[36] * 256 + tomb[37];
int WS = tomb[48] * 256 + tomb[49];
Console.WriteLine(" TCP");
Console.WriteLine(" Source Port: {0}", SP);
Console.WriteLine(" Destination Port: {0}", DP);
Console.WriteLine(" Window Size: {0}", WS);
string str = Convert.ToString(tomb[47], 2).PadLeft(8, '0');
Console.WriteLine(" CWR: {7}, ECE: {6}, URG: {5}, ACK: {4}, PSH: {3}, RST: {2}, SYN: {1}, FIN: {0}", str[7], str[6], str[5], str[4], str[3], str[2], str[1], str[0]);
}
public static void protocolsUDP(byte[] tomb)
{
int SP = tomb[34] * 256 + tomb[35];
int DP = tomb[36] * 256 + tomb[37];
int Length = tomb[38] * 256 + tomb[39];
int Chksum = tomb[40] * 256 + tomb[41];
Console.WriteLine(" UDP");
Console.WriteLine(" Source Port: {0}", SP);
Console.WriteLine(" Destination Port: {0}", DP);
Console.WriteLine(" Length: {0}", Length);
Console.WriteLine(" Checksum: {0}", Chksum.ToString("X"));
}
public static void protocolsICMP(byte[] tomb)
{
int Type = tomb[34];
int Code = tomb[35];
int Length = tomb[38] * 256 + tomb[39];
int Chksum = tomb[40] * 256 + tomb[41];
Console.WriteLine("ICMP");
Console.WriteLine(" Type: {0}", Type);
Console.WriteLine(" Code: {0}", Code);
}
}
Futtassuk most a projektünket debug módban először úgy, hogy az "arp-storm.pcap" file-t adjuk meg a példányosítás során. Az eredmény a 7. ábrán látható.
7. ábra ARP protokoll
Az EtherType értéke ebben az esetben 0x0806, ezért a protocols osztály protocolsARP() metódusa kerül meghívásra a Program osztály Main() metódusában. A protocolsARP() metódusban az átadott tömb adott indexű elemeit olvassuk ki és jelenítjük meg a konzolon. Ehhez fel kellett használni az 5. ábrán látható ARP header felépítését is.
A PcapFile osztály példányosításakor most a "http.cap" file-t használjuk, és futtassuk újra az alkalmazásunkat debug módban. Az eredményt a 8. ábrán láthatjuk, ahol megtaláljuk az alkalmazott TCP protokoll különböző adatait, amelyeket a protocols osztály protocolsTCP() metódusával olvastunk ki.
8. ábra A "TCP" protokoll fejléceiből kiolvasott adatok
Most a "pf" példányt úgy állítsuk elő, hogy a "PcapFile" konstruktorának a "vn-top5-n2-nc-udp1.pcap" cap file-t adjuk át a példányosításkor. Ebben a file-ban megtalálhatók az "ARP" és az "UDP" protokollok, amelyeknek a fejléceiből kiolvasott adatokat találjuk meg a 9. ábrán.
9. ábra "UDP" és "ARP protokoll
Végül adjuk meg a "hp-erm-1.cap" cap file-t a példányosítánál, és futtassuk újra az alkalmazásunkat debug módban. Ekkor a protocols osztály protocolICMP() metódusa kerül meghívásra a Main() metódusban, hiszen ez a file ICMP protokollt tartalmaz.
10. ábra "Az "ICMP" protokoll adatai
Ebben a cikkben bemutatjuk a metódusok alapjait. Nem érintjük viszont például a túlterhelést, ezt egy következő részben tekintjük át.. . . .
A sorozatunknak ebben a részében átnézzük általánosságban az osztályok alapjait egy konzolalkalmazás segítségével. A konstruktorok viszont a következő részben kerülnek bemutatásra.. . . .
Ebben a cikkben nem csak a metódusok túlterhelését nézzük meg egy konkrét példa segítségével, hanem szóba kerülnek a generikusok is, szintén egy kisebb projekt keretén belül.. . . .