loader
Foto

Tetszőleges hálózati csomag összeállítása, IP és az UDP Checksumjainak a kiszámítása

Ebben a cikkben megnézzük azt, hogy hogyan lehet egy korrekt hálózati csomagot összeállítani. A cél az, hogy a szállítási réteg UDP protokolljának alkalmazásával egy kétbites karaktersorozatot tudjunk kiküldeni a hálózati forgalomba.

Ahhoz, hogy egy jó csomagot ki tudjunk küldeni a hálózati forgalomba, egy hibátlan csomagot kell összeállítani. Ehhez nem elég megadni helyesen a MAC címeket, illetve az IP címeket, TTL-t, stb, hanem a Checksum-okat is korrektül kell kiszámolni. De hogyan számoljuk ki az IP checksum-ot? Illetve, ha a szállítási réteg egyik protokollját, például az UDP-t alkalmazzuk, akkor annak a Checksum-ját hogyan határozzuk  meg, hogy korrekt packetet tudjunk majd összeállítani Python nyelven? 

A kiküldendő hálózati csomag a két MAC címmel és az EtherType értékével kezdődik, majd a "Data" frame következik (1. ábra).

kep
1. ábra   Ethernet frame felépítése
 

Mit tegyünk be a "Data" mezőbe? A 2. ábrán is látható, hogy a Layer 3 mezőben lehet akár az IP, de akár egy ARP protokoll implementációja is. A példánkban legyen egy UDP protokoll (IP része), mert a célunk egy adatnak a kiküldése. A kiküldendő adat legyen a WebElektronika monogramja, azaz: "WE", amelyet a Szállítási réteg (OSI) UDP protokolljával fogunk továbbítani.

kep
2. ábra   Egy socket teljes felépítése
 

A 2. ábrán látható, hogy először össze kell rakni az Ethernet headert, amely a két MAC címből, illetve az EtherType-ból áll. Ezután következik az IP header értékeinek a meghatározása (UDP protokoll miatt), majd végül az UDP header mezőinek az értékeit kell meghatározni.

Tekintettel arra, hogy UDP protokollt fogunk alkalmazni, ezért az EtherType értékadása (0x0800) után beállítjuk az IP header (3. ábra) mezőinek az értékeit.

kep
3. ábra   IP header felépítése
 

Nézzük meg az IP header mezőinek a jelentését, feladatát.

Version: Az IP verziószáma. Ha IPv4-et alkalmazunk, akkor ennek a mezőnek az értéke 4, ha IPv6-ot használunk, akkor az értéke 6
- IHL: A header fejléchossza nem állandó. Ezért meg kell adni a méretét, amelyet ennél a mezőnél adnak meg. Ennek az "egysége" 4 byte. Tehát, ha ennek a mezőnek az értéke 5, akkor a header hossza 20 byte.
DSCP: szolgáltatási minőség
- ECN: Megjelöli a csomagot torlódás esetén, de nem dobja el. Torlódás jelzése
- Total Length: A csomag teljes hossza
Identification: Ennek az azonosítónak a segítségével lehet a szétdarabolt csomagokat összerakni. Minden csomagnak egyedi azonosítója van.
- Flags: Több flag található itt, például a DF beállításával megtiltható az üzenet darabolása. MF: az üzenet több részből áll.
- Fragment Offset: Megadja, hogy a feldarabolt üzenet hol kezdődik a csomagban.
- Time To Live: Élettartam, egy csomagnak mekkora az élettartama.
- Protocol: Itt jelzik, hogy a csomagban milyen protokoll található. Bőbben itt olvashatunk erről. TCP-nél ennek a mezőnek az értéke 6. Az ICMP: 1, IGMP: 2, UDP: 17 (0x11).
- Header Checksum: Ellenőrző összeg, csak a fejlécre számolják (RFC 1071).
- Source IP Address: A forrás IP címe.
- Destination IP Address: A cél IP címe.

 

Adjunk értékeket a különböző mezőknek, amely nem egyszerű, hiszen az átküldendő adatot majd az UDP fejlécben adjuk meg. Ezért tudnunk kell már most az UDP headernek is méretét is, hiszen az IP headerben a teljes csomag hosszát kell megadnunk.
A felsorolásnál látható, hogy a mezők értékeit először decimálisan, majd binárisan, majd végül hexadecimálisan adjuk meg. 

