嘗試拆解Solidity編譯後的bytecode(一)

ray
18 min readOct 14, 2022

--

以下範例皆參考openzeppelin opcode的系列文章(共六篇)撰寫。

本篇文章建議有solidity開發經驗的讀者閱讀,初學者不適合本篇文章。本文與Solidity opcode基礎有相關性,建議閱讀後再讀本篇文章。

本文目的不在教會讀者寫opcode(作者也沒厲害到這個程度XD),而是要帶領讀者了解solidity bytecode的基本架構!本文所述,皆參考openzeppelin opcode的系列文章,及使用remix debug工具「觀察」bytecode執行時「memory和stack的變化」所寫出。因此不保證涵蓋能解讀「完整」的solidity bytecode架構,這也是本文命名為,嘗試拆解Solidity編譯後。

範例程式使用「solidity v0.4.24」,對齊openzeppelin範例的版本,讀者操作時,請注意版本號。另外https://www.evm.codes/?fork=grayGlacier的opcode使用solidity最新版本編譯,使用該網站轉譯bytecode成opcode,「執行結果」與openzeppelin opcode系列文章會「略有不同」

佈署時constructor的_initialSupply請帶「10000」!!!

將範例合約佈署至測試鏈後,可以拿到以下bytecode。(建議實際佈署到測試鏈體驗)

可參閱作者佈署至goerli測試鏈的合約:https://goerli.etherscan.io/address/0xd048f37a6fea76560907cf4234df58bf5ff10765#code

  1. verify前:
// runtime code608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806318160ddd1461005c57806370a0823114610087578063a9059cbb146100de575b600080fd5b34801561006857600080fd5b50610071610143565b6040518082815260200191505060405180910390f35b34801561009357600080fd5b506100c8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061014c565b6040518082815260200191505060405180910390f35b3480156100ea57600080fd5b50610129600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610195565b604051808215151515815260200191505060405180910390f35b60008054905090565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141515156101d257600080fd5b600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054821115151561022057600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205403600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205401600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060019050929150505600a165627a7a72305820ff03d0d0bf3e0cec0938afdb696e0bd258ee5a360172bb63c2f7548f624a0f650029

2. verify後:

// creation code + runtime code + constructor arguments608060405234801561001057600080fd5b506040516020806103ee833981018060405281019080805190602001909291905050508060008190555080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550506103608061008e6000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806318160ddd1461005c57806370a0823114610087578063a9059cbb146100de575b600080fd5b34801561006857600080fd5b50610071610143565b6040518082815260200191505060405180910390f35b34801561009357600080fd5b506100c8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061014c565b6040518082815260200191505060405180910390f35b3480156100ea57600080fd5b50610129600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610195565b604051808215151515815260200191505060405180910390f35b60008054905090565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141515156101d257600080fd5b600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054821115151561022057600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205403600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205401600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060019050929150505600a165627a7a72305820ff03d0d0bf3e0cec0938afdb696e0bd258ee5a360172bb63c2f7548f624a0f6500290000000000000000000000000000000000000000000000000000000000002710

3. 利用remix查閱creation code + runtime code:(object為bytecode,可直接轉譯為opcodes。opcodes是PUSH1 0x80 PUSH1 0x40…以下省略)

// creation code + runtime code608060405234801561001057600080fd5b506040516020806103ee833981018060405281019080805190602001909291905050508060008190555080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550506103608061008e6000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806318160ddd1461005c57806370a0823114610087578063a9059cbb146100de575b600080fd5b34801561006857600080fd5b50610071610143565b6040518082815260200191505060405180910390f35b34801561009357600080fd5b506100c8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061014c565b6040518082815260200191505060405180910390f35b3480156100ea57600080fd5b50610129600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610195565b604051808215151515815260200191505060405180910390f35b60008054905090565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141515156101d257600080fd5b600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054821115151561022057600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205403600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205401600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060019050929150505600a165627a7a72305820ff03d0d0bf3e0cec0938afdb696e0bd258ee5a360172bb63c2f7548f624a0f650029

4. evm codes網站可將bytecode直接轉為opcode,以便追蹤opcode(請複製creation code + runtime code將其轉換為可閱讀之opcode)

5. 接續第4點,按下run及右上角的播放鍵後,可以在memory得到一組bytecode。查閱過後(用ctrl + f查閱),可以發現該組btyecode是runtime code

前置作業完成,讓我們正式開始拆解!

Solidity的bytecode可以拆成兩個大項目,creation code和runtime code。

  • creation code:代表constructor的程式碼
  • runtime code:代表非constructor的程式碼,也就是developer寫的各個function,最終會留在以太鏈的bytecode(程式碼)

拆解一份bytecode,要先找到程式的終止位置及
CODECOPY opcode。終止程式的opcode有stop、return、revert、selfdestruct(忘記可以回去複習Solidity opcode基礎)。CODECOPY opcode的用途是將指定區間的opcode複製到memory。

綜上所述,讓我們回到前置作業第5點的圖片,同步尋找codecopy和終止程式的opcode。我們發現89的位置是codecopy,8c的位置是return。若我們用step into一步步執行opcode,會發現執行到8c程式就把8e後的程式直接return了!(如下圖)

用remix debug工具觀察codecopy吃的參數

現在統整一下剛剛的操作。

  1. 我們執行了creation code + runtime code。注意,程式的第1部份是creation code,第2部份是runtime code。「程式從creation code開始執行,也就是從constructor開始執行!」
  2. creation code佔據142bytes,142的16進制為8d
  3. codecopy從8e開始複製opcode到最後一個bytecode
  4. codecopy執行過到8c(return opcode)直接return記憶體的資料回鏈上,終止程式。「記憶體內的資料正是runtime code!」

至此我們已經知曉一份合約是如何被初始化,及佈署至以太坊上囉!~^^

--

--