Comprendre du code .NET tiers inconnu avec Glass.NET
Vous dépendez d'une DLL dont vous n'avez pas le code source — un paquet abandonné, une bibliothèque interne dont l'auteur est parti, un SDK d'éditeur à la documentation maigre — et devez savoir comment elle fonctionne vraiment. Voici un flux d'investigation concret dans Glass.NET, le décompilateur .NET gratuit et purement statique qui n'exécute jamais l'assembly qu'il inspecte.
Tôt ou tard, tout projet .NET finit par s’appuyer sur une DLL que personne ne sait expliquer. Le paquet a été abandonné il y a trois ans et son dépôt a disparu. La bibliothèque utilitaire interne a été écrite par quelqu’un qui a quitté l’entreprise depuis longtemps. Le SDK de l’éditeur est livré avec une documentation qui s’arrête exactement là où commence votre question. Le comportement est faux, ou surprenant, ou tout simplement non documenté — et la seule chose qui trancherait, le code source, est la seule chose que vous n’avez pas.
Vous n’en avez pas besoin. Un assembly .NET compilé porte une description quasi complète et récupérable de ce qu’il fait : de l’IL plus des métadonnées riches nommant chaque type, méthode et champ. Glass.NET lit cela et reconstruit du C# lisible, transformant « je n’ai aucune idée de ce que fait cette dépendance » en « laissez-moi lire la méthode et le découvrir ». Il est gratuit, et strictement statique — il lit les métadonnées et l’IL et reconstruit le C#, et n’exécute jamais l’assembly qu’il inspecte. Ce dernier point compte le plus ici : vous pouvez pointer Glass vers une dépendance à laquelle vous ne faites pas confiance, car rien dans la DLL ne s’exécute jamais.
Rendons cela concret. Supposons que vous dépendiez d’un paquet NuGet abandonné — appelons-le Acme.Resilience — qui enveloppe vos appels HTTP sortants dans une politique de réessai. De façon intermittente, un appel qui aurait dû échouer vite se bloque près d’une minute ; un autre qui aurait dû réessayer abandonne aussitôt. La documentation est un paragraphe dans un README mort. Vous devez savoir ce que fait réellement RetryPolicy.ExecuteAsync : comment il calcule son backoff, quelles exceptions il considère comme réessayables, et s’il parle parfois au réseau de lui-même. Voici comment le découvrir.
Mettez l’assembly sur disque et ouvrez-le
D’abord, trouvez la DLL. Pour une dépendance NuGet, elle se trouve dans le cache global des paquets — %USERPROFILE%\.nuget\packages\<id>\<version>\lib\<tfm>\ sous Windows, ~/.nuget/packages/... ailleurs. Pour une bibliothèque interne ou une livraison d’éditeur, elle est là où votre build l’a copiée, généralement dans bin/.
Ouvrez-la dans Glass.NET : glissez le fichier sur la fenêtre, ou Fichier ▸ Ouvrir un assembly. Glass charge les métadonnées et affiche l’assembly dans un arbre. Vous n’avez besoin ni d’un PDB correspondant ni du code source d’origine — une simple .dll managée suffit, et Glass reconstruit le C# sans symboles (il y a plus là-dessus dans décompiler sans symboles PDB). Et comme ouvrir, lire et exporter sont toutes des opérations statiques, cette première étape est déjà sûre : un paquet que vous hésiteriez à exécuter s’ouvre sans souci, car Glass ne l’exécute jamais.
Orientez-vous avant de lire
Résistez à l’envie de plonger directement dans une méthode. Passez trente secondes à saisir d’abord la forme de la chose. Dépliez l’assembly dans son arbre espaces de noms ▸ types ▸ membres — il reflète la structure enregistrée par le compilateur, vous voyez donc immédiatement si vous avez affaire à trois types bien rangés ou à un fouillis d’une centaine. Pour Acme.Resilience, vous vous attendriez à une petite surface publique — RetryPolicy, peut-être un RetryOptions, un type d’exception ou deux — au-dessus d’une poignée d’utilitaires internes.
La vue d’information sur l’assembly est l’autre outil d’orientation, et elle répond à une question à régler tôt : qu’est-ce que cette chose peut même faire ? Elle montre le framework cible, les attributs de produit et — la partie utile — les assemblys référencés. Si un « utilitaire de réessai » référence System.Net.Http, c’est attendu ; s’il référence System.Diagnostics.Process ou quelque chose qui n’a rien à faire dans une bibliothèque de backoff, vous avez appris quelque chose avant de lire une seule ligne. Le rapport d’exposition de Glass est un bon complément ici : passez-le pour jauger à quel point l’assembly est lisible. Une bibliothèque de style open source ordinaire devrait se lire clairement ; si elle revient fortement obfusquée, c’est en soi un signal qui mérite qu’on s’interroge. (C’est la vue depuis l’autre côté de l’obfuscateur frère Nebula.NET, dont la virtualisation de code est une fonction Pro — mais une dépendance ordinaire n’a aucune raison de ressembler à cela.)
Décompilez le point d’entrée
Allez maintenant au code que vous appelez réellement. La voie la plus rapide est Atteindre tout (Ctrl+T), l’ouverture rapide floue sur chaque type et membre : tapez ExecuteAsync et sautez droit dessus, sans parcourir l’arbre. Cliquez sur le membre et Glass le décompile en C# à la demande.
Lisez ExecuteAsync comme le point d’entrée et lisez-le de façon ciblée — vous n’essayez pas de comprendre toute la bibliothèque, seulement le chemin que votre appel y emprunte. Vous verrez la boucle de réessai, l’appel à ce qui calcule le délai, le prédicat qui décide si une exception est réessayable, et où le travail réel est invoqué. Cela seul répond souvent à la question. Mais la logique intéressante vit généralement un niveau plus bas, dans les utilitaires que cette méthode appelle — alors suivez-les.
Suivez les appels pour comprendre le flux de contrôle
C’est là qu’un décompilateur centré sur la navigation gagne sa place, car comprendre du code inconnu consiste surtout à suivre des appels, pas à lire une méthode de haut en bas.
- Atteindre la définition (F12, ou Ctrl+Clic) sur l’appel à l’utilitaire de délai vous y mène droit. Vous pouvez maintenant lire exactement comment le backoff est calculé — et si ce blocage que vous avez vu est un délai exponentiel sans plafond, de sorte qu’un réessai tardif attend 60 secondes par conception.
- Rechercher toutes les références (Shift+F12) sur le champ
RetryOptions.MaxAttempts, s’écoulant dans le panneau de résultats, montre partout où la limite est lue — vous confirmez ainsi si le cas « abandonne aussitôt » est une valeur par défaut de1respectée à la lettre à un endroit inattendu. - Hiérarchie des appels et des types vous laissent parcourir la structure de haut en bas — qui appelle la méthode où vous êtes, de quoi un type donné dérive ou ce qu’il implémente — bien plus vite que de deviner d’après les noms.
Les références sont résolues depuis l’arbre de syntaxe propre au décompilateur, donc F12 atterrit sur le bon symbole et non sur une correspondance de texte. L’historique de navigation et un fil d’Ariane espace de noms ▸ type ▸ membre vous gardent orienté à mesure que vous bougez, de sorte que vous pouvez revenir sur vos pas.
Descendez à l’IL là où le C# est ambigu
Une méthode async comme ExecuteAsync est reconstruite à partir d’une machine à états générée par le compilateur. Le décompilateur reconnaît ce désucrage et vous rend du C# async/await propre la plupart du temps — mais « la plupart du temps » est le piège. Quand le C# reconstruit se lit bizarrement, ou que vous ne distinguez pas exactement où une exception est capturée par rapport à l’endroit où le délai attendu se place dans la machine à états, basculez le même membre sur la vue IL. L’IL est ce qui s’exécute réellement, c’est donc la vérité de terrain ; le lire est l’arbitre quand le C# est ambigu. Pas besoin de vivre dans l’IL — recourez-y sur la poignée de méthodes où la vue de haut niveau arrondit un détail qui compte. (Lire l’IL détaille ce que vous regardez, et comment décompiler une DLL .NET en C# couvre le flux de travail plus large.)
Exportez-le vers un projet pour un vrai examen
Quand vous voulez faire plus que lire — grepper tout l’assembly pour chaque HttpClient, Task.Delay ou Process.Start, lui pointer un analyseur, annoter ce que vous avez compris, ou garder la bibliothèque sur disque comme référence permanente — exportez-la vers un projet compilable. Dans l’interface, c’est Fichier ▸ Exporter vers un projet ; depuis un script, c’est le sous-commande glass export :
glass export Acme.Resilience.dll --output ./Acme.Resilience.Source
Cela écrit un .csproj plus les fichiers .cs reconstruits et les ressources extraites — un projet que votre chaîne d’outils comprend, pas un dépotoir. Une petite bibliothèque comme celle-ci recompile généralement sans plus ; une grande ou inhabituelle peut nécessiter une courte passe manuelle pour des références non résolues ou du code généré par le compilateur exotique. Dans tous les cas, vous avez désormais le code source sur disque pour l’explorer dans votre propre IDE, exactement comme si l’auteur l’avait livré — et, là encore, l’export n’a jamais exécuté le binaire pour le produire. L’analyse approfondie de cela est dans transformer une DLL en un projet compilable.
L’essentiel
Une dépendance inconnue n’est pas une boîte noire. Ouvrez-la, orientez-vous avec l’arbre des types et l’information sur l’assembly, décompilez le point d’entrée, suivez les appels pour voir le vrai flux de contrôle, descendez à l’IL là où le C# est ambigu, et exportez vers un projet quand vous le voulez sur disque. En une après-midi, vous pouvez savoir précisément ce que Acme.Resilience fait de vos appels HTTP — le backoff sans plafond, l’abandon trop empressé, quoi que cela s’avère être — au lieu de deviner d’après le comportement. Et comme Glass.NET est gratuit et strictement statique, vous avez tout fait sans exécuter une seule ligne de code en laquelle vous n’aviez pas confiance. Téléchargez Glass.NET, ouvrez la DLL qui vous intrigue et lisez-la vous-même.
Essayez Nebula.NET
Renforcez votre code .NET en quelques minutes — commencez avec l'édition gratuite.