Version: 4
- IHL: 5 (a header hossza 20 byte)
DSCP: 0
- ECN: 0
- Total Length: 30 byte;     (0000 0000 0001 1110);     0x001E
Identification: 1234;     (0000 0100 1101 0010);     0x04D2
- Flags, Fragment Offset: 16384;     (0100 0000 0000 0000);     0x4000
- Time To Live: 64;     (0100 0000);     0x40
- Protocol: 17;     (0001 0001);     0x11
- Header Checksum: ??????
- Source IP Address: 192.168.0.15;     (11000000 10101000 00000000 00010100);     0xC0A8   0x0014
- Destination IP Address: 192.168.0.20;     (11000000 10101000 00000000 00001111);      0xC0A8   0x000F

 

Az IP header tartalma a következő:

0x4500;   0x001E;   0x04D2;   0x4000;   0x4011;   0x????;   0xC0A8;   0x0014;    0xC0A8;    0x000F

Látható, hogy teljesen nem tudjuk még megadni az IP header teljes tartalmát, mert a Checksum-ot még meg kell határozni. A Checksum értéke ezeknek a számoknak az összegének az egyes komplemense.
Tekintettel arra, hogy a Checksum ábrázolási tartománya az IP fejlécében csak 2 byte, és ezeknek a különböző mezők értékeinek az összegét nem fogjuk tudni 2 byte-ban ábrázolni, ezért kettesével adjuk össze ezeket az értékeket, és ha túllépünk a 2 byte ábrázoláson, akkor a "Carry" bitet adjuk hozzá a részeredményhez, és ezt az alsó két byte-ot használjuk tovább a részeredmények összeadásakor.
A részeredményeket "Temp_X"-el jelöljük.

0x4500  ->  0100 0101 0000 0000
0x001E  ->  0000 0000 0001 1110
Temp_1 ->  0100 0101 0001 1110  ->  0x451E

0x451E  ->  0100 0101 0001 1110
0x04D2  ->  0000 0100 1101 0010
Temp_2 ->  0100 1001 1111 0000  ->  0x49F0

0x49F0  ->  0100 1001 1111 0000
0x4000  ->  0100 0000 0000 0000
Temp_3 ->  1000 1001 1111 0000  -> 0x89F0

0x89F0  ->  1000 1001 1111 0000
0xC0A8 ->  1100 0000 1010 1000
Temp_4 ->  0001 0100 1010 1001 1000  ->  14A98   (Carry bit hozzáadása)

0x4A99  ->  0100 1010 1001 1001
0x0014  ->  0000 0000 0001 0100
Temp_5 ->  0100 1010 1010 1101  ->  0x4AAD

0x4AAD ->  0100 1010 1010 1101
0xC0A8 ->  1100 0000 1010 1000
Temp_6 ->  0001 0000 1011 0101 0101  ->  0x10B55   (Carry bit hozzáadása)

0x0B56 ->  0000 1011 0101 0110
0x000F ->  0000 0000 0000 1111
Temp_7 -> 0000 1011 0110 0101  ->  0x0B65   (Carry bit hozzáadása)

 

Minden számot az IP header mezőiből összeadtuk (Temp_7), ennek az értéknek az egyes komplemense lesz a keresett Checksum. Az egyes komplemens megkapható úgy, hogy kivonjuk a 0xFFFF-ből a Temp_7 értékét, de ugyanazt az eredményt kapjuk akkor is, ha minden bitet negálunk.

Temp_7 -> 0000 1011 0110 0101  ->  0x0B65
IP_Chk ->  1111 0100 1001 1010  ->  0xF49A

 

Látható a következő számsorozaton az IP header mezőinek az értékei.

0x4500;   0x001E;   0x04D2;   0x4000;   0x4011;   0xF49A;   0xC0A8;   0x0014;    0xC0A8;    0x000F

 

Határozzuk most meg az UDP header mezőinek az értékeit. Ehhez ismernünk kell a fejléc felépítését (4. ábra). Látható az UDP protokoll fejlécénél, hogy itt már nem adunk meg IP címeket, hanem a cél- és a forrás portszámot, a fejléc méretét, illetve a Checksum-ot (ez nem egyezik meg az előbb kiszámolttal, az az IP Checksum értéke).

kep
4. ábra   UDP header felépítése
 

És hogyan számoljuk ki az UDP fejléc mezőjében szereplő Checksum-ot? Figyelembe kell vennünk a számolás során az IP címeket, viszont ezeket az UDP fejléc nem tartalmazza, de az IP header igen. Ezért nekünk egy ún. pseudo header-t is meg kell adnunk, amely a következő adatokból áll.

