P/Invoke und DllImport dekompilieren: die native Interop einer .NET-Assembly lesen
P/Invoke-Deklarationen kompilieren zu pinvokeimpl-Methoden mit einer ImplMap. Hier steht, wie ein Dekompilierer [DllImport], MarshalAs und Aufrufkonventionen rekonstruiert — und wie man genau liest, welche nativen Funktionen eine unbekannte Assembly aufruft.
Wenn Sie verstehen müssen, was eine unbekannte .NET-Assembly tatsächlich tut — nicht ihren eigenen Daten, sondern der Maschine — liegt die Antwort in der nativen Interop. Verwalteter Code, der im CLR bleibt, wird von ihm eingehegt; sobald er über P/Invoke nativen Code aufruft, kann er alles tun, was der Prozess kann. Glücklicherweise gehören P/Invoke-Deklarationen zu den am treuesten wiederherstellbaren Dingen einer dekompilierten Assembly, weil die Information, die ein Dekompilierer braucht, in einer eigenen Metadatentabelle sitzt. Sie gut zu lesen verwandelt „diese Assembly ruft irgendetwas Natives auf“ in ein präzises Inventar.
Wie P/Invoke gespeichert wird
Eine [DllImport]-Methode trägt keinen Methodenrumpf — sie ist eine extern-Deklaration. In der Assembly wird sie zu einer als pinvokeimpl markierten Methode mit einem Eintrag in der ImplMap-Metadatentabelle, der vier Dinge festhält: das Modul (die native DLL), den Einstiegspunktnamen, die Aufrufkonvention und den Zeichensatz / die Namensmangling-Regeln. Die verwaltete Signatur (Parameter- und Rückgabetypen plus etwaige [MarshalAs]) lebt in der normalen Methodensignatur und den Parameter-Metadaten.
Ein Dekompilierer wie Glass liest die ImplMap und die Signatur und rekonstruiert die ursprüngliche Deklaration — samt Attribut:
[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);
Alles in dieser Rekonstruktion — der DLL-Name, CharSet, SetLastError, CallingConvention und das [MarshalAs] an der Zeichenkette — stammt aus Metadaten, nicht aus Vermutungen.
Ein Inventar nativer Aufrufe erstellen
Da jedes P/Invoke ein ImplMap-Eintrag ist, können Sie die gesamte native Oberfläche einer Assembly aufzählen. Öffnen Sie in Glass die Assembly und sehen Sie sich ihre P/Invoke-Methoden an: Jede nennt eine DLL und eine Funktion. Dieses Inventar beantwortet die Fragen, die zählen, wenn Sie etwas prüfen, das Sie nicht geschrieben haben:
- Welche nativen Bibliotheken berührt sie?
kernel32/user32ist Routine; eine mitgelieferte unbekannte.dll,ws2_32(Sockets) oder Krypto-/Registry-Aufrufe vonadvapi32verdienen einen genaueren Blick. - Was kann sie dem System antun? Datei-, Prozess-, Registry- und Netzwerkfunktionen sind alle namentlich sichtbar.
- Wie überqueren Daten die Grenze? Die Metadaten
[MarshalAs],[StructLayout]und[FieldOffset]rekonstruieren den ABI-Vertrag, sodass Sie sehen, wie eine verwaltete Struct auf das native Layout abgebildet wird, das eine Funktion erwartet.
Das ist derselbe Ablauf wie eine verdächtige Assembly analysieren, mit der nativen Interop als Linse.
Die moderne Wendung: LibraryImport
.NET 7 führte [LibraryImport] ein, eine quellgenerierte Alternative zu [DllImport]. Statt dass die Laufzeit Argumente marshallt, gibt ein Quellgenerator das Marshalling als C# zur Compile-Zeit aus: eine von Ihnen deklarierte partial-Methode plus einen generierten Stub, der die Argumente marshallt und ein privates natives Ziel im DllImport-Stil aufruft. Dekompiliert erscheint ein [LibraryImport] daher als mehr Code als ein klassisches P/Invoke — der generierte Stub ist direkt da —, was für das Lesen sogar ein Vorteil ist: Sie sehen genau, wie jedes Argument konvertiert wird, statt der Laufzeit zu vertrauen. Es liest sich wie jeder quellgenerierte Code, einmal abgesenkt.
Mit Glass umsetzen
Glass rekonstruiert [DllImport]-Deklarationen aus der ImplMap — DLL, Einstiegspunkt, Aufrufkonvention, Charset und [MarshalAs] — und zeigt die generierten Stubs hinter [LibraryImport], sodass Sie die gesamte native Interop-Oberfläche einer Assembly und das genaue Marshalling jedes Aufrufs lesen können. Es ist der schnellste Weg, „was tut diese Assembly der Maschine an?“ zu beantworten, und es ist kostenlos.
Richten Sie Glass auf eine Assembly und listen Sie ihre P/Invoke-Methoden, um ihre native Reichweite auf einen Blick zu sehen.
Nebula.NET testen
Härten Sie Ihren .NET-Code in wenigen Minuten — starten Sie mit der kostenlosen Edition.