Coder Social home page Coder Social logo

evmdis's Introduction

evmdis

evmdis is an EVM disassembler. It performs static analysis on the bytecode to provide a higher level of abstraction for the bytecode than raw EVM operations.

Features include:

  • Separates bytecode into basic blocks.
  • Jump target analysis, assigning labels to jump targets and replacing addresses with label names.
  • Composes individual operations into compound expressions where possible.
  • Provides insight into the state of the stack at the start of each block.

Example

The following contract, compiled with solc --optimize:

contract Test {
    function double(uint a) returns (uint) {
        return multiply(a, 2);
    }

    function triple(uint a) returns (uint) {
        return multiply(a, 3);
    }

    function multiply(uint a, uint b) internal returns (uint) {
        return a * b;
    }
}

Produces the following disassembly:

# Stack: []
0x4 MSTORE(0x40, 0x60)
0xD PUSH(CALLDATALOAD(0x0) / 0x2 ** 0xE0)
0x13    DUP1
0x17    JUMPI(:label0, POP() == 0xEEE97206)

# Stack: [@0xD]
0x18    DUP1
0x21    JUMPI(:label2, 0xF40A049D == POP())

# Stack: [@0xD]
0x25    JUMP(0x2)

:label0
# Stack: [@0xD]
0x2A    JUMPI(0x2, CALLVALUE())

# Stack: [@0xD]
0x2B    PUSH(:label3)
0x2F    PUSH(CALLDATALOAD(0x4))
0x30    PUSH(0x0)
0x32    PUSH(:label4)
0x34    DUP3
0x35    PUSH(0x2)

:label1
# Stack: [[0x3 | 0x2] [@0x44 | @0x2F] [:label4 | :label4] [0x0 | 0x0] [@0x44 | @0x2F] [:label3 | :label3] @0xD]
0x38    PUSH(POP() * POP())
0x39    SWAP1
0x3A    JUMP(POP())

:label2
# Stack: [@0xD]
0x3F    JUMPI(0x2, CALLVALUE())

# Stack: [@0xD]
0x40    PUSH(:label3)
0x44    PUSH(CALLDATALOAD(0x4))
0x45    PUSH(0x0)
0x47    PUSH(:label4)
0x49    DUP3
0x4A    PUSH(0x3)
0x4E    JUMP(:label1)

:label3
# Stack: [@0x38 @0xD]
0x50    PUSH(0x40)
0x52    DUP1
0x53    PUSH(MLOAD(POP()))
0x54    SWAP2
0x55    DUP3
0x56    MSTORE(POP(), POP())
0x57    PUSH(MLOAD(POP()))
0x58    SWAP1
0x59    DUP2
0x5A    SWAP1
0x60    RETURN(POP(), 0x20 + POP() - POP())

:label4
# Stack: [@0x38 [0x0 | 0x0] [@0x44 | @0x2F] [:label3 | :label3] @0xD]
0x62    SWAP3
0x63    SWAP2
0x64    POP()
0x65    POP()
0x66    JUMP(POP())

How it works

evmdis works on the principle of static analysis by abstract execution. Abstract execution is the process of simulating the execution of a program, substituting the 'concrete' values for abstract ones representing some useful property. Done properly, this is guaranteed to terminate (unlike regular execution).

Abstract execution is implemented in abstract.go. Callers provide an EvmState object that represents the current state of execution for the analysis in question, with an Advance method that performs some execution before returning zero or more new states. EvmState implementations must be hashable, since they're used as keys in a Go map.

Abstract execution starts with a single initial state. After calling Advance, each of the output states is compared to a map keeping track of all previously seen states. If the output state is not in the map, it is added to the map and to a stack of pending states. The procedure then pops a pending state off the stack and repeats until the stack of pending states is empty.

For example, evmdis performs reaching definition analysis. The goal of this is to generate, for each instruction that produces output, a list of all the places that the output is consumed. It does this by simulating execution of the code, replacing actual stack values with the address of the operation that generated them. Each time we encounter an instruction, we take the top elements of the current stack, and union each element with a set of previous definitions we've seen. The abstract execution semantics ensure that we continue executing for as long as we keep encountering new arrangements of the execution stack.

Analyses

evmdis implements a number of different analyses, each building on the others. Only some of these require abstract execution.

Each analysis annotates instructions or basic blocks (described below) with objects that provide additional data about the code.

Parsing and basic block creation

First, the code is parsed and split up into basic blocks. A basic block is a series of sequential EVM operations that do not contain any control flow (jumps in or out). Each basic block may optionally start with a JUMPDEST, and may optionally end with a JUMP or JUMPI; these operations will never occur inside a block. The sequential nature of basic blocks makes them useful building blocks for analysis.

Reaching analysis

