Je suis développeur du logiciel de surveillance réseau Network MACMonitor .
Au cours du développement du programme, une tâche s'est imposée: déterminer quels ordinateurs les utilisateurs utilisent et associer ces informations aux ports des périphériques réseau. Dans cet article, je veux écrire comment j'ai réussi à le faire.

J'ai commencé par des considérations simples: pour associer un utilisateur à un port sur un périphérique réseau, vous devez d'abord connecter l'ordinateur avec lequel l'utilisateur travaille à ce port. Étant donné que le programme Network MACMonitor vous permet de trouver des adresses mac sur les ports des périphériques réseau, il a été décidé de connecter les ordinateurs aux ports à l'aide des adresses mac. Ensuite, vous devez connecter les utilisateurs aux ordinateurs. Ces informations peuvent être obtenues en interrogeant les ordinateurs de n'importe quelle manière.
J'ai vu deux options pour résoudre ce problème:
- Écrivez un agent Windows et interrogez-le à l'aide de Network MACMonitor;
- Utilisez Windows Management Instrumentation (WMI).
La version avec l'agent Windows présente un certain nombre d'inconvénients importants pour moi:
- développement d'un protocole sécurisé pour l'interaction réseau d'un agent Windows avec Network MACMonitor;
- la nécessité de préinstaller l'agent sur les ordinateurs;
- en utilisant un langage de programmation différent (j'écris en Java), car je considère que Java ne convient pas à l'écriture d'un agent: en raison de la consommation assez importante de mémoire virtuelle et de la nécessité d'installer JRE sur tous les ordinateurs.
En raison de tous les inconvénients ci-dessus, j'ai décidé de m'attarder sur l'option utilisant WMI.
Développement client WMI
Étant donné que Network MACMonitor est écrit en Java, j'ai essayé de trouver une bibliothèque Java multiplateforme prête à l'emploi qui implémente les fonctionnalités du client WMI. Et puis j'ai été déçu - il n'y a pas une telle bibliothèque. Toutes les bibliothèques existantes sont soit des wrappers sur les utilitaires Windows, soit (bibliothèque j-Interop) nécessitent une manipulation de registre supplémentaire (changement de propriétaire et d'autorisations sur les branches de registre) pour activer WMI via un registre distant. Puisqu'il n'y avait pas de bibliothèque entièrement fonctionnelle pour Java, j'ai décidé de trouver une bibliothèque ou un client WMI écrit dans n'importe quel autre langage de programmation. Et trouvé un client WMI pour Linux. Après avoir téléchargé et vérifié son travail, j'ai réalisé qu'il était possible d'interroger des ordinateurs Windows sous Linux.
Si cela est possible, j'ai décidé d'écrire ma bibliothèque en Java pur, ce qui me permettrait d'interroger l'ordinateur à l'aide de WMI.
Pour écrire la bibliothèque, une documentation claire sur le fonctionnement du protocole WMI était nécessaire. Il s'est avéré qu'il existe une telle documentation et qu'elle est du domaine public.
J'ai commencé à préparer l'écriture de la bibliothèque en regardant la pile réseau du protocole WMI.
Protocole | Spécifications techniques |
---|
Instrumentation de gestion Windows (WMI) | MS-WMI, MS-WMIO |
Modèle d'objet de composant distribué (DCOM) | MS-DCOM |
Appel de procédure à distance (RPC) | MS-RPCE |
Protocole de contrôle de transmission (TCP) | - |
Protocole Internet (IP) | - |
Pour que WMI fonctionne correctement, tous les niveaux de pile doivent être implémentés.
Étant donné que WMI n'est pas implémenté en Java, je suis passé au protocole suivant dans la pile - DCOM. Et ici, j'ai eu de la chance. Bien que la bibliothèque j-Interop susmentionnée n'implémente pas la fonctionnalité WMI, la fonctionnalité DCOM y est implémentée. Il reste donc à écrire une implémentation du protocole WMI, c'est-à-dire à écrire une implémentation des spécifications MS-WMI et MS-WMIO.
J'ai commencé par implémenter la spécification MS-WMIO, qui est responsable du format d'encodage des données dans les paquets réseau du protocole WMI. De la spécification, j'ai appris que lors du codage des données, la spécification de syntaxe étendue Backus-Naur (ABNF, RFC 5234) est utilisée. La spécification MS-WMIO décrit entièrement le format de codage utilisant ABNF. Il est connu que s'il existe une grammaire décrite dans ABNF, il est possible de créer un analyseur syntaxique pour cette grammaire. Sur Internet, j'ai trouvé un générateur d'analyseur ABNF pour Java et l'ai entré avec une grammaire tirée de la spécification. Étant donné que l'analyseur généré fonctionnait avec des chaînes et que MS-WMIO décrit un format de codage binaire, l'idée était de simplement remplacer l'analyseur généré par des chaînes par des tableaux et des caractères par des octets. Mais après avoir regardé le nombre de fichiers où un remplacement était nécessaire, et ayant également appris de la spécification MS-WMIO qu'il serait parfois nécessaire de travailler avec des bits, j'ai réalisé qu'il serait très difficile de corriger l'analyseur généré, et j'ai décidé d'abandonner cette idée. Je pensais que l'écriture d'un analyseur à partir de zéro serait plus rapide. Et maintenant, l'analyseur était prêt.
Mais comment vérifier que l'analyseur est correctement écrit si la spécification MS-WMI, qui est responsable du fonctionnement du protocole WMI, n'est pas encore implémentée? Puis Wireshark, un analyseur de trafic réseau, m'a aidé. Après avoir effectué des requêtes WMI à l'aide d'outils Windows standard (wbemtest), ayant précédemment désactivé le cryptage, j'ai reçu des paquets réseau et les ai enregistrés dans des fichiers binaires. Il était déjà possible d'utiliser ces fichiers comme données de test pour l'analyseur.
Lorsque l'analyseur a été testé et que les erreurs trouvées ont été corrigées, j'ai procédé à l'implémentation de la spécification MS-WMI, qui décrit le fonctionnement du protocole WMI.
La spécification MS-WMI est divisée en serveur et client. J'ai partiellement implémenté la partie client, dans la mesure nécessaire pour interroger un ordinateur via WMI. Dans cette partie, j'avais également besoin de Wireshark, mais déjà pour analyser la séquence des paquets réseau lors de l'interrogation WMI.
Essayer d'obtenir les données nécessaires à l'aide de WMI
Après avoir écrit la bibliothèque WMI, la tâche de l'utiliser dans le programme Network MACMonitor est devenue. La question s'est posée: quelles données devraient être obtenues à partir des ordinateurs? Je pensais avoir besoin d'obtenir le nom de l'ordinateur, le domaine, le système d'exploitation, l'heure de mise en marche, les adresses mac, les adresses ip, les utilisateurs actifs qui travaillent sur l'ordinateur.
Mais un problème très important s'est posé: comment identifier de manière unique un ordinateur lors de l'interrogation WMI? J'ai considéré les options suivantes:
- adresse mac, modification possible, non unique possible;
- nom et domaine de l'ordinateur (groupe de travail), modification possible, non unique (pour le groupe de travail);
- le numéro de série du disque dur sur lequel le système d'exploitation est installé, les droits d'administrateur sont requis lors de l'interrogation WMI, je n'ai pas vérifié l'unicité, mais je soupçonne qu'une non unique est possible;
- le numéro de série de la carte mère, non unique est possible, et assez souvent;
- l'identifiant du système informatique (propriété UUID WMI de la classe Win32_ComputerSystemProduct ), la non-unicité est possible, et assez souvent;
- Le temps d'installation du système d'exploitation est le meilleur de toutes les options, mais le caractère unique est possible lors du clonage du système ou lors du déploiement à partir d'une image.
Aucune option ne vous permet d'identifier de manière unique l'ordinateur, j'ai donc décidé d'identifier l'ordinateur de trois manières:
- numéro de série de la carte mère,
- identifiant du système informatique
- temps d'installation du système d'exploitation.
Bien sûr, ces trois paramètres peuvent coïncider sur différents ordinateurs, mais moins fréquemment que l'un d'eux.
Une tentative a également été effectuée pour obtenir des utilisateurs actifs à l'aide de la classe WMI standard: Win32_LogonSession . Puis le premier problème est apparu: il s'est avéré que Win32_LogonSession affiche toutes les sessions utilisateur, même celles qui sont déjà terminées. J'ai commencé à penser comment filtrer les sessions actives de celles qui se terminaient. Trouvé que cela peut être fait à l'aide de la classe Win32_SessionProcess , qui associe des instances des classes Win32_LogonSession à Win32_Process . Si le lien vers la session est présent dans la liste des instances de la classe Win32_SessionProcess (il existe au moins un processus avec l'identifiant de cette session), alors il est actif. Ensuite, la question s'est posée de savoir comment associer une session à un utilisateur. Cela peut être fait à l'aide de la classe Win32_LoggedOnUser , qui lie les instances des classes Win32_LogonSession et Win32_UserAccount . Il ne reste plus qu'à obtenir des instances de la classe Win32_UserAccount qui fournissent des informations détaillées sur l'utilisateur.

Mais ici, j'ai été déçu. Lorsque vous utilisez WMI à distance, il s'est avéré qu'en essayant d'obtenir des instances de la classe Win32_UserAccount , il est possible d'obtenir uniquement des utilisateurs d'ordinateurs locaux. Autrement dit, il s'est avéré qu'en utilisant des outils WMI standard, il est impossible de savoir quels utilisateurs sont actifs sur l'ordinateur.
Développement d'un fournisseur WMI.
En raison de l'impossibilité d'identifier sans ambiguïté les ordinateurs et de l'impossibilité d'obtenir des informations sur les utilisateurs actifs à l'aide des classes WMI standard, il a été décidé d'étendre les fonctionnalités de WMI. Vous pouvez le faire en décrivant vos classes WMI dans un fichier MOF et en écrivant un fournisseur WMI pour obtenir des instances de ces classes.
Deux nouvelles classes WMI ont été décrites: NMBY_InstallInfo - pour identifier un ordinateur et NMBY_LogonSession - pour identifier les utilisateurs actifs d'un ordinateur.

Ensuite, un fournisseur WMI a été écrit avec lequel vous pouvez obtenir des instances de ces classes.
Des exigences supplémentaires ont été fixées pour le fournisseur:
- travailler sur un système sans .NET;
- travailler sur le système d'exploitation Windows XP et supérieur;
- la possibilité d'obtenir des informations à l'aide d'un compte non administratif.
Par conséquent, le fournisseur a été écrit en C ++ à l'aide de WinApi.
Lors de la rédaction du fournisseur, des difficultés sont survenues en raison de la petite quantité et de la qualité de la documentation sur ce sujet, mais malgré cela, le fournisseur a été rédigé avec succès.
Un fournisseur écrit est disponible sur la page de téléchargement . Il peut être installé et utilisé gratuitement.
Résumé
En conséquence, en utilisant le programme Network MACMonitor, il est devenu possible:
- Associer des utilisateurs à des ordinateurs

- Associer des ordinateurs à des ports sur des périphériques réseau

- Associer des ports de périphérique réseau aux ordinateurs et aux utilisateurs

- Afficher l'historique d'enregistrement des utilisateurs sur les ordinateurs.

Site Web du programme