Nous allons nous concentrer sur les parties de code purement liées à hping3 et scapy. Construction des paquets, émission et réception via hping3 :

proc sendsyn {} {
    global sport dport myip target
    append syn "ip(saddr=$myip,daddr=$target,ttl=255)+"
    append syn "tcp(sport=$sport,dport=$dport,flags=s)"
    hping send $syn
    incr sport
    after 1 sendsyn
}

proc recvsynack {} {
    global lastisn relative_attractor

    set packets [hping recv eth0 0 0]
    foreach p $packets {
        if {![hping hasfield tcp flags $p]} continue
        set isn [hping getfield tcp seq $p]
        if {$relative_attractor} {
                set tisn [expr abs($isn-$lastisn)]
                set lastisn $isn
                set isn $tisn
        }
        #puts "ISN: $isn"
        displaypoint $isn
    }
    after 10 recvsynack
}

Deux fonctions sont utilisées, une pour envoyer des paquets, l'autre pour recevoir les réponses, respectivement sendsyn et recvsynack. Ces fonctions sont appelées par le gestionnaire d'évènement de tcl : envoi d'un paquet toute les millisecondes, réceptions des paquets de réponse toutes le 10 millisecondes. La fonction hping utilisée pour envoyer est send, et recv pour recevoir. Ces deux fonctions traitent des listes tcl au format APD.

Voici la méthode que j'avais choisi initialement avec scapy :

def sendSyns():
   "Envoie des paquets SYN, reception de SYN-ACK"
    port = 1
    while True:
       seq = sr1(IP(dst=hostname,ttl=255)/TCP(flags="S",sport=port,dport=dport),verbose=0).seq
       diff = abs(oldseq - seq)
       oldseq = seq
       port += 1
       displayPoint(diff)

L'avantage de scapy est clairement ici :

seq = sr1(IP(dst=hostname,ttl=255)/TCP(flags="S",sport=port,dport=dport),verbose=0).seq

En une seule ligne, je construis un paquet, je l'envoie, je récupère la réponse et j'en extrait le numéro de séquence. Dans mon programme python, j'aurai donc une unique fonction pour envoyer et recevoir les paquets : sendSyns() Cette fonction est exécutée dans un thread (on utilise pas cette notion de gestionnaire d'évènements tcl).

Cette fonction scapy sr1 qui permet d'envoyer et de recevoir un paquet, c'est très pratique. Mais dans le cas de notre programme, c'est trop lent. Il faut envoyer plusieurs milliers de paquets pour obtenir nos spectrogrammes et c'est beaucoup trop lent d'itérer sur une fonction sr1 de scapy qui enverra nouveau paquet seulement après avoir reçu la réponse du paquet précédent.

J'ai donc utilisé une autre méthode que je vais vous décrire. Mais pour être honnête, dans tous les cas il semble que scapy soit beaucoup plus lent que hping3 pour ce qui concerne la création et l'envoi de paquets. La vitesse n'est surement pas le critère le plus important, mais la différence est suffisamment importante pour être notée :

Envoie de 10000 paquets sous hping3 :

hping3> for { set i 0 } { $i < 10000 } { incr i } { hping send "ip(saddr=192.168.1.2,daddr=192.168.1.1,ttl=255)+tcp(sport=$i dport=222,flags=s)" }

Ceci envoie 10000 paquets TCP avec le flag SYN et la valeur du port source qui incrémente à chaque paquet. Cette opération prend moins de deux secondes sur mon PC.

L'équivalent sous scapy va mettre plus de 3 minutes à envoyer les 10000 paquets :

 >>> for i in range(10000):  send(IP(src="192.168.1.2",dst="192.168.1.1",ttl=255)/TCP(sport=ri,dport=222,flags="S"),verbose=0,inter=0)
 ou
 >>> send(IP(src="192.168.1.2",dst="192.168.1.1",ttl=255)/TCP(sport=range(10000),dport=222,flags="S"),verbose=0,inter=0)

Bref, on l'a compris, ça sera plus lent de générer les spectrogrammes avec scapy, mais avec la fonction sc1, c'est carrément inutilisable, alors voici la solution que j'ai adoptée :

def sendSyns():
    "Envoie des paquets SYN, reception de SYN-ACK"
    sport = 1
    while running==1:
        r = sr(IP(dst=hostname,ttl=255)/ TCP(flags="S", sport=range(sport,sport+nbsyn), dport=dport),
               verbose=0,timeout=0)
        sport += nbsyn
        for snd,rcv in r0:
            diff = abs(oldseq - rcv.seq)
            oldseq = rcv.seq
            displayPoint(diff)