Next, we perform reaching definition analysis on the code, as described above in "How it works". This produces a ReachingDefinition annotation on each reachable basic block and each reachable instruction. A ReachingDefinition is a list of sets of InstructionPointers, pointing to the source of each definition that reaches the given argument.

ReachingDefinition annotations on instructions have the same number of elements as the opcode has input arguments. ReachingDefinition annotations on basic blocks have elements for every stack slot that can be statically determined to be always present.

Reaches analysis

Reaches analysis is the inverse of reaching definition analysis; for each instruction it annotates all the locations that its output reaches. This step does not require symbolic execution; it simply iterates over the reaching definition analysis and inverts it. This produces a ReachesDefinition annotation on each instruction. A ReachesDefinition is a list of instruction pointers.

Jump target creation

This step finds all PUSH instructions whose output are consumed exclusively by JUMP or JUMPI instructions, and converts them to symbolic jump targets, inserting jump targets at the beginning of the appropriate basic blocks.

Relevant PUSH instructions are annotated with an Expression that is an instance of JumpLabel, and relevant basic blocks are annotated with the same JumpLabel.

Expression construction

Expression construction iterates over each basic block, identifying operations that are consumed only at a single location, constructing composite expressions for them. For instance, PUSH1 3 PUSH1 1 PUSH1 2 ADD MUL is rewritten to the expression (1 + 2) * 3. POP operations are elided when possible, and explicitly included as annotations where not. DUP and SWAP operations are eliminated where possible, and adjusted to show the correct number of stack slots when 'lifting' removes elements from the (virtual) stack.

Each instruction that is not part of a subexpression is annotated with an Expression instance.

Building

Retrieve the evmdis source. For example:

go get github.com/Arachnid/evmdis

Build and install evmdis to $GOPATH/bin:

go install github.com/Arachnid/evmdis/evmdis

Sanity check, assuming $GOPATH/bin is in your $PATH:

evmdis -h

evmdis's People

Contributors

arachnid avatar ericpauley avatar evilsocket avatar federicobond avatar shoenseiwaso avatar tomashaber avatar veox avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

evmdis's Issues

Illegal instruction

After installing evmdis, when I execute it via (or any other way actually)

evmdis -h

I get the following message in stdout:

Illegal instruction

I am on android using GNURootDebian. (not sure if is part of the problem)

