Générer la liste des classes et des fonctions de Scapy

Est-il nécessaire de présenter Scapy ? Ce module est l’un des préférés des hackers. Avec une facilité déconcertante, il permet de forger des paquets à tous les niveaux des couches protocolaires, ouvrant la porte au spoofing, au sniffing, et autres joyeusetés.
Scapy, pour sniffer, spoofer et autres
Etrangement, l’API de Scapy n’est pas véritablement documentée, en ceci qu’il est impossible de trouver une description du modèle objet et tout ce qui l’accompagne, en particulier les signatures des fonctions.
Fort heureusement, il suffit de deux lignes de Python pour pallier le problème…

UML, pour les petits…

La documentation officielle de Scapy est en fait un didacticiel, intéressant à lire, mais qui laisse en pratique sur sa faim, car il ne donne pas à voir tout le potentiel du module comme le ferait un guide de référence du développeur documentant précisément l’API.
Toutefois, il est possible de suivre les instructions prodiguées ici pour générer un diagramme UML sous forme d’un fichier PNG pour une classe donnée.
Pour être bien complet, il faut rajouter commment installer le module pyreverse sur Windows :
  • télécharger GraphViz et désarchiver le ZIP dans un répertoire quelconque qu’il faut va rajouter à la variable d’environnement PATH de Windows ;
  • depuis la ligne de commandes, installer le module avec pip : pip install pyreverse ;
Quoi qu’il en soit, la mise en oeuvre de cette technique produit ce type de diagramme pour Scapy 2.4.0 :
Un diagramme UML d'une classe de Scapy
Il faudrait réitérer la démarche pour chaque classe afin d’obtenir un vision complète du modèle objet de Scapy. En vérité, cela présente peu d’intérêt, pour plusieurs raisons :
  • UML n’a jamais servi que les rond-de-cuir, pour produire la paperasse leur permettant de justifier leur existence sans mettre les mains dans le cambouis ;
  • les classes sont assez nombreuses, et il faudrait déjà en connaître la liste pour parvenir à en produire les diagrammes UML ;
  • disposer du modèle objet ne donne pas de vision sur ce qui est en dehors, à savoir les fonctions qui ne sont pas des méthodes.

…et une liste à plat, pour les grands

En matière de documentation d’API, rien ne vaut que de générer un bonne liste à plat de fonctions et de classes.
Pour ce qui concerne la liste des fonctions, il suffit d’écrire un petit programme en Python qui s’appuie sur la réflection, c’est-à-dire le module inspect :
import inspect
from scapy.all import *

for name, value in inspect.getmembers(scapy.all, inspect.isfunction):
	spec = inspect.getfullargspec(value)
	if 'return' in spec.annotations:
		print (name, '() => ', spec.annotations['return'], sep='')
	else:
		print (name, '()', sep='')
	if spec.defaults is None:
		nbDefaults = 0
	else:
		nbDefaults = len(spec.defaults)
	for i in range(len (spec.args)):
		if i > (len(spec.args) - nbDefaults):
			print ('\t', spec.args[i], '=', spec.defaults[i - len(spec.args)], sep='')
		else:
			print ('\t', spec.args[i], sep='')
	for kwonlyarg in spec.kwonlyargs:
		if kwonlyarg in spec.kwonlydefaults:
			print ('\t"', kwonlyarg, '"=', spec.kwonlydefaults[kwonlyarg], sep='')
		else:
			print ('\t"', kwonlyarg, '"', sep='')
	if spec.varargs is not None:
		print ('\t*', spec.varargs, sep='')
	if spec.varkw is not None:
		print ('\t**', spec.varkw, sep='')
La liste des fonctions peut être générée bien plus rapidement en utilisant la fonction signature(), mais cela ne permet pas de distinguer les paramètres pour mettre en forme le résultat dans l’objectif de produire une référence lisible.
Dans une version extrêmement condensée qui exploite les compréhensions, cela donne :
import inspect
from scapy.all import *

print ('\nFunctions\n')
print ('\n'.join(sorted ([m[0] + ' ' + str(inspect.signature(m[1])) + ' [' + inspect.getfile(m[1])[len('C:\\Python36\\lib\\site-packages\\scapy\\'):] + ']' for m in inspect.getmembers(scapy.all, inspect.isfunction)])))
De la même manière, une ligne de plus permet de générer la liste des classes :
print ('\nClasses\n')
print ('\n'.join(sorted ([m[0] + '.' + m[1].__module__ for m in inspect.getmembers(scapy.all, inspect.isclass)])))
Ce qui produit des listes qu’il serait trop long de reproduire ici, mais dont voici une illustration :
Génération de la liste des fonctions et classes de Scapy
De là, il devient possible de travailler sérieusement avec Scapy…
Générer la liste des classes et des fonctions de Scapy