Skip to content
← Todas las publicaciones
· Delta1 Labs Glass.NETDecompiladorGuía.NET

Decompila una app .NET de archivo único o autocontenida con Glass.NET

Publicaste una app .NET, arrastras el .exe a un decompilador y no sale nada legible. El problema no es el decompilador — es que dotnet publish tiene varios formatos y la mayoría no te entrega un ensamblado gestionado donde lo esperas. Aquí tienes dónde vive realmente tu IL en una compilación autocontenida, de archivo único, ReadyToRun o Native AOT, y cómo abrirlo en Glass.NET.

Publicaste una app .NET. Ahora quieres leer qué se entregó de verdad — quizá es tu propia versión y el código fuente desapareció hace tiempo, quizá es una herramienta cuyo comportamiento estás investigando. Así que arrastras el .exe a Glass.NET y… no sale nada útil. Ni espacios de nombres, ni métodos, ni C# — solo un stub, o un rechazo liso y llano. Es tentador concluir que el decompilador está roto. No lo está. El problema es que dotnet publish tiene varios formatos de salida, y la mayoría no colocan un ensamblado gestionado decompilable en la ruta que buscas por instinto.

Una vez que sabes qué formato tienes entre manos y dónde vive realmente su IL gestionado, la decompilación en sí es la parte fácil. Este artículo recorre los formatos — autocontenido, archivo único, ReadyToRun y Native AOT — dice exactamente dónde está el IL legible en cada uno (o, con honestidad, cuándo no hay ninguno), y luego ejecuta el flujo normal de Glass sobre él. Glass.NET es gratuito y estrictamente estático: lee los metadatos y el IL del ensamblado gestionado y reconstruye C#, y nunca ejecuta la app que inspecciona. Eso importa doblemente aquí, porque una app publicada es justamente lo que de otro modo habrías tenido que ejecutar para averiguar qué hace.

Lo que confunde a todos: el apphost no es tu código

El dotnet publish moderno envuelve tu programa en un lanzador nativo llamado apphost — el MyApp.exe que ejecutas con doble clic. El apphost es un pequeño arrancador nativo, y su único trabajo es poner en marcha el runtime de .NET y apuntarlo hacia tu ensamblado gestionado. Es código máquina nativo; no contiene nada de tu C#. Un decompilador lee ensamblados gestionados — IL más metadatos — así que apuntarlo al apphost .exe lo apunta al archivo equivocado por completo.

Tu código está en una .dll gestionada. Que esa DLL esté junto al apphost o escondida dentro de él es la única variable real, y la decide el formato de publicación. Así que toda la tarea se reduce a una pregunta: ¿dónde está la DLL gestionada? Aquí tienes la respuesta, formato por formato.

Autocontenido: abre la DLL gestionada, no el .exe

Una publicación autocontenida entrega tu app junto con su propia copia privada del runtime de .NET, para que corra en una máquina sin .NET instalado. Abre la carpeta de publicación y verás una multitud de archivos: el apphost nativo MyApp.exe, docenas de ensamblados del runtime (System.*.dll, Microsoft.*.dll), algunas bibliotecas nativas (*.dll, *.so, *.dylib) — y, ahí mismo en disco, el que quieres: tu propio ensamblado gestionado, MyApp.dll.

Esa MyApp.dll es exactamente lo que emitió el compilador de C# — IL corriente y metadatos completos. Abre esa en Glass (Archivo ▸ Abrir ensamblado, o arrástrala a la ventana), no el .exe. El apphost que tiene al lado es una pista falsa; ignóralo. Autocontenido no es archivo único salvo que también lo pidieras, así que por defecto no se empaqueta nada y nada necesita extraerse — la DLL gestionada ya está en disco, lista para leer. Si no sabes cuál de las muchas DLL es la tuya, ordénalas por nombre y busca el nombre de tu ensamblado, o abre una candidata y comprueba en la vista de información del ensamblado tus atributos de producto en vez de una identidad System.*.

Archivo único: extrae primero el bundle, luego abre la DLL

Una publicación de archivo único (PublishSingleFile) da un paso más. Toma esos ensamblados gestionados y los incrusta dentro del apphost .exe como recursos, para que distribuyas un archivo en lugar de una carpeta. El IL gestionado sigue ahí dentro, byte a byte sin cambios — pero está empaquetado dentro del host nativo, así que no hay ninguna MyApp.dll en disco que Glass pueda abrir. Glass lee ensamblados gestionados; no abre por ti un bundle nativo. Así que primero extraes el bundle con el mecanismo estándar de .NET, y luego abres la DLL que sale.

El extractor viene incorporado en el runtime. Apunta DOTNET_BUNDLE_EXTRACT_BASE_DIR a un directorio y ejecuta la app una vez; el host desempaqueta ahí sus ensamblados incrustados antes de lanzarse:

