2009-03-22 29 views
16

¿Por qué es tan fácil descompilar .NET IL-code en código fuente, en comparación con la descompilación de binarios x86 nativos? (Reflector produce código fuente bastante bueno la mayor parte del tiempo, mientras que descompilar el resultado de un compilador C++ es casi imposible.)¿Por qué es tan fácil descompilar el código .NET IL?

¿Es porque IL contiene muchos metadatos? ¿O es porque IL es una abstracción más alta que las instrucciones x86? Hice algunas investigaciones y encontré los siguientes dos artículos útiles, pero ninguno de ellos responde mi pregunta.

+0

Hay (o hubo) descompiladores bastante buenos para C/C++, con complementos de biblioteca para diferentes versiones de Watcom, Borland, Microsoft y otros compiladores populares. P.ej. IDA. Sin embargo, eso no hace que lo que diga sea incorrecto, CLI * es * un entorno de tiempo de ejecución más abstracto, de mayor nivel pero más limpio que x86. –

+0

IDA no es un descompilador, aunque la compañía que lo hace sí hace un descompilador llamado rayos-hex. La calidad de la descompilación x86 es de calidad mucho más baja que la calidad de la descompilación jvm o msil. –

Respuesta

24

Creo que ya tienes las partes más importantes.

  • Como dices, hay más metadatos disponibles. No sé los detalles de lo que se emite por un compilador C o C++, pero sospecho que ahora más nombres e información similar se incluyen en IL. Basta con mirar lo que el decompilador sabe acerca de lo que hay en un marco de pila particular, por ejemplo: en lo que respecta al x86, usted solo sabe cómo es la pila utilizada; en IL usted sabe lo que el contenido de la pila representa (o al menos, el tipo - ¡no el significado semántico!)
  • De nuevo, como ya ha mencionado, IL es una abstracción de nivel superior a x86. x86 no tiene idea de lo que es un método o llamada de función, o un evento, o una propiedad etc. IL tiene toda esa información todavía dentro de ella.
  • Normalmente los compiladores C y C++ optimizan mucho más que (por ejemplo) el compilador C#. Esto se debe a que el compilador de C# asume que la mayor parte de la optimización aún se puede realizar más tarde, mediante el JIT.De alguna manera, tiene sentido que el compilador de C# no intente hacer mucha optimización, ya que hay varios bits de información que están disponibles para el JIT pero no para el compilador de C#. El código optimizado es más difícil de descompilar, porque está más lejos de ser una representación natural del código fuente original.
  • IL fue diseñado para ser compilado JIT; x86 fue diseñado para ser ejecutado de forma nativa (es cierto que a través de microcódigo). La información que el compilador JIT necesita es similar a la que un descompilador querría, por lo que un descompilador tiene un tiempo más fácil con IL. De alguna manera esto es realmente solo una reformulación del segundo punto.
+4

Razón de bonificación: IL debe ser verificable de tipo seguro, lo que limita los tipos de optimizaciones disponibles, de lo contrario el verificador no podrá decir "Sí, este código no infringe ninguna de las reglas. Lo permitiré que se ejecute". –

4

C# y IL casi mapa uno-a-uno. (Esto es menos así con algunas características más recientes de C# 3.0.) La cercanía de la asignación (y la falta de un optimizador en el compilador de C#) hace que las cosas sean tan 'reversibles'.

9

Hay una cantidad de cosas que hacen que la ingeniería inversa sea bastante fácil.

  • Información de tipo. Esto es masivo. En el ensamblador x86, debe inferir los tipos de variables en función de cómo se usan.

  • estructura. La información sobre la estructura de la aplicación está más disponible en desmontables. Esto, combinado con la información del tipo, le brinda una increíble cantidad de datos. Está trabajando a un nivel bastante alto en este punto (relativo al ensamblador x86). En ensamblador nativo, debe deducir los diseños de estructura (e incluso el hecho de que son estructuras) en función de cómo se utilizan los datos. No es imposible, pero consume mucho más tiempo.

  • nombres. Conocer los nombres de las cosas puede ser útil.

Estas cosas, combinadas, significan que tiene bastantes datos sobre el ejecutable. Básicamente, estoy trabajando en un nivel mucho más cercano a la fuente de lo que sería un compilador de código nativo. El nivel más alto en el que funciona el bytecode, la ingeniería inversa más fácil es, en términos generales.

3

Extendiendo respuesta correcta de Brian

Si usted piensa que todo es fácilmente IL decompilable, sugiero escribir un programa C# no trivial e intentar descompilar el código. F # realiza una gran cantidad de transformaciones de código y, por lo tanto, tiene un mapeo muy pobre a partir de la IL emitida real y la base de código original. En mi humilde opinión, es significativamente más difícil mirar el código descompuesto F # y recuperar el programa original que C# o VB.Net.

Cuestiones relacionadas