Décompiler P/Invoke et DllImport : lire l'interopérabilité native d'une assembly .NET
Les déclarations P/Invoke se compilent en méthodes pinvokeimpl avec un ImplMap. Voici comment un décompilateur reconstruit [DllImport], MarshalAs et les conventions d'appel — et comment lire exactement quelles fonctions natives une assembly inconnue appelle.
Quand vous devez comprendre ce qu’une assembly .NET inconnue fait réellement — non à ses propres données, mais à la machine — l’interopérabilité native est là où se trouve la réponse. Le code managé qui reste dans le CLR est mis en bac à sable par lui ; dès qu’il appelle du code natif via P/Invoke, il peut faire tout ce que le processus peut faire. Heureusement, les déclarations P/Invoke comptent parmi les choses les plus fidèlement récupérables d’une assembly décompilée, car l’information dont un décompilateur a besoin siège dans une table de métadonnées dédiée. Bien la lire transforme « cette assembly appelle du natif » en un inventaire précis.
Comment P/Invoke est stocké
Une méthode [DllImport] ne porte pas de corps de méthode — c’est une déclaration extern. Dans l’assembly elle devient une méthode marquée pinvokeimpl, avec une entrée dans la table de métadonnées ImplMap qui enregistre quatre choses : le module (la DLL native), le nom du point d’entrée, la convention d’appel et le jeu de caractères / règles de mangling des noms. La signature managée (types des paramètres et de retour, plus tout [MarshalAs]) vit dans la signature normale de la méthode et les métadonnées des paramètres.
Un décompilateur comme Glass lit l’ImplMap et la signature et reconstruit la déclaration d’origine — attribut compris :
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true,
CallingConvention = CallingConvention.Winapi)]
static extern IntPtr CreateFileW(
[MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes,
uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
Tout dans cette reconstruction — le nom de la DLL, CharSet, SetLastError, CallingConvention et le [MarshalAs] sur la chaîne — provient des métadonnées, pas de suppositions.
Constituer un inventaire des appels natifs
Comme chaque P/Invoke est une entrée ImplMap, vous pouvez énumérer toute la surface native d’une assembly. Dans Glass, ouvrez l’assembly et regardez ses méthodes P/Invoke : chacune nomme une DLL et une fonction. Cet inventaire répond aux questions qui comptent quand vous auditez quelque chose que vous n’avez pas écrit :
- Quelles bibliothèques natives touche-t-elle ?
kernel32/user32est de routine ; une.dllinconnue embarquée,ws2_32(sockets) ou des appels crypto/registre d’advapi32méritent un examen plus attentif. - Que peut-elle faire au système ? Les fonctions de fichier, de processus, de registre et de réseau sont toutes visibles par nom.
- Comment les données franchissent-elles la frontière ? Les métadonnées
[MarshalAs],[StructLayout]et[FieldOffset]reconstruisent le contrat ABI, donc vous voyez comment une struct managée se mappe sur la disposition native qu’attend une fonction.
C’est le même flux qu’analyser une assembly suspecte, avec l’interopérabilité native comme loupe.
La touche moderne : LibraryImport
.NET 7 a introduit [LibraryImport], une alternative à [DllImport] générée par source. Au lieu que le runtime marshale les arguments, un générateur de source émet le marshalling en C# à la compilation : une méthode partial que vous avez déclarée, plus un stub généré qui marshale les arguments et appelle une cible native privée de style DllImport. Décompilé, un [LibraryImport] apparaît donc comme plus de code qu’un P/Invoke classique — le stub généré est là — ce qui est en fait un atout pour la lecture : vous voyez exactement comment chaque argument est converti, au lieu de faire confiance au runtime. Cela se lit comme n’importe quel code généré par source une fois abaissé.
Le faire avec Glass
Glass reconstruit les déclarations [DllImport] à partir de l’ImplMap — DLL, point d’entrée, convention d’appel, charset et [MarshalAs] — et montre les stubs générés derrière [LibraryImport], pour que vous puissiez lire toute la surface d’interopérabilité native d’une assembly et le marshalling exact de chaque appel. C’est le moyen le plus rapide de répondre à « que fait cette assembly à la machine ? » et c’est gratuit.
Pointez Glass sur une assembly et listez ses méthodes P/Invoke pour voir sa portée native d’un coup d’œil.
Essayez Nebula.NET
Renforcez votre code .NET en quelques minutes — commencez avec l'édition gratuite.