Auf der Blockchain kann ich den Code eines Vertrags überprüfen und die EVM-Opcodes sehen. Gibt es eine Möglichkeit, dies zu dekompilieren und wieder in (Solidity-) Quellcode zu konvertieren?
Eine Rückkompilierung in den ursprünglichen Quellcode ist unmöglich, da alle Variablennamen, Typnamen und sogar Funktionsnamen entfernt werden. Es könnte technisch möglich sein, zu einem Quellcode zu gelangen, der dem ursprünglichen Quellcode ähnelt, aber sehr kompliziert ist, insbesondere wenn der Optimierer während der Kompilierung verwendet wurde. Ich kenne keine Tools, die mehr tun, als Bytecode in Opcodes umzuwandeln.
Da Verträge auf ihren eigenen Code zugreifen und den Code somit zur Speicherung von Daten (ab-)verwenden können, ist nicht immer klar, ob ein Teil des Codes tatsächlich als Code oder nur als reine Daten verwendet wird und ob es sinnvoll ist, ihn zu dekompilieren . Es ist rechnerisch unentscheidbar, ob ein Teil des Codes erreichbar ist oder nicht.
Beachten Sie, dass es keinen dedizierten Bereich zum Speichern fester Daten zur Erstellungszeit (wie Nachschlagetabellen usw.) gibt. Abgesehen vom Vertragscode wäre es auch möglich, die Daten im Speicher zu speichern, aber das wäre viel teurer, daher ist das Einfügen solcher Daten in den Code eigentlich eine gängige Sache.
Es gibt jetzt ein Projekt Porosity https://github.com/comaeio/porosity Es ist auch in die Quorum-Toolchain integriert https://www.coindesk.com/first-ethereum-decompiler-launches-jp-morgan-quorum-integration/
Es ist unmöglich, zum Solidity-Code zurückzukehren. Sie könnten den Bytecode einfach in Opcodes decodieren.
Schauen Sie sich dieses Beispiel an: https://etherscan.io/opcode-tool?a=0x9e1b57fc92eba6434251a8458811c32690f32c45
Im Allgemeinen ist es, wie andere Benutzer anmerkten, in der Praxis nicht möglich, den ursprünglichen Quellcode zurückzuerhalten. Theoretisch sollten jedoch sowohl kompilierte als auch Quellanwendungen genau dieselbe Ausgabe erzeugen (dh dieselbe Semantik haben), sodass es möglich sein sollte, ein Programm in Quellcodedarstellung zu erhalten, das genau dasselbe tut wie der Bytecode. Es wurden andere Decompiler wie Porosity erwähnt. Es gibt auch Decompiler (zu einer Zwischendarstellung) namens Mythril, EthIR und Vandal. Als Benutzer im Jahr 2018 ist der vollständigste verfügbare Decompiler https://www.contract-library.com . Es ist kein eigenständiges Tool, kann aber die meisten Verträge dekompilieren, die sich derzeit im Ethereum-Mainnet und anderen Testnetzen befinden.
Und das ist der Vertrag, den Badr Bellaj vorgeschlagen hat: https://contract-library.com/contracts/Ethereum/0x9e1b57fc92eba6434251a8458811c32690f32c45
Wie Sie sehen können, werden sogar einige der Funktionsnamen automatisch abgeleitet, basierend auf dem Wissen, das beim Versuch, vergangene Verträge zu verstehen, erworben wurde. Insgesamt sind Decompiler für Ethereum derzeit nicht so konzipiert, dass ihre Ausgabe für den menschlichen Konsum optimiert ist, sie sind jedoch für den Konsum durch andere Maschinen (Algorithmen) optimiert, die Sicherheitslücken finden können.
Dies ist keine vollständige Antwort, sondern beschreibt einen Ansatz, für den man einen Decompiler für Solidity schreiben könnte, der möglicherweise besser ist als viele der vorhandenen Decompiler.
Es stützt sich auf Erfahrungen mit einem Python-Decompiler, den ich entwickelt und gewartet habe .
Chriseths ausgezeichnete akzeptierte Antwort beschreibt den allgemeinen Fall und geht davon aus, dass die Dekompilierung in allen Situationen über den gesamten Code hinweg funktionieren soll.
Aber oft ist dies möglicherweise nicht der Fall. Hier sind einige Szenarien, in denen Sie möglicherweise besser abschneiden können als im allgemeinen Fall:
Angenommen, der Code, den ich dekompilieren möchte, ist eine Codevariante, für die ich den Quellcode zur Verfügung habe. (Dies wird auch in Neville Grechs Antwort erwähnt.) Vielleicht wurde der Bytecode/Ewasm aus einer älteren oder neueren Version der Quelle generiert, die ich habe. Hier kann ich darauf zurückgreifen, dass ich viele der Variablennamen und deren Typen bereits kenne, nur dass es im Code zu geringfügigen Abweichungen kommen kann. Selbst wenn eine Variable "err" im Quellcode, die ich habe, in "error" im verlorenen Quellcode geändert wird, der bei der Kompilierung verwendet wurde, ist es nicht so schlimm, den Variablennamen "err" zu verwenden, solange die Typen gleich sind. obwohl es "Fehler" war. Es ist wahrscheinlich ein hilfreicherer Name als ein willkürlich erfundener Name.
Solc führt vor der yul-basierten Optimierung eine Stack-Optimierung und einige lokale Optimierungen durch, jedoch nicht die störenderen Arten der "globalen" Optimierung. Selbst mit Optimierung kann es möglich sein, Operationsfolgen nach Mustern abzugleichen, um größere Strukturen wie assert
und zu erhalten require
. In Python verwende ich einen J. Earley-Parser, der cool ist, weil er es ermöglicht, Grammatiken auf mehrdeutige Weise anzugeben. Das heißt, eine Folge von Opcodes könnte mit einer Grammatik übereinstimmen, mehreren unterschiedlichen Konstrukten auf hoher Ebene. Aber das ist in Ordnung, denn das ist in der Tat die Natur des Spiels. Bei der Dekompilierung sollten Sie nicht erwarten, etwas zu erhalten, das genau die Quelle ist (obwohl das passieren kann). Stattdessen sollten Sie etwas Gleichwertiges bekommen.
Wenn Sie zusätzlich die bei der Kompilierung verwendete Solc-Version und/oder die Optimierungsstufe kennen, kann dies weiter dazu beitragen, möglicherweise ausgegebene Muster einzugrenzen und somit die Grammatik zu verkürzen und weniger zweideutig zu machen. Wenn die Solc-Version vor 0.5 oder so liegt, wissen Sie, dass Yul-Optimierung keine Sache ist.
Ich bin mir sicher, dass es in Solc überall Boilerplate-Code gibt. Zum Beispiel bei Vertragsbeginn. Dieser Code kann abgeglichen werden. Es gibt Boilerplate-Code, den Solc verwendet, um zu sehen, ob der Index in einem dynamischen Array akzeptabel ist. Wenn dieses Codemuster eindeutig ist, können wir möglicherweise schlussfolgern, dass ein dynamisches Array verwendet wird. In ähnlicher Weise kann Code, der ein "neues" ausgibt, in die Mustererkennung fallen.
Hinweis: Warum schreibe ich, dass Sie auf Opcodes und nicht auf Anweisungen (dh Opcode- und Operandenpaar) übereinstimmen? Dies liegt daran, dass Sie beim Musterabgleich ein wenig abstrahieren möchten. die Verwendung des Opcodes für die Anweisung tut dies. In den Fällen, in denen Operandeninformationen enthalten sein sollten, ändert sich im Python-Decompiler der Opcode, um dieses zusätzliche Abstraktionselement widerzuspiegeln. Es gibt nichts, was vorschreibt, dass Sie vorhandene EVM-Opcodes abgleichen müssen. Sie können neue Opcodes erfinden, Opcodes einfügen, die die Grenze der Steuerstruktur angeben, oder einige Opcode-Namen ändern, um den Musterabgleich zu unterstützen.
Ich habe den Solidity-Decompiler hier gefunden https://www.ethervm.io/decompile
smatthewenglisch