loader
Foto

PCAP file-ok analízise C# nyelv segítségével

Korábban megnéztünk két sniffer alkalmazást, amelyeket Python nyelven írtunk meg. Azokkal az alkalmazásokkal kényelmesen tudtunk különböző protokollokat detekálni a hálózaton, és írtunk ki a konzolra különböző tulajdonságokat, például a fizikai címeket, CRC-ket, stb. Most egy olyan programot készítünk el C# nyelven, ahol nem a hálózati kártyáról vesszük "le" a forgalmat, hanem egy *.pcap file tartalmát olvassuk ki és dolgozzuk fel.

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). 

kep
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).

kep
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).

kep
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.

kep
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.

kep
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.

kep
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ó.

kep
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.

kep
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. 

kep
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.

kep
10. ábra   "Az "ICMP" protokoll adatai
 

 



Egyéb cikkek

Mappa

További cikkeink ebben a témakörben

Régebbi cikkeink

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.. . . .