En la cadena de bloques puedo inspeccionar el código de un contrato y ver los códigos de operación de EVM. ¿Hay alguna manera de descompilar esto y volver a convertirlo en código fuente (Solidity)?
La compilación al código fuente original es imposible porque se eliminan todos los nombres de variables, nombres de tipos e incluso nombres de funciones. Puede ser técnicamente posible llegar a un código fuente que sea similar al código fuente original pero que sea muy complicado, especialmente cuando se usó el optimizador durante la compilación. No conozco ninguna herramienta que haga más que convertir código de bytes en códigos de operación.
Dado que los contratos pueden acceder a su propio código y, por lo tanto, (ab)utilizar el código para almacenar datos, no siempre está claro si una parte del código se usa realmente como código o solo como meros datos y si tiene sentido intentar descompilarlo. . Es computacionalmente indecidible si alguna parte del código es accesible o no.
Tenga en cuenta que no hay un área dedicada para almacenar datos fijos en el momento de la creación (como tablas de búsqueda, etc.). Además del código del contrato, también sería posible almacenar los datos en el almacenamiento, pero eso sería mucho más costoso, por lo que poner dichos datos en el código es algo común.
Hay un proyecto Porosity ahora https://github.com/comaeio/porosity También está integrado en la cadena de herramientas Quorum https://www.coindesk.com/first-ethereum-decompiler-launches-jp-morgan-quorum-integration/
es imposible volver al código de solidez. simplemente podría decodificar el código de bytes en códigos de operación.
mira este ejemplo: https://etherscan.io/opcode-tool?a=0x9e1b57fc92eba6434251a8458811c32690f32c45
En general, como comentaron otros usuarios, no es posible recuperar el código fuente original en la práctica. Sin embargo, en teoría, tanto las aplicaciones compiladas como las fuente deberían producir exactamente la misma salida (es decir, tener la misma semántica), por lo que debería ser posible obtener un programa en representación de código fuente que haga exactamente lo mismo que el código de bytes. La gente ha mencionado otros descompiladores como Porosity. También existen descompiladores (a una representación intermedia) llamados Mythril, EthIR y Vandal. Como usuario en 2018, el descompilador más completo disponible es https://www.contract-library.com . No es una herramienta independiente, pero puede descompilar la mayoría de los contratos que se encuentran actualmente en la red principal de Ethereum y otras redes de prueba.
Y este es el contrato que sugirió Badr Bellaj: https://contract-library.com/contracts/Ethereum/0x9e1b57fc92eba6434251a8458811c32690f32c45
Como puede ver, incluso algunos de los nombres de las funciones se infieren automáticamente, según el conocimiento que adquirió al intentar comprender los contratos anteriores. En general, los descompiladores para Ethereum actualmente no están diseñados para optimizar su salida para el consumo humano; sin embargo, están optimizados para el consumo de otras máquinas (algoritmos) que pueden encontrar vulnerabilidades de seguridad.
Esta no es una respuesta completa, pero describe un enfoque para el cual uno podría escribir un descompilador para Solidity que puede ser mejor que muchos de los descompiladores existentes.
Se basa en la experiencia en un descompilador de Python que desarrollé y mantengo .
la excelente respuesta aceptada de chriseth describe el caso general y supone que desea que la descompilación funcione en todas las situaciones en todo el código.
Pero muchas veces esta podría no ser la situación. Aquí hay algunos escenarios en los que puede hacerlo mejor que en el caso general:
Supongamos que el código que quiero descompilar es una variante del código para el cual tengo disponible el código fuente. (Esto también se menciona en la respuesta de Neville Grech). Tal vez el bytecode/ewasm se generó a partir de una versión anterior o más nueva de la fuente que tengo. Aquí, puedo basarme en el hecho de que muchos de los nombres de las variables y sus tipos ya los conozco, es solo que puede haber algunas pequeñas diferencias en el código. Incluso si una variable "err" en el código fuente que tengo se cambia a "error" en el código fuente perdido que se usó en la compilación, siempre que los tipos sean los mismos, no es tan malo usar el nombre de variable "err" a pesar de que era "error". Es probable que proporcione un nombre más útil que un nombre inventado arbitrariamente.
solc antes de la optimización basada en yul realiza optimización de pila y alguna optimización local, pero no los tipos más disruptivos de optimización "global". Incluso con la optimización, puede ser posible combinar patrones de secuencias de operación para obtener estructuras más grandes como assert
y require
. En Python utilizo un analizador J. Earley que es genial porque permite que las gramáticas se den de forma ambigua . Es decir, una secuencia de códigos de operación podría coincidir con una gramática, varias construcciones diferentes de alto nivel. Pero está bien, porque esa es, de hecho, la naturaleza del juego. En la descompilación, no debe esperar obtener algo que sea exactamente la fuente (aunque eso puede suceder). En su lugar, debe obtener algo que sea equivalente.
Si además conoce la versión de solc que se usó en la compilación y/o el nivel de optimización, eso puede ayudar aún más a reducir los patrones que posiblemente podrían emitirse y, por lo tanto, acortar la gramática y hacerla menos ambigua. Si la versión de solc es anterior a 0.5 o más, sabrá que la optimización de yul no es importante.
Estoy seguro de que en solc hay un código repetitivo por todas partes. Por ejemplo, al inicio de los contratos. Ese código puede coincidir. Hay un código repetitivo que solc usa para ver si el índice en una matriz dinámica es aceptable. Si este patrón de código es único, entonces podríamos inferir que hay una matriz dinámica en uso. De manera similar, el código que emite un "nuevo" podría caer en el reconocimiento de coincidencia de patrones.
Nota: ¿por qué escribo que coincide con los códigos de operación y no con las instrucciones (es decir, el código de operación y el par de operandos)? Esto se debe a que si está haciendo coincidencia de patrones, desea abstraerse un poco; usar el código de operación para la instrucción hace eso. En aquellos casos en los que se debe incluir información del operando, lo que se hace en el descompilador de Python es que el código de operación cambia para reflejar esta pieza adicional de abstracción. No hay nada que dicte que tenga que coincidir con los códigos de operación EVM existentes. Puede crear nuevos códigos de operación, insertar códigos de operación que podrían indicar el límite de la estructura de control o cambiar algunos nombres de códigos de operación para ayudar en la coincidencia de patrones.
Encontré el descompilador Solidity aquí https://www.ethervm.io/decompile
smatthewenglish