A la place de sr1, j'utilise la fonction sr pour envoyer plusieurs paquets d'un coup, et recevoir l'ensemble des réponses ensuite. le sport=range(sport,sport+nbsyn) va fournir une liste de port à l'argument sport (port source) de l'objet TCP. Ceci à pour conséquence la création de plusieurs paquets. nbsyn correspond au nombre de paquets à envoyer d'une seule fois.

On gagne en vitesse par rapport à sr1 dans la mesure ou on envoi par paquets de nbsyn paquets sans attendre la réponse. J'ai testé avec différentes valeurs (on peut la spécifier par la ligne de commande) mais la création du graphe reste quand même bien plus lente qu'avec le programme sous hping3.

Nombre de lignes de code : vainqueur scapy Vitesse : vainqueur hping3

Le programme complet :

 
 #!/usr/bin/env python
 # -*- coding: iso-8859-15 -*-
 
 # David ROBERT david@ombrepixel.com
 # isn-scaptogram v0.1
 # Fortement inspiré de isn-spectrogram.htcl (projet hping3)
 # Pour comparer les fonctionnalité de scripts de hping3 et python/scapy
 
 from scapy import sr,IP,TCP
 from Tkinter import *
 import threading, sys, time
 
 try:
     hostname = sys.argv[1]
     dport = int(sys.argv[2])
     div = int(sys.argv[3])
     nbsyn = int(sys.argv[4])
 except:
     print "Utilisation: spectoscapy.py <host> <open-tcp-port> <scale> <blocks>"
     print "Exemple: spectoscapy.py www.example.com 80 100000 10"
     print " scale : echelle du spectrogramme"
     print " blocks : nombre de paquets SYN dans un bloc scapy"
     sys.exit(1)
 
 def quit():
     # Pour arreter mon thread
     global running
     running=0
 
 # Definition des paramètres TK
 running=1
 pastcol = {}
 root = Tk()
 root.title("isn-scaptogram")
 root.config(background="#000000")
 frame = Frame(root)
 frame.config(background="#000000")
 frame.pack(side=TOP)
 button = Button(frame, text="QUIT", fg="red", command=quit)
 button.pack(side=LEFT)
 canvas = Canvas(root)
 canvas.config(width=800,height=300)
 canvas.config(background="#000000")
 canvas.pack(fill=BOTH,expand=TRUE)
 canvas.create_rectangle(40,250,139,250,fill="#FFFFFF",width=0)
 canvas.create_text(90,270,fill="#FFFFFF",text=div*100)
 canvas.create_text(10,10,fill="#FFFFFF",text= \
                    "Host : %s, Port : %s Nombre de SYN par blocs: %d" \
                    % (hostname, dport, nbsyn),anchor=W)
 canvas.create_line(1,20,800,20,fill="#FFFFFF")
 def sendSyns():
     "Envoie des paquets SYN, reception de SYN-ACK"
     oldseq=0
     sport = 1
     while running==1:
         r = sr(IP(dst=hostname,ttl=255)/ TCP(flags="S", sport=range(sport,sport+nbsyn), dport=dport),
                verbose=0,timeout=0)
         sport += nbsyn
         for snd,rcv in r[0]:
             diff = abs(oldseq - rcv.seq)
             oldseq = rcv.seq
             displayPoint(diff)
     frame.quit()
 
 def displayPoint(isn):
     "Affiche le ligne sur le spectrogramme"
     isn = isn/div
     y = 50
     x = isn
     if not pastcol.has_key((x,y)):
         pastcol[(x,y)]=0
         graylevel = 0
     else:
         pastcol[(x,y)]+=10
         graylevel = pastcol[(x,y)]
     if graylevel >= 256*3: graylevel = 256*3-1
     if graylevel <= 255:
         b = graylevel
         g = r = 0
     elif graylevel <= 511:
         b = 0
         g = graylevel - 256
         r = 255
     elif graylevel <= 767:
         b = g = 255
         r = graylevel - 512
     canvas.create_rectangle(x,y,x+1,y+170,fill="#%02X%02X%02X" % (r,g,b) ,width=0)
 
 t = threading.Thread(target = sendSyns, args = ())
 t.start()
 
 root.mainloop()