解密EVM實現機制

以下都是來自我的新書《解密EVM機制及合約安全漏洞》裏的內容

電子版PDF下載https://download.csdn.net/download/softgmx/10800947

 

研究環境:

OS

ubuntu 16.04

VM及合約語言

EVM/ solidity

合約調試器

https://remix.ethereum.org

Ethererum源碼

go語言版本的

 

EVM機制原理

智能合約容易產生漏洞的主要原因:

  • 開發人員對EVM的運行機制不瞭解
  1. stack、memory和storage是怎樣存儲數據的
  2. 合約間調用是怎樣實現的,傳參和返回值又是怎樣在合約間傳遞的
  3. 用鏈上數據做隨機數種子時,應注意僞隨機的問題(鏈上數據是可見的,合約裏定義的私有變量實際是公開可見的,且一些字段是可以被礦工操縱的)
  4. solidity封裝好的區塊訪問方法實際上是讀取區塊鏈的哪部分數據
  5. 區塊鏈中交易費用出價高者可以插隊優先交易
  6. gas的消耗實現機制
  • 開發人員對solidity編譯器的一些內部處理不瞭解
  1. send、transfer和call底層轉賬方法的實現原理和區別
  2. fallback機制的實現原理
  3. storage變量的存儲和索引原理
  • 開發人員對solidity語言不熟悉
  1. 構造函數
  2. 鑑權方法
  3. 日誌記錄
  4. 算數溢出

 

這樣我們分析問題可以抽象出的三個層次來研究,如下圖:

 

                                                  圖一 智能合約的層次

 

以太坊的智能合約機(EVM)構成及工作原理:

每次我們call一個合約方法時,在call的函數實現裏,首先會創建一個contract 類對象,並填充對應的字段值(code,CallerAddress,Input,gas,value,selfAddress),然後把這個contract對象傳入EVM的解釋器Interpeter進行逐條指令的解釋執行,但在開始解釋之前,會生成一個新的stack和一個memory對象以用於後面程序的運行,並把結果寫入合約地址對應的StateDB。

 

                                                          圖二 EVM的工作原理

下面列出了EVM的幾個關鍵類的定義:

 

                                                                    圖三 對應的類圖

 

 

                     

                                 圖四 EVM執行智能合約中function的過程

 

EVM三大核心部件:

  1. stack
  2. memory
  3. storage

 

Stack的實現:

    type Stack struct {

            data []*big.Int

    }

  1. 最小單元32字節(最小對齊單位)
  2. 初始容量1024個元素
  3. 以動態數組方式實現,理論上可擴容(但實際上一個方法只有1024個元素可用)
  4. 主要指令push、pop、swap 、dup
  5. 數據不持久化
  6. 合約間調用,方法之間不共用棧(一個方法分配一個棧)
  7. 同一合約的方法調用不產生call, 直是簡單的 jump

    

Memory的實現:

        type Memory struct {

                store       []byte

                lastGasCost uint64

         }

  • 最小單位有一個字節
  • 以動態字節數組方式實現,可以擴容
  • 類似x86架構裏的堆,數據不具有持久性,合約執行完成,數據消失
  • 主要指令mload、mstore

Storage的實現:

  • 通過StateDB把數據存儲在區塊鏈上
  • 主要指令sload、sstore
  • 數據被持久化(寫在鏈上了,並在所有礦機上同步)
  • 容量是2的256次冪,storage[slot]=value,   key和value都是32位對齊的

slot=[ 0x0000000000000000000000000000000000000000000000000000000000000000,……

,0xffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff]