- Forrás IP címe (IP): 192.168.0.15;     (11000000 10101000 00000000 00010100);     0xC0A8   0x0014
- Cél IP címe (IP): 192.168.0.20;     (11000000 10101000 00000000 00001111);      0xC0A8   0x000F
- Protokoll (IP): 17;     (0000 0000 0001 0001);     0x0011
- Length (UDP): 10;     (0000 0000 0000 1010);     0x000A

 

Ezeket a számokat kell összeadni, amely az UDP Checksum részösszege lesz.

Részösszeg = 1818E = 0xC0A8 + 0x0014 + 0xC0A8 + 0x000F + 0x0011 + 0x000A

 

A teljes Checksum összeg megállapításához hozzá kell adnunk a részösszeghez a következő mezők értékeit is.

- Source Port (UDP): 22;     (0000 0000 0001 0110);     0x0016
- Destination Port (UDP): 12345;     (0011 0000 0011 1001);     0x3039
- Length (UDP): 10;     (0000 0000 0000 1010);     0x000A
- Adat (UDP): WE;     (W:0101 0111; E:0100 0101);     0x5745

 

Ezeket az értékeket adjuk hozzá az előbb kiszámolt részösszeghez.

UDP_Checksum_Temp = Részösszeg + 0x0016 + 0x3039 + 0x000A + 0x5745
UDP_Checksum_Temp = 0x2092C;     (0010 0000 1001 0010 1100)

Vegyük észre, hogy az átmeneti eredményt nem tudjuk 2 byte-on ábrázolni, hanem 3 byte kell ehhez, viszont az UDP Header-ben a Checksum eltárolásához csak 2 byte van. Ezért az alsó 2 byte-ot adjuk hozzá a felső 2 byte-hoz.

   0000 0000 0000 0010  ->  0x0002
+ 0000 1001 0010 1100  ->  0x092C

   0000 1001 0010 1110  ->  0x092E

 

Végül ezt a számot ki kell vonnunk a 0xFFFF-ből (egyes komplemens), és megkapjuk az UDP header-ben lévő Checksum értékét. Akár bitenként is negálhattuk volna az eredményt, hiszen a részeredmény egyes komplemense a Checksum.

UDP_Checksum = 0xF6D1 = 0xFFFF - 0x092E

 

Az UDP header mezőinek az értéke a következő.

0x0016   0x3039   0x000A   0xF6D1   0x5745

 

Az IP és az UDP fejlécek tartalma a következő:

0x4500;   0x001E;   0x04D2;   0x4000;   0x4011;   0xF49A;   0xC0A8;   0x0014;    0xC0A8;    0x000F, 0x0016   0x3039   0x000A   0xF6D1   0x5745

 

Ehhez a sorozathoz kell hozzáadni a MAC címeket és az EtherType értékeket. A teljes kiküldendő adat karakterenéknt felírva a következő (elhagya a "0X" előtagot):

12 34 56 78 9A BC BC 9A 78 56 34 12 08 00 45 00 00 1E 04 D2 40 00 40 11 F4 9A C0 A8 00 14 C0 A8 00 0F 00 16 30 39 00 0A F6 D1 57 45

 

Ennek segítségével elküldtük a 192.168.0.15 IP címről a "WE" adatot a 192.168.0.20 IP címre.

A következő részben megírjuk azt a Python programot, amelynek segítségével ezeknek az adatoknak az alkalmazásával egy korrekt UDP csomag küldhető ki a hálózatra.

 



Egyéb cikkek

Mappa

További cikkeink ebben a témakörben

Régebbi cikkeink

Elkezdünk egy sorozatot, amelyhez a Kali Linux használata elengedhetetlen lesz, de természetesen folytatjuk a C# nyelv aszinkron használatának a bemutatását is. Az IT biztonság (amely elég széles területet foglal magába) egyik elengedhetetlen eszköze. . . .

Az nmap (grafikus megjelenítésnél a ZenMap) használata az IT biztonság, illetve az üzemeltetés területén dolgozó szakembereknél szinte elkerülhetetlen. Az ingyenes szoftver segítségével tesztelhetők a számítógépeink, a számítógéphálózatunk, vizsgálha. . . .

A saját informatikai rendszerünk tesztelésénél előfordulhat, hogy meg kell változtatni a MAC címünket. Hogyan tehetjük ezt meg a Kali Linux segítségével? Megnézzük most ezt ebben a cikkben.. . . .