Un decompilador es un programa de computadora que toma un archivo ejecutable como entrada e intenta crear un archivo fuente de alto nivel que se puede volver a compilar con éxito. Por lo tanto, es lo opuesto a un compilador , que toma un archivo fuente y lo convierte en ejecutable. Los descompiladores generalmente no pueden reconstruir perfectamente el código fuente original y, como tal, con frecuencia producirán código ofuscado . No obstante, los descompiladores siguen siendo una herramienta importante en la ingeniería inversa del software de computadora .
Introducción
El término descompilador se aplica más comúnmente a un programa que traduce programas ejecutables (la salida de un compilador ) en código fuente en un lenguaje de (relativamente) alto nivel que, cuando se compila, producirá un ejecutable cuyo comportamiento es el mismo que el ejecutable original. programa. En comparación, un desensamblador traduce un programa ejecutable a lenguaje ensamblador (y se podría usar un ensamblador para ensamblarlo nuevamente en un programa ejecutable).
La descompilación es el acto de utilizar un descompilador, aunque el término también puede referirse a la salida de un descompilador. Se puede utilizar para la recuperación de código fuente perdido y también es útil en algunos casos para la seguridad informática , interoperabilidad y corrección de errores . [1] El éxito de la descompilación depende de la cantidad de información presente en el código que se descompila y de la sofisticación del análisis realizado en él. Los formatos de código de bytes utilizados por muchas máquinas virtuales (como Java Virtual Machine o .NET Framework Common Language Runtime ) a menudo incluyen metadatos extensos y características de alto nivel que hacen que la descompilación sea bastante factible. La presencia de datos de depuración puede hacer posible reproducir los nombres de estructura y variable originales e incluso los números de línea. El lenguaje de máquina sin tales metadatos o datos de depuración es mucho más difícil de descompilar. [2]
Algunos compiladores y herramientas de poscompilación producen código ofuscado (es decir, intentan producir una salida que es muy difícil de descompilar o que se descompila en una salida confusa). Esto se hace para dificultar la ingeniería inversa del ejecutable.
Mientras que los descompiladores se utilizan normalmente para (volver a) crear código fuente a partir de ejecutables binarios, también hay descompiladores para convertir archivos de datos binarios específicos en fuentes editables y legibles por humanos. [3] [4]
Diseño
Se puede pensar que los descompiladores están compuestos por una serie de fases, cada una de las cuales aporta aspectos específicos del proceso general de descompilación.
Cargador
La primera fase de descompilación carga y analiza el código de máquina de entrada o el formato de archivo binario del programa de lenguaje intermedio . Debería poder descubrir datos básicos sobre el programa de entrada, como la arquitectura (Pentium, PowerPC, etc.) y el punto de entrada. En muchos casos, debería poder encontrar el equivalente de la función de un programa en C , que es el inicio del código escrito por el usuario . Esto excluye el código de inicialización en tiempo de ejecución, que no debe descompilarse si es posible. Si están disponibles, también se cargan las tablas de símbolos y los datos de depuración. El front-end puede identificar las bibliotecas utilizadas incluso si están vinculadas con el código, esto proporcionará interfaces de biblioteca. Si puede determinar el compilador o los compiladores utilizados, puede proporcionar información útil para identificar los modismos del código. [5]main
Desmontaje
La siguiente fase lógica es el desmontaje de las instrucciones del código de máquina en una representación intermedia independiente de la máquina (IR). Por ejemplo, la instrucción de la máquina Pentium
mov eax , [ ebx + 0x04 ]
podría traducirse al IR
eax : = m [ ebx + 4 ] ;
Modismos
Las secuencias idiomáticas de código de máquina son secuencias de código cuya semántica combinada no es inmediatamente evidente a partir de la semántica individual de las instrucciones. Ya sea como parte de la fase de desensamblaje o como parte de análisis posteriores, estas secuencias idiomáticas deben traducirse en IR equivalentes conocidos. Por ejemplo, el código ensamblador x86 :
cdq eax ; edx se establece en la extensión de signo ≠ edi, edi + (tex) push xor eax , edx sub eax , edx
podría traducirse a
eax: = abs (eax);
Algunas secuencias idiomáticas son independientes de la máquina; algunos involucran solo una instrucción. Por ejemplo, borra el registro (lo pone a cero). Esto se puede implementar con una regla de simplificación independiente de la máquina, como .xor eax, eax
eax
a = 0
En general, es mejor retrasar la detección de secuencias idiomáticas si es posible, a etapas posteriores que se ven menos afectadas por el orden de las instrucciones. Por ejemplo, la fase de programación de instrucciones de un compilador puede insertar otras instrucciones en una secuencia idiomática o cambiar el orden de las instrucciones en la secuencia. Un proceso de coincidencia de patrones en la fase de desmontaje probablemente no reconocería el patrón alterado. Las fases posteriores agrupan expresiones de instrucción en expresiones más complejas y las modifican en una forma canónica (estandarizada), lo que hace más probable que incluso el idioma alterado coincida con un patrón de nivel superior más adelante en la descompilación.
Es particularmente importante reconocer los modismos del compilador para llamadas a subrutinas , manejo de excepciones y declaraciones de cambio . Algunos lenguajes también tienen un amplio soporte para cadenas o enteros largos .
Análisis de programas
Se pueden aplicar varios análisis de programas al IR. En particular, la propagación de expresiones combina la semántica de varias instrucciones en expresiones más complejas. Por ejemplo,
mov eax , [ ebx + 0x04 ] agregar eax , [ ebx + 0x08 ] sub [ ebx + 0x0C ], eax
podría resultar en el siguiente IR después de la propagación de la expresión:
m [ebx + 12]: = m [ebx + 12] - (m [ebx + 4] + m [ebx + 8]);
La expresión resultante se parece más a un lenguaje de alto nivel y también ha eliminado el uso del registro de máquina eax
. Los análisis posteriores pueden eliminar el ebx
registro.
Análisis de flujo de datos
Los lugares donde se definen y utilizan los contenidos de los registros se deben rastrear mediante análisis de flujo de datos . El mismo análisis se puede aplicar a ubicaciones que se utilizan para datos temporales y locales. Entonces se puede formar un nombre diferente para cada conjunto conectado de definiciones de valor y usos. Es posible que se haya utilizado la misma ubicación de variable local para más de una variable en diferentes partes del programa original. Peor aún, es posible que el análisis del flujo de datos identifique una ruta por la cual un valor puede fluir entre dos de esos usos, aunque nunca suceda o importe en la realidad. En casos graves, esto puede llevar a la necesidad de definir una ubicación como una unión de tipos. El descompilador puede permitir al usuario romper explícitamente esas dependencias antinaturales que conducirán a un código más claro. Por supuesto, esto significa que una variable se usa potencialmente sin ser inicializada y, por lo tanto, indica un problema en el programa original.
Análisis de tipo
Un buen descompilador de código de máquina realizará un análisis de tipo. En este caso, la forma en que se utilizan los registros o las ubicaciones de memoria genera restricciones sobre el posible tipo de ubicación. Por ejemplo, una and
instrucción implica que el operando es un número entero; los programas no utilizan dicha operación en valores de punto flotante (excepto en el código de biblioteca especial) o en punteros . Una add
instrucción da como resultado tres restricciones, ya que los operandos pueden ser ambos enteros, o un entero y un puntero (con resultados enteros y punteros respectivamente; la tercera restricción proviene del orden de los dos operandos cuando los tipos son diferentes). [6]
Se pueden reconocer varias expresiones de alto nivel que desencadenan el reconocimiento de estructuras o matrices. Sin embargo, es difícil distinguir muchas de las posibilidades, debido a la libertad que el código máquina o incluso algunos lenguajes de alto nivel como C permiten con conversiones y aritmética de punteros.
El ejemplo de la sección anterior podría resultar en el siguiente código de alto nivel:
struct T1 * ebx ; struct T1 { int v0004 ; int v0008 ; int v000C ; }; ebx -> v000C - = ebx -> v0004 + ebx -> v0008 ;
Estructuración
La penúltima fase de descompilación implica la estructuración del IR en construcciones de nivel superior, como while
bucles y if/then/else
declaraciones condicionales. Por ejemplo, el código de la máquina
xor eax , eax l0002: o ebx , ebx jge l0003 agregar eax , [ ebx ] mov ebx , [ ebx + 0x4 ] jmp l0002 l0003: mov [ 0x10040000 ], eax
podría traducirse en:
eax = 0 ; while ( ebx < 0 ) { eax + = ebx -> v0000 ; ebx = ebx -> v0004 ; } v10040000 = eax ;
El código no estructurado es más difícil de traducir a código estructurado que el código ya estructurado. Las soluciones incluyen replicar algún código o agregar variables booleanas. [7]
Codigo de GENERACION
La fase final es la generación del código de alto nivel en el back-end del descompilador. Así como un compilador puede tener varios backends para generar código de máquina para diferentes arquitecturas, un descompilador puede tener varios backends para generar código de alto nivel en diferentes lenguajes de alto nivel.
Justo antes de la generación de código, puede ser deseable permitir una edición interactiva del IR, quizás usando alguna forma de interfaz gráfica de usuario . Esto permitiría al usuario ingresar comentarios y nombres de funciones y variables no genéricos. Sin embargo, estos se ingresan casi tan fácilmente en una edición posterior a la descompilación. Es posible que el usuario desee cambiar aspectos estructurales, como convertir un while
bucle en un for
bucle. Estos se modifican con menos facilidad con un editor de texto simple, aunque las herramientas de refactorización del código fuente pueden ayudar con este proceso. El usuario puede necesitar ingresar información que no pudo ser identificada durante la fase de análisis de tipo, por ejemplo, modificando una expresión de memoria a una expresión de matriz o estructura. Por último, es posible que sea necesario corregir un IR incorrecto o realizar cambios para que el código de salida sea más legible.
Legalidad
La mayoría de los programas informáticos están cubiertos por las leyes de derechos de autor . Aunque el alcance exacto de lo que cubre el derecho de autor difiere de una región a otra, la ley de derechos de autor generalmente proporciona al autor (el (los) programador (es) o el empleador) una colección de derechos exclusivos del programa. [8] Estos derechos incluyen el derecho a realizar copias, incluidas las realizadas en la memoria RAM de la computadora (a menos que la creación de dicha copia sea esencial para utilizar el programa). [9] Dado que el proceso de descompilación implica la realización de varias copias, por lo general está prohibido sin la autorización del titular de los derechos de autor. Sin embargo, debido a que la descompilación es a menudo un paso necesario para lograr la interoperabilidad del software , las leyes de derechos de autor tanto en los Estados Unidos como en Europa permiten la descompilación en un grado limitado.
En los Estados Unidos, la defensa del uso justo de los derechos de autor se ha invocado con éxito en casos de descompilación. Por ejemplo, en Sega v. Accolade , el tribunal sostuvo que Accolade podría participar legalmente en la descompilación para eludir el mecanismo de bloqueo de software utilizado por las consolas de juegos de Sega. [10] Además, la Ley de Derechos de Autor del Milenio Digital (LEY PÚBLICA 105-304 [11] ) tiene las exenciones adecuadas para las Pruebas y Evaluación de Seguridad en §1201 (i), y la Ingeniería Inversa en §1201 (f). [12]
En Europa, la Directiva de software de 1991 establece explícitamente el derecho a descompilar para lograr la interoperabilidad. Como resultado de un acalorado debate entre, por un lado, proteccionistas del software y, por otro, académicos y desarrolladores de software independientes, el artículo 6 permite la descompilación solo si se cumplen una serie de condiciones:
- Primero, una persona o entidad debe tener una licencia para usar el programa que se va a descompilar.
- En segundo lugar, la descompilación debe ser necesaria para lograr la interoperabilidad con el programa de destino u otros programas. Por lo tanto, la información de interoperabilidad no debe estar disponible fácilmente, como a través de manuales o documentación de API . Ésta es una limitación importante. El descompilador debe probar la necesidad. El propósito de esta importante limitación es principalmente proporcionar un incentivo para que los desarrolladores documenten y divulguen la información de interoperabilidad de sus productos. [13]
- En tercer lugar, el proceso de descompilación debe, si es posible, limitarse a las partes del programa de destino relevantes para la interoperabilidad. Dado que uno de los propósitos de la descompilación es comprender la estructura del programa, esta tercera limitación puede ser difícil de cumplir. Una vez más, la carga de la prueba recae en el descompilador.
Además, el artículo 6 prescribe que la información obtenida mediante la descompilación no se puede utilizar para otros fines y que no se puede dar a otros.
En general, el derecho de descompilación proporcionado por el artículo 6 codifica lo que se afirma que es una práctica común en la industria del software. Se sabe que han surgido pocas demandas europeas por el derecho de descompilación. Esto podría interpretarse en el sentido de una de tres cosas:
- ) el derecho de descompilación no se utiliza con frecuencia y, por lo tanto, el derecho de descompilación puede haber sido innecesario,
- ) el derecho de descompilación funciona bien y proporciona suficiente seguridad jurídica para no dar lugar a disputas legales o
- ) la descompilación ilegal pasa desapercibida en gran medida.
En un informe de 2000 sobre la implementación de la Directiva de Software por parte de los estados miembros europeos, la Comisión Europea parecía apoyar la segunda interpretación. [14]
Herramientas
Los descompiladores suelen tener como objetivo un formato binario específico. Algunos son conjuntos de instrucciones nativos (por ejemplo, Intel x86, ARM, MIPS), otros son códigos de bytes para máquinas virtuales (Dalvik, archivos de clase Java, WebAssembly, Ethereum).
Debido a la pérdida de información durante la compilación, la descompilación casi nunca es perfecta y no todos los descompiladores funcionan igual de bien para un formato binario determinado. Hay estudios que comparan el desempeño de diferentes descompiladores. [15]
Ver también
- Recompilador binario
- Vinculador (informática)
- Interpretación abstracta
- Descompilador de moca
- Descompilador JD
- Descompilador JAD
- Reflector de .NET
- Descompilador JEB (Android Dalvik, Intel x86, ARM, MIPS, WebAssembly, Ethereum)
- Editor de recursos
Referencias
- ↑ Van Emmerik, Mike (29 de abril de 2005). "Por qué la descompilación" . Program-transformation.org . Consultado el 15 de septiembre de 2010 .
- ^ Miecznikowski, Jerome; Hendren, Laurie (2002). "Descompilación de código de bytes de Java: problemas, trampas y trampas". En Horspool, R. Nigel (ed.). Construcción del compilador: 11a Conferencia Internacional, actas / CC 2002 . Springer-Verlag . págs. 111-127. ISBN 3-540-43369-4.
- ^ Paul, Matthias R. (10 de junio de 2001) [1995]. "Descripción de formato de archivos DOS, OS / 2 y Windows NT .CPI y Linux .CP" (archivo CPI.LST) (1.30 ed.). Archivado desde el original el 20 de abril de 2016 . Consultado el 20 de agosto de 2016 .
- ^ Paul, Matthias R. (13 de mayo de 2002). "[fd-dev] mkeyb" . freedos-dev . Archivado desde el original el 10 de septiembre de 2018 . Consultado el 10 de septiembre de 2018 .
[…] Analizador, validador y descompilador de archivos de página de códigos .CPI y .CP […] Descripción general sobre / Parámetros de estilo: […] Archivos fuente ASM incluidos […] Archivos fuente ASM autónomos […] Archivos fuente ASM modulares […]
- ^ Cifuentes, Cristina; Gough, K. John (julio de 1995). "Descompilación de programas binarios". Práctica y experiencia en software . 25 (7): 811–829. CiteSeerX 10.1.1.14.8073 . doi : 10.1002 / spe.4380250706 . S2CID 8229401 .
- ^ Mycroft, Alan (1999). "Descompilación basada en tipos". En Swierstra, S. Doaitse (ed.). Lenguajes y sistemas de programación: VIII Simposio Europeo sobre Lenguajes y Sistemas de Programación . Springer-Verlag . págs. 208–223. ISBN 3-540-65699-5.
- ^ Cifuentes, Cristina (1994). "Capítulo 6". Técnicas de compilación inversa (PDF) (tesis doctoral). Universidad de Tecnología de Queensland . Archivado (PDF) desde el original el 22 de noviembre de 2016 . Consultado el 21 de diciembre de 2019 .)
- ^ Rowland, Diane (2005). Ley de tecnología de la información (3 ed.). Cavendish. ISBN 1-85941-756-6.
- ^ "Oficina de derechos de autor de EE. UU. - Ley de derechos de autor: Capítulo 1" .
- ^ "La legalidad de la descompilación" . Program-transformation.org. 2004-12-03 . Consultado el 15 de septiembre de 2010 .
- ^ "Ley de derechos de autor del milenio digital" (PDF) . Congreso de Estados Unidos . 1998-10-28 . Consultado el 15 de noviembre de 2013 .
- ^ https://www.federalregister.gov/d/2018-23241/p-26
- ^ Czarnota, Bridget; Hart, Robert J. (1991). Protección legal de programas informáticos en Europa: una guía de la directiva de la CE . Londres: Butterworths Tolley . ISBN 0-40600542-7.
- ^ "Informe de la Comisión al Consejo, al Parlamento Europeo y al Comité Económico y Social sobre la aplicación y los efectos de la Directiva 91/250 / CEE sobre la protección jurídica de los programas de ordenador" .
- ^ Harrand, Nicolas; Soto-Valero, Cesar; Monperrus, Martin; Baudry, Benoit (2019). "Las fortalezas y las peculiaridades del comportamiento de los descompiladores de códigos de bytes de Java". XIX Conferencia de Trabajo Internacional sobre Análisis y Manipulación de Código Fuente (SCAM) . IEEE : 92–102. arXiv : 1908.06895 . Código bibliográfico : 2019arXiv190806895H . doi : 10.1109 / SCAM.2019.00019 . ISBN 978-1-7281-4937-0. S2CID 201070711 .
enlaces externos
- Descompiladores y desmontadores en Curlie