Skip to content
← Todas las publicaciones
· Delta1 Labs Descompilación.NETGuía

Descompilar P/Invoke y DllImport: leer la interoperabilidad nativa de un ensamblado .NET

Las declaraciones P/Invoke se compilan a métodos pinvokeimpl con un ImplMap. Así reconstruye un descompilador [DllImport], MarshalAs y las convenciones de llamada — y así lees exactamente qué funciones nativas invoca un ensamblado desconocido.

Cuando necesitas entender qué hace realmente un ensamblado .NET desconocido — no a sus propios datos, sino a la máquina — la interoperabilidad nativa es donde está la respuesta. El código gestionado que se queda dentro del CLR está aislado por él; en cuanto llama a código nativo vía P/Invoke, puede hacer cualquier cosa que pueda el proceso. Por fortuna, las declaraciones P/Invoke están entre las cosas más fielmente recuperables de un ensamblado descompilado, porque la información que necesita un descompilador está en una tabla de metadatos dedicada. Leerla bien convierte «este ensamblado llama a cosas nativas» en un inventario preciso.

Cómo se guarda P/Invoke

Un método [DllImport] no lleva cuerpo de método — es una declaración extern. En el ensamblado se convierte en un método marcado pinvokeimpl, con una entrada en la tabla de metadatos ImplMap que registra cuatro cosas: el módulo (la DLL nativa), el nombre del punto de entrada, la convención de llamada y el juego de caracteres / reglas de mangling de nombres. La firma gestionada (tipos de parámetros y de retorno, más cualquier [MarshalAs]) vive en la firma normal del método y en los metadatos de parámetros.

Un descompilador como Glass lee el ImplMap y la firma y reconstruye la declaración original — atributo incluido:

[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);

Todo en esa reconstrucción — el nombre de la DLL, CharSet, SetLastError, CallingConvention y el [MarshalAs] en la cadena — vino de metadatos, no de conjeturas.

DllImport is reconstructed from the ImplMap metadata método pinvokeimpl+ entrada ImplMap ImplMap: DLL, entrada,conv. llamada, charset+ [MarshalAs] en params Glass → C# [DllImport]declaración nativa completa La tabla ImplMap es un índice completo de cada función nativa que el ensamblado puede llamar.

Construir un inventario de llamadas nativas

Como cada P/Invoke es una entrada ImplMap, puedes enumerar toda la superficie nativa de un ensamblado. En Glass, abre el ensamblado y mira sus métodos P/Invoke: cada uno nombra una DLL y una función. Ese inventario responde a las preguntas que importan cuando auditas algo que no escribiste:

  • ¿Qué bibliotecas nativas toca? kernel32/user32 es rutina; una .dll desconocida empaquetada, ws2_32 (sockets) o llamadas de cripto/registro de advapi32 merecen un vistazo más atento.
  • ¿Qué puede hacerle al sistema? Las funciones de archivo, proceso, registro y red son todas visibles por nombre.
  • ¿Cómo cruzan los datos la frontera? Los metadatos [MarshalAs], [StructLayout] y [FieldOffset] reconstruyen el contrato ABI, así que puedes ver cómo una struct gestionada se mapea al diseño nativo que espera una función.

Este es el mismo flujo que analizar un ensamblado sospechoso, con la interoperabilidad nativa como lente.

El giro moderno: LibraryImport

.NET 7 introdujo [LibraryImport], una alternativa a [DllImport] generada por fuente. En lugar de que el runtime marshalice los argumentos, un generador de fuente emite el marshalling como C# en tiempo de compilación: un método partial que declaraste, más un stub generado que marshaliza los argumentos y llama a un destino nativo privado al estilo DllImport. Descompilado, un [LibraryImport] aparece por tanto como más código que un P/Invoke clásico — el stub generado está ahí mismo — lo que de hecho es una ventaja para leer: ves exactamente cómo se convierte cada argumento, en lugar de confiar en que el runtime lo haga. Se lee muy parecido a cualquier código generado por fuente una vez reducido.

Cómo hacerlo con Glass

Glass reconstruye las declaraciones [DllImport] desde el ImplMap — DLL, punto de entrada, convención de llamada, charset y [MarshalAs] — y muestra los stubs generados detrás de [LibraryImport], para que puedas leer toda la superficie de interoperabilidad nativa de un ensamblado y el marshalling exacto de cada llamada. Es la forma más rápida de responder «¿qué le hace este ensamblado a la máquina?» y es gratuito.

Apunta Glass a un ensamblado y lista sus métodos P/Invoke para ver su alcance nativo de un vistazo.

Prueba Nebula.NET

Endurece tu código .NET en minutos — empieza con la edición gratuita.