Enumération de shares SMB

Ecrit par @mickyballadelli

Avec l’utilisation en production de serveurs NAS il n’est souvent plus possible d’utiliser les commandes WMI pour récupérer la liste des Shares.

Ce script se base sur le protocole Remote Administration Protocol (MS-RAP) qui lui-même se base sur SMB pour exécuter la commande NetShareEnum et lister les Shares sur un serveur donné.

Les paramètres du script sont, le nom du serveur à interroger (avec localhost par défaut), le niveau de détail en retour souhaité

Pour la définition du niveau de détail Microsoft s’appuie sur des structures qu’on va devoir déclarer dans notre script afin de nous accorder avec la taille des buffers que la commande NetShareEnum va nous renvoyer. Nous allons nous appuyer sur les déclarations style C# afin de créer notre namespace et pouvoir appeler la fonction NetShareEnum ainsi qu’avoir le retour souhaité avec les déclarations des structures de différents niveaux.

Pour cela nous allons utiliser Add-type qui nous permet de créer un namespace .NET au sein de notre script.

Le bloc de texte est délimité par @ » et « @ car il comporte plusieurs lignes. Ce bloc de texte est en fait du C# qui sera compilé on the fly lors de l’exécution du script.

On commence par déclarer les librairies .NET dont nous allons avoir besoin :

System est la librairie de base qui contient la gestion des exceptions ainsi que la définition des types de données fondamentales pour .NET.

System.Runtime.InteropServices contient tous les services d’interopérabilité entre objets COM et autres services et s’assure de la gestion des variables managées (Managed) versus les variables non-managées (Unmanaged). La différence étant qu’une variable exécutée dans le runtime du framework .NET est managée et une variable qui est utilisée en dehors de ce runtime sera non-managée. Managée veut simplement dire gérée par .NET (dans son CLR), c’est-à-dire que chaque variable est checkée et la gestion de la mémoire comme le garbage collection sont prise en charge par le framework. Unmanaged code veut dire que vous êtes en pleine descente et personne ne vous a fourni de freins, à vous d’y avoir pensé.

Nous définissons notre classe:

Et nous déclarons que nos structures sont séquentielles. Ceci indique au compilateur que la taille de notre structure est telle que nous l’avons définie, et le compilateur n’ajoutera pas d’octets pour aligner la mémoire (padding), ce qui grossirait la structure en mémoire et nous ne saurions plus forcément la référencer correctement.

Nous déclarons aussi que nos caractères sont stockés sur deux bytes au lieu d’un pour le format ANSI.

Finalement nous déclarons nos structures :

La structure plus simple déclare un nom, qui sera le nom du Share, et on signale au compilateur qu’il sera un Long Pointeur vers une large (Wide) chaîne de caractères LPWStr.

Nous allons faire de même avec les structures des autres niveaux de détail.

Ensuite nous allons déclarer la fonction NetShareEnum et l’importer à partir d’une DLL (Netapi32.dll) où elle se trouve.

NetShareEnum alloue de la mémoire (qui sera référencée par le pointeur bufptr) et il incombera de la libérer après utilisation. Donc on déclare la fonction à cet effet, NetApiBufferFree:

Voilà, notre namespace est déclaré et nous pouvons y accéder à travers Powershell via la commande [netapi]::NetShareEnum.

Nous déclarons ensuite un objet $struct qui sera créé en fonction du choix du niveau de détail souhaité :

Nous allons maintenant faire un peu d’arithmétique de pointeurs. Ce qui est chose courante dans des langages comme le C, et l’est beaucoup moins avec des langages de scripting comme Powershell (en tout cas c’est mon expérience).

Généralement , une variable contient une valeur. Un pointeur est une variable qui contient l’adresse en mémoire où se trouve la valeur. Pour être plus précis, une variable classique est définie sur le stack (allocation statique effectuée avant l’exécution du programme) et sa mémoire est libérée lorsque nous quittons son block de code ou fonction. Si nous voulons qu’une variable survive au-delà du bloc de code dans laquelle elle est déclarée, nous devons l’allouer sur le Heap (pile persistante créée de façon dynamique lors de l’exécution du programme).

L’arithmétique de pointeur est une façon de repositionner le pointeur sur la bonne adresse de mémoire.

NetShareEnum alloue suffisamment de mémoire pour tous les shares à retourner, chaque share équivaut à une structure en mémoire. Nous devons donc trouver l’endroit en mémoire ou se trouve le premier élément, on récupère la structure, ensuite on bougera le pointeur en fonction de la taille de la structure, pour trouver l’élément suivant.
Dans le cas le plus simple, la structure contient un pointeur vers une chaine de caractères, donc l’élément suivant se trouvera après la taille (le sizeof) de la première structure (8 bytes). Finalement c’est une simple addition.

Voyons tout ça étape par étape.

On appelle la fonction NetShareEnum en passant le paramètre $buffer par référence, ceci fait en sorte que le contenu de notre variable est mis à jour par la fonction avec l’adresse de la mémoire allouée. Sans ce [ref], une nouvelle variable serait créée sur le stack et passée à la fonction sans que nous puissions récupérer l’adresse du buffer.

Une fois la fonction exécutée, on récupère l’adresse du buffer en mémoire, la variable $buffer contient cette information qu’on transforme en Int64 :

On crée un objet $ptr qui pointe vers cette mémoire, voici comment le créer :

Le constructeur d’un objet IntPtr prend comme argument une adresse, nous avons notre pointeur.

Nous allons récupérer le contenu de la structure à partir du pointeur, Powershell créera un objet qu’on ajoutera à un array:

Nous allons maintenant calculer la taille de la structure, ceci permettra de repositionner le pointeur correctement une fois utilisé :

Nous calculons le nouveau offset, en ajoutant à l’adresse la taille de la structure, nous utiliserons cet offset pour créer un nouveau pointeur qui sera positionné sur l’élément suivant.

Et on boucle pour tous les éléments retournés.

Finalement on formate en tableau, à titre d’exemple, l’array $shares.

Voici le script complet:

Partager ce contenu