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 :2048

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

    Quelle.

    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.


Anmelden zum Antworten