Con lenguajes de máquina virtual basados en bytecode como Java, VB.NET, C #, ActionScript 3.0, etc., a veces se escucha lo fácil que es simplemente descargar un descompilador de Internet, ejecutar el bytecode a través de él un buen momento, y a menudo, se trata de algo que no está muy lejos del código fuente original en cuestión de segundos. Supuestamente este tipo de lenguaje es particularmente vulnerable a eso.
Recientemente comencé a preguntarme por qué no escuchas más sobre esto con respecto al código binario nativo, cuando al menos sabes en qué idioma se escribió originalmente (y, por lo tanto, en qué idioma tratar de descompilar). Durante mucho tiempo, pensé que era solo porque el lenguaje de máquina nativo es mucho más loco y complejo que el típico código de bytes.
Pero, ¿cómo se ve el bytecode? Se parece a esto:
1000: 2A 40 F0 14
1001: 2A 50 F1 27
1002: 4F 00 F0 F1
1003: C9 00 00 F2
¿Y cómo se ve el código de máquina nativo (en hexadecimal)? Por supuesto, se ve así:
1000: 2A 40 F0 14
1001: 2A 50 F1 27
1002: 4F 00 F0 F1
1003: C9 00 00 F2
Y las instrucciones provienen de un estado de ánimo algo similar:
1000: mov EAX, 20
1001: mov EBX, loc1
1002: mul EAX, EBX
1003: push ECX
Entonces, dado el lenguaje para tratar de descompilar algunos binarios nativos en, digamos C ++, ¿qué tiene de difícil? Las únicas dos ideas que se me ocurren de inmediato son: 1) realmente es mucho más intrincado que el bytecode, o 2) algo sobre el hecho de que los sistemas operativos tienden a paginar programas y dispersar sus piezas causa demasiados problemas. Si una de esas posibilidades es correcta, explique. Pero de cualquier manera, ¿por qué nunca escuchas de esto básicamente?
NOTA
Estoy a punto de aceptar una de las respuestas, pero quiero mencionar algo primero. Casi todo el mundo se está refiriendo al hecho de que diferentes partes del código fuente original podrían correlacionarse con el mismo código de máquina; se pierden nombres de variables locales, no sabe qué tipo de bucle se utilizó originalmente, etc.
Sin embargo, ejemplos como los dos que acabamos de mencionar son algo triviales a mis ojos. Sin embargo, algunas de las respuestas tienden a indicar que la diferencia entre el código de máquina y la fuente original es drásticamente mucho más que algo tan trivial.
Pero, por ejemplo, cuando se trata de cosas como nombres de variables locales y tipos de bucles, el código de bytes también pierde esta información (al menos para ActionScript 3.0). Ya he recuperado esas cosas a través de un descompilador antes, y realmente no me importaba si se llamaba strMyLocalString:String
o no a una variable loc1
. Todavía podría mirar en ese pequeño alcance local y ver cómo se está utilizando sin muchos problemas. Y un for
bucle es casi exactamente lo mismo que unwhile
bucle, si lo piensas. Además, incluso cuando ejecutaba la fuente a través de irrFuscator (que, a diferencia de secureSWF, no hace mucho más que aleatorizar variables de miembros y nombres de funciones), todavía parece que podría comenzar a aislar ciertas variables y funciones en clases más pequeñas, figura averiguar cómo se usan, asignarles sus propios nombres y trabajar desde allí.
Para que esto sea un gran problema, el código de la máquina necesitaría perder mucha más información que eso, y algunas de las respuestas entran en esto.