命令集分類:

  • 算術、邏輯運算(addsubmuldivmodlt ,gt notxororand)
  • 棧操作、內存操作、存儲操作(pushpopmloadmstoresloadsstore)
  • 區塊鏈操作(addressbalancegascallercallvalueoriginblockhashtimestampdifficultygaslimitcoinbaselog
  • 執行流操作(jumpjumpicallcallcodedelegatecall staticcall

 

命令集具體說明:

Instruction

 

 

Explanation

stop

-

F

stop execution, identical to return(0,0)

add(x, y)

 

F

x + y

sub(x, y)

 

F

x - y

mul(x, y)

 

F

x * y

div(x, y)

 

F

x / y

sdiv(x, y)

 

F

x / y, for signed numbers in two’s complement

mod(x, y)

 

F

x % y

smod(x, y)

 

F

x % y, for signed numbers in two’s complement

exp(x, y)

 

F

x to the power of y

not(x)

 

F

~x, every bit of x is negated

lt(x, y)

 

F

1 if x < y, 0 otherwise

gt(x, y)

 

F

1 if x > y, 0 otherwise

slt(x, y)

 

F

1 if x < y, 0 otherwise, for signed numbers in two’s complement

sgt(x, y)

 

F

1 if x > y, 0 otherwise, for signed numbers in two’s complement

eq(x, y)

 

F

1 if x == y, 0 otherwise

iszero(x)

 

F

1 if x == 0, 0 otherwise

and(x, y)

 

F

bitwise and of x and y

or(x, y)

 

F

bitwise or of x and y

xor(x, y)

 

F

bitwise xor of x and y

byte(n, x)

 

F

nth byte of x, where the most significant byte is the 0th byte

shl(x, y)

 

C

logical shift left y by x bits

shr(x, y)

 

C

logical shift right y by x bits

sar(x, y)

 

C

arithmetic shift right y by x bits

addmod(x, y, m)

 

F

(x + y) % m with arbitrary precision arithmetics

mulmod(x, y, m)

 

F

(x * y) % m with arbitrary precision arithmetics

signextend(i, x)

 

F

sign extend from (i*8+7)th bit counting from least significant

keccak256(p, n)

 

F

keccak(mem[p…(p+n)))

sha3(p, n)

 

F

keccak(mem[p…(p+n)))

jump(label)

-

F

jump to label / code position

jumpi(label, cond)

-

F

jump to label if cond is nonzero

pc

 

F

current position in code

pop(x)

-

F

remove the element pushed by x

dup1 … dup16

 

F

copy ith stack slot to the top (counting from top)

swap1 … swap16

*

F

swap topmost and ith stack slot below it

mload(p)

 

F

mem[p..(p+32))

mstore(p, v)

-

F

mem[p..(p+32)) := v

mstore8(p, v)

-

F

mem[p] := v & 0xff (only modifies a single byte)

sload(p)

 

F

storage[p]

sstore(p, v)

-

F

storage[p] := v

msize

 

F

size of memory, i.e. largest accessed memory index

gas

 

F

gas still available to execution

address

 

F

address of the current contract / execution context

balance(a)

 

F

wei balance at address a

caller

 

F

call sender (excluding delegatecall)

callvalue

 

F

wei sent together with the current call

calldataload(p)

 

F

call data starting from position p (32 bytes)

calldatasize

 

F

size of call data in bytes

calldatacopy(t, f, s)

-

F

copy s bytes from calldata at position f to mem at position t

codesize

 

F

size of the code of the current contract / execution context

codecopy(t, f, s)

-

F

copy s bytes from code at position f to mem at position t

extcodesize(a)

 

F

size of the code at address a

extcodecopy(a, t, f, s)

-

F

like codecopy(t, f, s) but take code at address a

returndatasize

 

B

size of the last returndata

returndatacopy(t, f, s)

-

B

copy s bytes from returndata at position f to mem at position t

create(v, p, s)

 

F

create new contract with code mem[p..(p+s)) and send v wei and return the new address

create2(v, n, p, s)

 

C

create new contract with code mem[p..(p+s)) at address keccak256(<address> . n . keccak256(mem[p..(p+s))) and send v wei and return the new address

call(g, a, v, in, insize, out, outsize)

 

F

call contract at address a with input mem[in..(in+insize)) providing g gas and v wei and output area mem[out..(out+outsize)) returning 0 on error (eg. out of gas) and 1 on success

callcode(g, a, v, in, insize, out, outsize)

 

F

identical to call but only use the code from a and stay in the context of the current contract otherwise

delegatecall(g, a, in, insize, out, outsize)

 

H

identical to callcode but also keep caller and callvalue

staticcall(g, a, in, insize, out, outsize)

 

B

identical to call(g, a, 0, in, insize, out, outsize) but do not allow state modifications

return(p, s)

-

F

end execution, return data mem[p..(p+s))

revert(p, s)

-

B

end execution, revert state changes, return data mem[p..(p+s))

selfdestruct(a)

-

F

end execution, destroy current contract and send funds to a

invalid

-

F

end execution with invalid instruction

log0(p, s)

-

F

log without topics and data mem[p..(p+s))

log1(p, s, t1)

-

F

log with topic t1 and data mem[p..(p+s))

log2(p, s, t1, t2)

-

F

log with topics t1, t2 and data mem[p..(p+s))

log3(p, s, t1, t2, t3)

-

F

log with topics t1, t2, t3 and data mem[p..(p+s))

log4(p, s, t1, t2, t3, t4)

-

F

log with topics t1, t2, t3, t4 and data mem[p..(p+s))

origin

 

F

transaction sender

gasprice

 

F

gas price of the transaction

blockhash(b)

 

F

hash of block nr b - only for last 256 blocks excluding current

coinbase

 

F

current mining beneficiary

timestamp

 

F

timestamp of the current block in seconds since the epoch

number

 

F

current block number

difficulty

 

F

difficulty of the current block

gaslimit

 

F

block gas limit of the current block

 

storage、memory和stack操作的gas花費對比:

指令

對應宏定義

消耗gas數量

創建合約

TxGasContractCreation

53000

創建新賬戶(對方地址不存在)

CallNewAccountGas

25000

SSTORE

SstoreSetGas

20000

MSTORE

MemoryGas

3*N(N爲有多少個32字節)

PUSH(1…N)

GasFastestStep

3

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章