# app de archivo único: desempaqueta a disco los ensamblados incrustados
DOTNET_BUNDLE_EXTRACT_BASE_DIR=./extracted ./MyApp
# ./extracted/<appname>/<hash>/MyApp.dll  — IL gestionado corriente, listo para Glass

Una advertencia honesta: este método ejecuta la app para que se autoextraiga. Para tu propia versión no hay problema. Para un binario en el que no confías, no lo ejecutes — recurre en su lugar a una herramienta extractora de bundles independiente que lea el diseño documentado del bundle y escriba las DLL incrustadas en disco sin lanzar el ejecutable. En cualquier caso acabas con MyApp.dll en disco, y a partir de ahí Glass sigue siendo plenamente estático: solo abre la DLL ya extraída. Suelta esa MyApp.dll en Glass y estarás de vuelta en una decompilación corriente.

ReadyToRun: sigue habiendo IL debajo, se decompila sin problema

ReadyToRun (PublishReadyToRun) es fácil de temer sin motivo. R2R precompila una imagen nativa de tus métodos de forma anticipada para recortar el coste de arranque — pero conserva el IL original junto a esa imagen nativa como respaldo. El resultado sigue siendo un ensamblado gestionado perfectamente corriente, con metadatos e IL completos. Glass lee un ensamblado R2R igual que cualquier otra DLL; el código nativo precompilado simplemente se ignora, y decompilas el IL como siempre. R2R cambia la rapidez con que arranca tu app, no si se puede leer.

Native AOT: no hay nada que decompilar — para nadie

Native AOT (PublishAot) es la excepción honesta, y vale la pena ser rotundo. AOT compila tu IL hasta código máquina nativo de forma anticipada y descarta el IL. El artefacto publicado es un binario nativo de verdad: sin ensamblado gestionado, sin metadatos, sin IL. No hay nada que Glass — ni ILSpy, ni dotPeek, ni ningún decompilador de .NET — pueda reconstruir, porque lo que un decompilador lee se ha compilado y desaparecido. Esto no es una limitación de Glass; es la naturaleza del formato, y ningún decompilador puede devolver el código nativo al C# que lo produjo. Si tienes que analizar un binario de NativeAOT estás en terreno de ingeniería inversa nativa — un desensamblador nativo — no de decompilación de .NET. (Una salida práctica: muchas apps reales son solo parcialmente AOT, y cualquier ensamblado gestionado que quedó fuera de la imagen AOT vuelve a ser una DLL normal y decompilable. Las contrapartidas de protección detrás de todo esto se tratan en obfuscar apps Native AOT y de archivo único.)

Luego es el flujo normal de Glass

Una vez que tienes la DLL gestionada en disco — directa de una carpeta autocontenida, o extraída de un bundle de archivo único — todo lo que sigue es el flujo corriente de Glass, el mismo de cómo decompilar una DLL de .NET a C#:

  • Ábrela y oriéntate. Glass carga los metadatos y despliega el ensamblado como un árbol de espacios de nombres ▸ tipos ▸ miembros. Echa un vistazo primero a la vista de información del ensamblado — framework objetivo y ensamblados referenciados — para confirmar que cogiste tu ensamblado y no uno del runtime que viajó de polizón en la carpeta de publicación.
  • Decompila a C#. Haz clic en cualquier tipo o miembro y Glass reconstruye C# legible bajo demanda. No necesitas un PDB que coincida; los metadatos llevan los nombres reales de tipos y métodos, y Glass sintetiza los nombres de locales cuando faltan los símbolos.
  • Baja al IL cuando el C# sea ambiguo. Cambia el mismo miembro a la vista de IL cuando un método async o un iterador reconstruido se lea de forma rara — el IL es lo que de verdad se ejecuta, así que es la verdad de base. (Leer el IL lo repasa.)
  • Exporta a un proyecto. Cuando quieras el código en disco para hacer grep, comparar o abrirlo en un IDE, Archivo ▸ Exportar a proyecto (o la CLI glass export) escribe un .csproj más los archivos .cs reconstruidos. El recorrido completo está en exportar un ensamblado a un proyecto compilable.

La idea

El formato de publicación es una decisión de empaquetado. Cambia dónde vive tu IL gestionado, no si se puede leer. Autocontenido deja la DLL en disco — ábrela, no el apphost. Archivo único la esconde dentro del apphost — extrae el bundle, luego abre la DLL. ReadyToRun conserva el IL — abre el ensamblado directamente. Native AOT compila el IL y lo hace desaparecer — y entonces de verdad no hay nada que decompilar, para Glass ni para nadie. Sabe cuál tienes entre manos y la decompilación está a dos clics. Y como Glass.NET es gratuito y estrictamente estático, lees todo sin ejecutar ni una sola línea. Descarga Glass.NET, apúntalo a la DLL gestionada y lee qué se entregó.

Prueba Nebula.NET

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