any idea? (can't find any reference to that message in the source code

Can't install EVMDIS on Ubuntu 20.04 virtual machine

Hi,
I tried the following:
osboxes@osboxes:$ go get github.com/Arachnid/evmdis
osboxes@osboxes:
$ go install github.com/Arachnid/evmdis/evmdis
osboxes@osboxes:$ which evmdis
osboxes@osboxes:
$

osboxes@osboxes:$ evmdis
evmdis: command not found
osboxes@osboxes:
$ whereis evmdis
evmdis:
osboxes@osboxes:~$

Please guide me how to install evmdis?

Zulfi.

Issue with the SWAP1 instruction when both operands are lifted

Hi,
It looks like there is an issue with how the SWAP1 instruction is being processed in the BuildExpressions function. I built the following small test case to illustrate the issue:
root@manage:~/go/src/github.com/Arachnid/evmdis# cat test_case
60008054600160a060020a03908116

This is the corresponding disassembly:
PUSH1 0x00
DUP1
SLOAD
PUSH1 0x01
PUSH1 0xa0
PUSH1 0x02
EXP
SUB
SWAP1
DUP2
AND

And this is what gets generated by the evmdis:
0x4 PUSH(0x0)
0x6 DUP1
0xF PUSH(0x2 ** 0xA0 - 0x1)
0x11 DUP1
0x12 PUSH(POP() & SLOAD(POP()))

This is clearly wrong... trying to follow that disassembly makes one conclude that SLOAD should load the data from completely wrong address.
I've narrowed down the problem to BuildExpressions call, specifically this block:
`

                           if len(reaching) > 2 || (!leftLifted && !rightLifted) {
                                    // One side only is lifted; resolve by making arg explicit again
                                    if leftLifted && !rightLifted {
                                            delete(lifted, *swapFrom.First())
                                    } else if !leftLifted && rightLifted {
                                            delete(lifted, *swapTo.First())
                                    }

                                    if !leftLifted || !rightLifted {
                                            // Count number of non-lifted elements between the operands
                                            count := 0
                                            for i := 1; i < len(reaching)-1; i++ {
                                                    if len(reaching[i]) != 1 || !lifted[*reaching[i].First()] {
                                                            count += 1
                                                    }
                                            }
                                            var expression Expression = &SwapExpression{count + 1}
                                            inst.Annotations.Set(&expression)
                                    }
                            }

`
This block will skip the SWAP1 instruction entirely, since it only has reaching with length=2. Is that intentional? Also, in my test case above, both leftLifted and rightLifted is true. Wouldn't it make sense to unlift both left and right expressions in that case? I made a PR based on these considerations which fixes the above case.
With this fix in place, evmdis generates the following disassembly for this case:
0x4 PUSH(0x0)
0x6 DUP1
0x7 PUSH(SLOAD(POP()))
0xF PUSH(0x2 ** 0xA0 - 0x1)
0x10 SWAP1
0x11 DUP2
0x12 PUSH(POP() & POP())

Let me know what you think.

Program crashes while reaching "reaching.go:80 Advance()" -> invalid memory address

I found a small bug: I tried to use the program to disassemble the DarkDao-controlling-Proxy contract: 0xc0ee9db1a9e07ca63e4ff0d5fb6f86bf68d47b89

The program would crash with error: panic: runtime error: invalid memory address or nil pointer dereference

I narrowed down the problem to the fact that sometimes the program reaches reaching.go: Advance() , but self.nextBlock is no longer defined.

Adding a quickfix line of if(self.nextBlock == nil){return nil, nil;} seemed to have fixed the problem, and the contract disassembly seems complete*

*as far as I have seen so far.

Non-static jump error

One of the contracts used during the SH attacks had the following payload:

3273d3e32594cedbc102d739142aa70d21f4caea56181415305773764d7849614e664f6646614161416153744c6255ff

Which is

[0] ORIGIN 
[21] PUSH20 0xd3e32594cedbc102d739142aa70d21f4caea5618 
[22] EQ 
[23] ISZERO 
[24] ADDRESS 
[25] JUMPI 
[46] PUSH20 0x764d7849614e664f6646614161416153744c6255 
[47] SUICIDE

Note the implicit PUSH from ADDRESS, which, if the check fails, becomes a JUMP-location (which obviously will fail).

Running that with evmdis produces the following:

2017/02/10 14:41:21 Entering block at 0 with stack height 0
panic: Error performing reaching analysis: 25: Could not determine jump location statically; source is 24

goroutine 1 [running]:
main.main()
	/home/martin/go/src/github.com/Arachnid/evmdis/evmdis/main.go:23 +0x411

hangs upon execution

compiling the following with solc --optimize --bin-runtime

pragma solidity ^0.4.11;

contract Simple {
	bytes32 public v;
	function set(bytes32 _v) {
		v = _v;
	}
}

bytecode:

606060405263ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416637c2efcba81146046578063db80813f146068575b600080fd5b3415605057600080fd5b6056607d565b60405190815260200160405180910390f35b3415607257600080fd5b607b6004356083565b005b60005481565b60008190555b505600a165627a7a72305820645352c019aae26da402b9c2edac41e0989caf52f865879af82a9646ae1f3f9c0029

I then try:

evmdis -log <binfile>
evmdis -log <bytecode>

The command hangs till I manually SIGINT.


I try:

cat <binfile> | evmdis -log

I get:

2017/08/03 09:24:35 Entering block at 0 with stack height 0
# Stack: []
0x0	STOP()

I also tried solc --optimize --bin and evmdis -ctor -log, same results.


My setup:

  • evmdis: 1abeda0
  • solc: 0.4.14-develop.2017.7.27+commit.16ca1eea.Linux.g++
  • gcc/g++: 7.1.1
  • linux: 4.11.3-1-ARCH

Doesn't appear to work for Serpent sourced bytecode.

I'm trying to disassemble Augur's REP contract, but the output is way shorter than expected.
https://etherscan.io/address/0x48c80F1f4D53D5951e5D5438B54Cba84f29F32a5#code

Bytecode:

0x600061051f537c01000000000000000000000000000000000000000000000000000000006000350460003413156100495760006000600060003433611388f11515610048576000565b5b63a9059cbb81141561016457365990590160009052366004823760043560605260243560805250335460a05260805160a05112801561008857806100a1565b7401000000000000000000000000000000000000000054155b905080156100af57806100cd565b613a9874010000000000000000000000000000000000000006540142125b9050156100d8576000565b335460805113151580156100ec57806100fc565b6060515460805160605154011215155b905015610107576000565b6080513354033355608051606051540160605155601c60405990590160009052016080518152606051337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602084a3506001610140526020610140f35b6323b872dd8114156103185736599059016000905236600482376004356101605260243560605260443560805250610160515460a05260805160a0511280156101ad57806101d6565b606060605990590160009052600181526101605181602001523381604001528090502054608051135b905080156101e457806101fd565b7401000000000000000000000000000000000000000054155b9050801561020b5780610229565b613a9874010000000000000000000000000000000000000006540142125b905015610234576000565b6101605154608051131515801561024b578061025b565b6060515460805160605154011215155b905015610266576000565b608051606060605990590160009052600181526101605181602001523381604001528090502054036060606059905901600090526001815261016051816020015233816040015280905020556080516101605154036101605155608051606051540160605155601c60405990590160009052016080518152606051610160517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602084a3506001610260526020610260f35b63095ea7b38114156103b4573659905901600090523660048237600435610280526024356102a052506102a051606060605990590160009052600181523381602001526102805181604001528090502055601c60405990590160009052016102a051815261028051337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925602084a35060016102e05260206102e0f35b63d39d08478114156105fe57365990590160009052366004823760043560208201016103005260243560208201016103205250600061034052602061030051035161036052602061032051035161036051141515610410576000565b740100000000000000000000000000000000000000005480156104335780610452565b613a987401000000000000000000000000000000000000000654014212155b90501561045d576000565b740100000000000000000000000000000000000000055433141515610480576000565b5b6103605161034051121561058157610340516020026103005101515415156104c0577401000000000000000000000000000000000000000054156104c3565b60005b1561057157610340516020026103205101516103405160200261030051015155610340516020026103205101517401000000000000000000000000000000000000000154017401000000000000000000000000000000000000000155601c60405990590160009052016103405160200261032051015181526103405160200261030051015160007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602084a3505b6001610340510161034052610481565b6a09195731e2ce35eb000000740100000000000000000000000000000000000000015414156105c457600174010000000000000000000000000000000000000000555b6a09195731e2ce35eb000000740100000000000000000000000000000000000000015413156105f1576000565b60016103a05260206103a0f35b63943f401b8114156106805773668fc8d2004379275357c8d8e2502e19153adeee3314151561062b576000565b6000541561067b576000547401000000000000000000000000000000000000000154037401000000000000000000000000000000000000000155600060005560016103c05260206103c0f361067f565b6000565b5b63dd62ed3e8114156106db5736599059016000905236600482376004356103e0526024356102805250606060605990590160009052600181526103e05181602001526102805181604001528090502054610400526020610400f35b6318160ddd811415610709577401000000000000000000000000000000000000000154610440526020610440f35b6370a0823181141561073b57365990590160009052366004823760043561046052506104605154610480526020610480f35b6306fdde038114156107695774010000000000000000000000000000000000000002546104a05260206104a0f35b63313ce5678114156107975774010000000000000000000000000000000000000004546104c05260206104c0f35b6395d89b418114156107c55774010000000000000000000000000000000000000003546104e05260206104e0f35b63e44b11a28114156107f3577401000000000000000000000000000000000000000054610500526020610500f35b50

evmdis output:

# Stack: []
0x5     MSTORE8(0x51F, 0x0)
0x27    PUSH(CALLDATALOAD(0x0) / 0x100000000000000000000000000000000000000000000000000000000)
0x30    JUMPI(:label0, !SGT(CALLVALUE(), 0x0))

# Stack: [@0x27]
0x44    JUMPI(:label0, !!CALL(0x1388, CALLER(), CALLVALUE(), 0x0, 0x0, 0x0, 0x0))

# Stack: [@0x27]
0x47    JUMP(0x0)

:label0
# Stack: [@0x27]
0x4F    DUP1
0x55    JUMPI(0x164, !(POP() == 0xA9059CBB))

# Stack: [@0x27]
0x57    PUSH(MSIZE())
0x5E    MSTORE(MSIZE() + CALLDATASIZE(), 0x0)
0x62    DUP1
0x63    CALLDATACOPY(POP(), 0x4, CALLDATASIZE())
0x66    PUSH(CALLDATALOAD(0x4))
0x67    STOP()

Original source:
https://github.com/AugurProject/augur-core/blob/develop/src/legacyRepContract.se

Add usage instructions to README and help.

I build/installed evmdis and ran evmdis -h. It shows me flags I can pass but doesn't actually tell me how to use it. I took a guess and did:
echo <some evm bytecode> | evmdis but that resulted in:

# Stack: []
0x0     STOP()

Which doesn't seem correct. Readme should have usage instructions, and the CLI help should as well.

Disassembler loops forever with this bytecode

When trying to disassemble the following:

60006014525b600a601451101561001c5760016001601451016014525b600a60145110156100055700

The tool seems to loop forever. This runs fine if fed to "./evm --code".

Any ideas? From a quick glance, it seems like there may be an issue with the forward reference in for the JUMP :label creation.

A newcomer need a detailed example

After I downloaded evmdis, I don't how to configure the environment and use it. If I got the bytecode "0x606060405260388060106000396000f3606060405260e060020a6000350463c6888fa18114601c575b6002565b3460025760076004350260408051918252519081900360200190f3". What should I do to analysis it with evmdis? Can you help me list the detailed steps?Thank you very much!

Am I use evmdis only when I compile a contract ?

Hello,I am confused about when and who use evmdis?Only the creator who creat the contract can use evmdis to disassemble contract when the creator compile the contrct?if I’m not the creator,and I only have the bytecode of a contract,can I use evmdis to disassemble bytecode and know how a contract run?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.