TCP/IP Sniffer mit pcap
-
Guten Tag,
Ich wollte einen Sniffer schreiben, der TCP/IP-Pakete in einem Netzwerk mit liest. Ich dachte auch eine gewisse Zeit, er würde richtig funktionieren, bis mir aufgefallen ist, dass die Quell IP-Adresse, so scheint es, in die Ziel IP-Adresse rein ragt.//Richtiges Beispiel
IP Protokoll Layer 3:
---------------------
Source Address :192.168.2.103
Destination Address :62.210.82.44
ID :1338
Type :1536
Length :638//Falsches Beispiel
IP Protokoll Layer 3:
---------------------
Source Address :200.255.192.168
Destination Address :2.1.0.0
ID :1540
Type :6656
Length :2048Bei dem falschen Beispiel kann man sehen, dass die Quell Adresse halb als Quell Adresse und halb als Ziel Adresse ausgegeben wird. Richtige und falsche Beispiele treffen durchaus während eines Programmdurchlaufs gleichermaßen auf.
Der folgende Code kümmert sich um das Empfangen und Ausgeben der Pakete.//Callbackfunktion um auf das Empfangen eines Packets zu reagieren //wird von pcap_loop aufgerufen void loop_callback(unsigned char* additional_data, const pcap_pkthdr* packet_header, const unsigned char* packet_buffer) { unsigned char buffer[packet_header->len]; memset(buffer, 0, packet_header->len); memcpy(buffer, packet_buffer, packet_header->len); ip_header* ip_hdr = (ip_header*)(buffer + ETHER_HDR_LEN); tcp_header* tcp_hdr = (tcp_header*)(buffer + ETHER_HDR_LEN + sizeof(ip_header)); dump_IP(buffer + ETHER_HDR_LEN); } //Die Strukturen, die ich verwende struct ether_header { unsigned char ether_destaddr[ETHER_ADDR_LEN]; //Ziel Mac-Address unsigned char ether_srcaddr[ETHER_ADDR_LEN]; //Source Mac-Address unsigned short ether_type; //Ethernet-Pakettyp }; struct ip_header { unsigned char ip_header_len:4; //Headerlänge unsigned char ip_version:4; //Version unsigned char ip_tos; //Diensttyp unsigned short ip_len; //Gesatmlänge unsigned short ip_id; //Identifikationsnummer unsigned char ip_flags; //Flags unsigned char ip_fragment_offset; //Fragmentoffset unsigned char ip_ttl; //Time to Live unsigned char ip_type; //Protokolltype unsigned short ip_checksum; //Prüfsumme unsigned int ip_src_addr; //Quell Ip Addresse unsigned int ip_dest_addr; //Source Ip Addresse }; //Und hier die Funktion um den IP_Header auszugeben void dump_IP(const unsigned char* data_buffer) { ip_header *header = (ip_header*)data_buffer; in_addr src; memset(&src, 0, sizeof(in_addr)); src.s_addr = header->ip_src_addr; in_addr des; memset(&des, 0, sizeof(in_addr)); des.s_addr = header->ip_dest_addr; printf("\tIP Protokoll Layer 3:\n"); printf("\t---------------------\n"); printf("\t\tSource Address :%s\n", inet_ntoa(src)); printf("\t\tDestination Address :%s\n", inet_ntoa(des)); printf("\t\tID :%u\n", ntohs(header->ip_id)); printf("\t\tType :%u\n", ntohs(header->ip_type)); printf("\t\tLength :%u\n", ntohs(header->ip_len)); return; }
Ich hoffe ihr könnt mir helfen.
-
Wikipedia schrieb:
Der IPv4-Header ist normalerweise 20 Bytes lang. Bei Übertragung auf Basis von Ethernet folgt er dem Ethernet-Typfeld, das für IP-Pakete auf 0800 16 festgelegt ist. Auf anderen Übertragungsmedien und Protokollen kann der Header auch der erste Eintrag sein.
Mit einem Shift von zwei Bytes könnte das hinhauen.
Ansonsten solltest du dir einmal anschauen, was für Daten bei dir ankommen, wenn du merkst, dass du da Blödsinn ausliest.Außerdem ist das eher C denn C++.
-
Übrigens:
Dein Interface einfach so auf den Speicher zu pressen ist bestenfalls riskant. Zumindest mein Compiler macht aus deinem IP-Paket einen 20-Byte-Struct, aber woher weiß du, dass andere Compiler irgendwann nicht noch Padding-Bytes zwischen den verschiedenen Elementen hinzufügen? Das kann dir dann auch noch irgendwann den Hals brechen.
-
In meiner Struktur beginnt die Quell Adresse am 12. Byte innerhalb der Struktur, so wie der IP-Header im rfc auch beschrieben wird. Weshalb muss ich den Speicher shiften und weshalb sollte der Compiler Padding Bytes einfügen? Kann man so etwas verhindern?
Einen schönen Abend.
-
DilBahadur schrieb:
In meiner Struktur beginnt die Quell Adresse am 12. Byte innerhalb der Struktur, so wie der IP-Header im rfc auch beschrieben wird.
Wenn du das ausschließt, dann:
dachschaden schrieb:
Ansonsten solltest du dir einmal anschauen, was für Daten bei dir ankommen, wenn du merkst, dass du da Blödsinn ausliest.
Und zwar so komplett. Alle Felder rausgeben mit allen Adressen. Vielleicht liegt es daran, vielleicht auch nicht. Ohne die Rohdaten gesehen zu haben würde ich das weder ausschließen noch bestätigen.
DilBahadur schrieb:
weshalb sollte der Compiler Padding Bytes einfügen?
Klick.
Das Problem ist halt, dass der Zugriff auf Elemente, die nicht auf das natürliche Layout des Prozessors ausgerichtet sind, nicht atomar sind - also mit zusätzlichem Aufwand verknüpft sind. Deswegen kann der Compiler padden. Als Programmierer merkst du davon nichts, solange du dich an die Spielregeln hältst. Ein Interface mit Gewalt auf einen Speicherbereich zu pressen bricht die Spielregeln.DilBahadur schrieb:
Kann man so etwas verhindern?
Hängt vom Compiler ab. Beim VS kannst du in den Projekteinstellungen das Padding deaktivieren, glaube ich. Beim GCC habe ich die Option nicht gesehen (und auch nicht aktiv gesucht), deswegen kann ich dazu nichts sagen.
-
Ich habe das Padding durch #pragma pack(1) und #pragma pack() deaktivieren können, jedoch sind die falschen Pakete nicht verschwunden. Ich habe nun dein Rat befolgt und habe mir den Ethernet Header ausgeben lassen. Nur solche Pakete, welche an den Router gesendet, werden falsch angezeigt.
Ethernet Protokoll Layer 2: --------------------------- Source Address :00:1a:2a:08:c8:ff Destination Address :ff:ff:ff:ff:ff:ff Type :1544 IP Protokoll Layer 3: --------------------- Source Address :200.255.192.168 Destination Address :2.1.0.0 ID :1540 Type :6656 Length :2048 Tcp Protokoll Layer 4: ---------------------- Source Port :0 Destination Port :0 Sequenz nummer :49320 Acknowledge nummer :31415 Flags: :FIN SYN RST PSH ACK URG
Habt ihr noch Ideen, ich bin nämlich ziemlich ratlos.
Einen schönen zweiten Advent.
-
Ja gut, dann habe ich eine starke Vermutung, was das Problem ist: du versucht, ein Nicht-IP-Paket auszulesen.
Wie ich darauf komme? Der Ethernet-Paket-Typ hat mich darauf gebracht. 1544 (0x0608) kenne ich nämlich nicht.
Aber da du wahrscheinlich auf einer Little-Endian-Maschine bist und dein Speicherlayout halt durchdrücken willst, ist die ID verkehrt herum. Denn die ID 0x0806 beschreibt das Address-Resolution-Protokoll. Schau dir mal an, wie das Protokoll im Gegensatz zu IPv4 aufgebaut ist - bei ARP beginnt die IP-Adresse bei Bit 112, bei IP bei Bit 96. Der Unterschied sind die bereits in meinem vorherigen Post vermuteten 2 Byte Shifting (16 Bit).Weißt du, was mich vollends überzeugt? Die letzten beiden Bytes deiner MAC-Adresse sind die ersten beiden Bytes deiner IP-Adresse (0xc8 == 200, 0xff == 255). Bei ARP muss das so sein
-
Guten Abend dachschaden.
Ich danke dir
Da ich mein Socket mit dem TCP-Protokoll initialisiert habe bin ich davon ausgegangen, dass ich nur tcp/ip pakete erhalte.
Aber gut, dass war offensichtlich ein Fehler.
Einen schönen Abend noch.