時間:2018/09/04
Remix調試器
Remix帶有一個非常強大的
Debugger
,當我的調試器寫到一半的時候,才發現了Remix自帶調試器的強大之處,本文首先,對Remix的調試器進行介紹。
能調試的範圍:
1. 在Remix上進行每一個操作(創建合約/調用合約/獲取變量值)時,在執行成功後,都能在下方的控制界面點擊
DEBUG
按鈕進行調試
2. Debugger能對任意交易進行調試,只需要在調試窗口輸入對應交易地址
3. 能對公鏈,測試鏈,私鏈上的任意交易進行調試
點擊
Environment
可以對區塊鏈環境進行設置,選擇
Injected Web3
,環境取決去瀏覽器安裝的插件
比如我,使用的瀏覽器是
Chrome
,安裝的插件是
MetaMask
通過
MetaMask
插件,我能選擇環境爲公鏈或者是測試鏈,或者是私鏈
當
Environment
設置爲
Web3 Provider
可以自行添加以太坊區塊鏈的RPC節點,一般是用於設置環境爲私鏈
4. 在JavaScript的EVM環境中進行調試
見3中的圖,把
Environment
設置爲
JavaScript VM
則表示使用本地虛擬環境進行調試測試
在調試的過程中能做什麼?
Remix的調試器只提供了詳細的數據查看功能,沒法在特定的指令對
STACK/MEM/STORAGE
進行操作
在瞭解清楚Remix的調試器的功能後,感覺我進行了一半的工作好像是在重複造輪子。
之後仔細思考了我寫調試器的初衷,今天的WCTF有一道以太坊智能合約的題目,因爲第一次認真的逆向EVM的OPCODE,不熟練,一個下午還差一個函數沒有逆向出來,然後比賽結束了,感覺有點遺憾,如果當時能動態調試,可能逆向的速度能更快。
Remix的調試器只能對已經發生的行爲(交易)進行調試,所以並不能滿足我打CTF的需求,所以對於我寫的調試器,我轉換了一下定位:調試沒有源碼,只有OPCODE的智能合約的邏輯,或者可以稱爲離線調試。
調試器的編寫
智能合約調試器的編寫,我認爲最核心的部分是實現一個OPCODE解釋器,或者說是自己實現一個EVM。
實現OPCODE解釋器又分爲兩部分,1. 設計和實現數據儲存器(把STACK/MEM/STORAGE統稱爲數據儲存器),2. 解析OPCODE指令
數據儲存器
STACK
根據OPCODE指令的情況,EVM的棧和計算機的棧數據結構是一個樣的,先入先出,都有
PUSH
和
POP
操作。不過EVM的棧還多了
SWAP
和
DUP
操作,棧交換和棧複製,如下所示,是我使用
Python
實現的EVM棧類:
<span class="token keyword">class</span> STACK<span class="token punctuation">(</span>Base<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
evm stack
<span class="token string">""</span>"
stack<span class="token punctuation">:</span> <span class="token punctuation">[</span>int<span class="token punctuation">]</span>
max_value<span class="token punctuation">:</span> int
<span class="token keyword">def</span> __init__<span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
self<span class="token punctuation">.</span>stack <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
self<span class="token punctuation">.</span>max_value <span class="token operator">=</span> <span class="token number">2</span><span class="token operator">*</span><span class="token operator">*</span><span class="token number">256</span>
<span class="token keyword">def</span> push<span class="token punctuation">(</span>self<span class="token punctuation">,</span> data<span class="token punctuation">:</span> int<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
OPCODE<span class="token punctuation">:</span> PUSH
<span class="token string">""</span>"
self<span class="token punctuation">.</span>stack<span class="token punctuation">.</span>append<span class="token punctuation">(</span>data <span class="token operator">%</span> self<span class="token punctuation">.</span>max_value<span class="token punctuation">)</span>
<span class="token keyword">def</span> pop<span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span>> <span class="token punctuation">(</span>int<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
OPCODE POP
<span class="token string">""</span>"
<span class="token keyword">return</span> self<span class="token punctuation">.</span>stack<span class="token punctuation">.</span>pop<span class="token punctuation">(</span><span class="token punctuation">)</span>
@Base<span class="token punctuation">.</span>stackcheck
<span class="token keyword">def</span> swap<span class="token punctuation">(</span>self<span class="token punctuation">,</span> n<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
OPCODE<span class="token punctuation">:</span> SWAPn<span class="token punctuation">(</span><span class="token number">1</span><span class="token operator">-</span><span class="token number">16</span><span class="token punctuation">)</span>
<span class="token string">""</span>"
tmp <span class="token operator">=</span> self<span class="token punctuation">.</span>stack<span class="token punctuation">[</span><span class="token operator">-</span>n<span class="token number">-1</span><span class="token punctuation">]</span>
self<span class="token punctuation">.</span>stack<span class="token punctuation">[</span><span class="token operator">-</span>n<span class="token number">-1</span><span class="token punctuation">]</span> <span class="token operator">=</span> self<span class="token punctuation">.</span>stack<span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span>
self<span class="token punctuation">.</span>stack<span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> tmp
@Base<span class="token punctuation">.</span>stackcheck
<span class="token keyword">def</span> dup<span class="token punctuation">(</span>self<span class="token punctuation">,</span> n<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
OPCODE<span class="token punctuation">:</span> DUPn<span class="token punctuation">(</span><span class="token number">1</span><span class="token operator">-</span><span class="token number">16</span><span class="token punctuation">)</span>
<span class="token string">""</span>"
self<span class="token punctuation">.</span>stack<span class="token punctuation">.</span>append<span class="token punctuation">(</span>self<span class="token punctuation">.</span>stack<span class="token punctuation">[</span><span class="token operator">-</span>n<span class="token punctuation">]</span><span class="token punctuation">)</span>
和計算機的棧比較,我覺得EVM的棧結構更像Python的List結構
計算機的棧是一個地址儲存一個字節的數據,取值可以精確到一個字節,而EVM的棧是分塊儲存,每次PUSH佔用一塊,每次POP取出一塊,每塊最大能儲存32字節的數據,也就是
2^256-1
,所以上述代碼中,對每一個存入棧中的數據進行取餘計算,保證棧中的數據小於
2^256-1
MEM
EVM的內存的數據結構幾乎和計算機內存的一樣,一個地址儲存一字節的數據。在EVM中,因爲棧的結構,每塊儲存的數據最大爲
256bits
,所以當OPCODE指令需要的參數長度可以大於
256bits
時,將會使用到內存
如下所示,是我使用
Python
實現的MEM內存類:
<span class="token keyword">class</span> MEM<span class="token punctuation">(</span>Base<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
EVM memory
<span class="token string">""</span>"
mem<span class="token punctuation">:</span> bytearray
max_value<span class="token punctuation">:</span> int
length<span class="token punctuation">:</span> int
<span class="token keyword">def</span> __init__<span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
self<span class="token punctuation">.</span>mem <span class="token operator">=</span> bytearray<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
self<span class="token punctuation">.</span>max_value <span class="token operator">=</span> <span class="token number">2</span><span class="token operator">*</span><span class="token operator">*</span><span class="token number">256</span>
self<span class="token punctuation">.</span>length <span class="token operator">=</span> <span class="token number">0</span>
self<span class="token punctuation">.</span>extend<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
@Base<span class="token punctuation">.</span>memcheck
<span class="token keyword">def</span> set<span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">:</span> int<span class="token punctuation">,</span> value<span class="token punctuation">:</span> int<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
OPCODE<span class="token punctuation">:</span> MSTORE
<span class="token string">""</span>"
value <span class="token operator">%</span><span class="token operator">=</span> self<span class="token punctuation">.</span>max
self<span class="token punctuation">.</span>mem<span class="token punctuation">[</span>key<span class="token punctuation">:</span> key<span class="token operator">+</span><span class="token number">0x20</span><span class="token punctuation">]</span> <span class="token operator">=</span> value<span class="token punctuation">.</span>to_bytes<span class="token punctuation">(</span><span class="token number">0x20</span><span class="token punctuation">,</span> <span class="token string">"big"</span><span class="token punctuation">)</span>
self<span class="token punctuation">.</span>length <span class="token operator">+</span><span class="token operator">=</span> <span class="token number">0x20</span>
@Base<span class="token punctuation">.</span>memcheck
<span class="token keyword">def</span> set_byte<span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">:</span> int<span class="token punctuation">,</span> value<span class="token punctuation">:</span> int<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
OPCODE<span class="token punctuation">:</span> MSTORE8
<span class="token string">""</span>"
self<span class="token punctuation">.</span>mem<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> value <span class="token operator">&</span>amp<span class="token punctuation">;</span> <span class="token number">0xff</span>
self<span class="token punctuation">.</span>length <span class="token operator">+</span><span class="token operator">=</span> length
@Base<span class="token punctuation">.</span>memcheck
<span class="token keyword">def</span> set_length<span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">:</span> int<span class="token punctuation">,</span> value<span class="token punctuation">:</span> int<span class="token punctuation">,</span> length<span class="token punctuation">:</span> int<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
OPCODE<span class="token punctuation">:</span> XXXXCOPY
<span class="token string">""</span>"
value <span class="token operator">%</span><span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">2</span><span class="token operator">*</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token number">8</span><span class="token operator">*</span>length<span class="token punctuation">)</span><span class="token punctuation">)</span>
data <span class="token operator">=</span> value<span class="token punctuation">.</span>to_bytes<span class="token punctuation">(</span>length<span class="token punctuation">,</span> <span class="token string">"big"</span><span class="token punctuation">)</span>
self<span class="token punctuation">.</span>mem<span class="token punctuation">[</span>key<span class="token punctuation">:</span> key<span class="token operator">+</span>length<span class="token punctuation">]</span> <span class="token operator">=</span> data
self<span class="token punctuation">.</span>length <span class="token operator">+</span><span class="token operator">=</span> length
@Base<span class="token punctuation">.</span>memcheck
<span class="token keyword">def</span> get<span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span>> <span class="token punctuation">(</span>int<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
OPCODE<span class="token punctuation">:</span> MLOAD
<span class="token keyword">return</span> uint256
<span class="token string">""</span>"
<span class="token keyword">return</span> int<span class="token punctuation">.</span>from_bytes<span class="token punctuation">(</span>self<span class="token punctuation">.</span>mem<span class="token punctuation">[</span>key<span class="token punctuation">:</span> key<span class="token operator">+</span><span class="token number">0x20</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string">"big"</span><span class="token punctuation">,</span> signed<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span>
@Base<span class="token punctuation">.</span>memcheck
<span class="token keyword">def</span> get_bytearray<span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span>> <span class="token punctuation">(</span>bytearray<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
OPCODE<span class="token punctuation">:</span> MLOAD
<span class="token keyword">return</span> <span class="token number">32</span> byte array
<span class="token string">""</span>"
<span class="token keyword">return</span> self<span class="token punctuation">.</span>mem<span class="token punctuation">[</span>key<span class="token punctuation">:</span> key<span class="token operator">+</span><span class="token number">0x20</span><span class="token punctuation">]</span>
@Base<span class="token punctuation">.</span>memcheck
<span class="token keyword">def</span> get_bytes<span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span>> <span class="token punctuation">(</span>bytes<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
OPCODE<span class="token punctuation">:</span> MLOAD
<span class="token keyword">return</span> <span class="token number">32</span> bytes
<span class="token string">""</span>"
<span class="token keyword">return</span> bytes<span class="token punctuation">(</span>self<span class="token punctuation">.</span>mem<span class="token punctuation">[</span>key<span class="token punctuation">:</span> key<span class="token operator">+</span><span class="token number">0x20</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
@Base<span class="token punctuation">.</span>memcheck
<span class="token keyword">def</span> get_length<span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">:</span>int <span class="token punctuation">,</span> length<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span>> <span class="token punctuation">(</span>int<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
<span class="token keyword">return</span> mem int value
<span class="token string">""</span>"
<span class="token keyword">return</span> int<span class="token punctuation">.</span>from_bytes<span class="token punctuation">(</span>self<span class="token punctuation">.</span>mem<span class="token punctuation">[</span>key<span class="token punctuation">:</span> key<span class="token operator">+</span>length<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string">"big"</span><span class="token punctuation">,</span> signed<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span>
@Base<span class="token punctuation">.</span>memcheck
<span class="token keyword">def</span> get_length_bytes<span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">:</span>int <span class="token punctuation">,</span> length<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span>> <span class="token punctuation">(</span>bytes<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
<span class="token keyword">return</span> mem bytes value
<span class="token string">""</span>"
<span class="token keyword">return</span> bytes<span class="token punctuation">(</span>self<span class="token punctuation">.</span>mem<span class="token punctuation">[</span>key<span class="token punctuation">:</span> key<span class="token operator">+</span>length<span class="token punctuation">]</span><span class="token punctuation">)</span>
@Base<span class="token punctuation">.</span>memcheck
<span class="token keyword">def</span> get_length_bytearray<span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">:</span>int <span class="token punctuation">,</span> length<span class="token punctuation">:</span> int<span class="token punctuation">)</span> <span class="token operator">-</span>> <span class="token punctuation">(</span>bytearray<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
<span class="token keyword">return</span> mem int value
<span class="token string">""</span>"
<span class="token keyword">return</span> self<span class="token punctuation">.</span>mem<span class="token punctuation">[</span>key<span class="token punctuation">:</span> key<span class="token operator">+</span>length<span class="token punctuation">]</span>
<span class="token keyword">def</span> extend<span class="token punctuation">(</span>self<span class="token punctuation">,</span> num<span class="token punctuation">:</span> int<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
extend mem space
<span class="token string">""</span>"
self<span class="token punctuation">.</span>mem<span class="token punctuation">.</span>extend<span class="token punctuation">(</span>bytearray<span class="token punctuation">(</span><span class="token number">256</span><span class="token operator">*</span>num<span class="token punctuation">)</span><span class="token punctuation">)</span>
使用python3中的
bytearray
類型作爲MEM的結構,默認初始化256B的內存空間,因爲有一個OPCODE是
MSIZE
:
Get the size of active memory in bytes.
所以每次設置內存值時,都要計算
active memory
的size
內存相關設置的指令分爲三類
- MSTORE, 儲存0x20字節長度的數據到內存中
- MSTORE8, 儲存1字節長度的數據到內存中
- CALLDATACOPY(或者其他類似指令),儲存指定字節長度的數據到內存中
所以對應的設置了3個不同的儲存數據到內存中的函數。獲取內存數據的類似。
STORAGE
EVM的STORAGE的數據結構和計算機的磁盤儲存結構相差就很大了,STORAGE是用來儲存全局變量的,全局變量的數據結構我在上一篇文章中分析過,所以在用Python實現中,我把STORAGE定義爲了字典,相關代碼如下:
<span class="token keyword">class</span> STORAGE<span class="token punctuation">(</span>Base<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
EVM storage
<span class="token string">""</span>"
storage<span class="token punctuation">:</span> <span class="token punctuation">{</span>str<span class="token punctuation">:</span> int<span class="token punctuation">}</span>
max<span class="token punctuation">:</span> int
<span class="token keyword">def</span> __init__<span class="token punctuation">(</span>self<span class="token punctuation">,</span> data<span class="token punctuation">)</span><span class="token punctuation">:</span>
self<span class="token punctuation">.</span>storage <span class="token operator">=</span> data
self<span class="token punctuation">.</span>max <span class="token operator">=</span> <span class="token number">2</span><span class="token operator">*</span><span class="token operator">*</span><span class="token number">256</span>
@Base<span class="token punctuation">.</span>storagecheck
<span class="token keyword">def</span> set<span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">:</span> str<span class="token punctuation">,</span> value<span class="token punctuation">:</span> int<span class="token punctuation">)</span><span class="token punctuation">:</span>
self<span class="token punctuation">.</span>storage<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> value <span class="token operator">%</span> self<span class="token punctuation">.</span>max
@Base<span class="token punctuation">.</span>storagecheck
<span class="token keyword">def</span> get<span class="token punctuation">(</span>self<span class="token punctuation">,</span> key<span class="token punctuation">:</span> str<span class="token punctuation">)</span> <span class="token operator">-</span>> <span class="token punctuation">(</span>int<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token keyword">return</span> self<span class="token punctuation">.</span>storage<span class="token punctuation">[</span>key<span class="token punctuation">]</span>
因爲EVM中操作STORAGE的相關指令只有
SSTORE
和
SLOAD
,所以使用python的dict類型作爲STORAGE的結構最爲合適
解析OPCODE指令
對於OPCODE指令的解析難度不是很大,指令只佔一個字節,所以EVM的指令最多也就256個指令(
0x00-0xff
),但是有很多都是處於
UNUSE
,所以以後智能合約增加新指令後,調試器也要進行更新,因此現在寫的代碼需要具備可擴展性。雖然解析指令的難度不大,但是仍然是個體力活,下面先來看看OPCODE的分類
OPCODE分類
在以太坊官方黃皮書中,對OPCODE進行了相應的分類:
0s: Stop and Arithmetic Operations (從0x00-0x0f的指令類型是STOP指令加上算術指令)
10s: Comparison & Bitwise Logic Operations (0x10-0x1f的指令是比較指令和比特位邏輯指令)
20s: SHA3 (目前0x20-0x2f只有一個SHA3指令)
30s: Environmental Information (0x30-0x3f是獲取環境信息的指令)
40s: Block Information (0x40-0x4f是獲取區塊信息的指令)
50s: Stack, Memory, Storage and Flow Operations (0x40-0x4f是獲取棧、內存、儲存信息的指令和流指令(跳轉指令))
60s & 70s: Push Operations (0x60-0x7f是32個PUSH指令,PUSH1-PUSH32)
80s: Duplication Operations (0x80-0x8f屬於DUP1-DUP16指令)
90s: Exchange Operations (0x90-0x9f屬於SWAP1-SWAP16指令)
a0s: Logging Operations (0xa0-0xa4屬於LOG0-LOG4指令)
f0s: System operations (0xf0-0xff屬於系統操作指令)
設計可擴展的解釋器
首先,設計一個字節和指令的映射表:
import typing
class OpCode(typing.NamedTuple):
name: str
removed: int # 參數個數
args: int # PUSH根據該參數獲取opcode之後args字節的值作爲PUSH的參數
_OPCODES = {
'00': OpCode(name = 'STOP', removed = 0, args = 0),
......
}
for i in range(96, 128):
_OPCODES[hex(i)[2:]] = OpCode(name='PUSH' + str(i - 95), removed=0, args=i-95)
......
# 因爲編譯器優化的問題,OPCODE中會出現許多執行不到的,UNUSE的指令,爲防止解析失敗,還要對UNUSE的進行處理
for i in range(0, 256):
if not _OPCODES.get(hex(i)[2:].zfill(2)):
_OPCODES[hex(i)[2:].zfill(2)] = OpCode('UNUSE', 0, 0)
然後就是設計一個解釋器類:
<span class="token keyword">class</span> Interpreter<span class="token punctuation">:</span>
<span class="token string">""</span>"
EVM Interpreter
<span class="token string">""</span>"
MAX <span class="token operator">=</span> <span class="token number">2</span><span class="token operator">*</span><span class="token operator">*</span><span class="token number">256</span>
over <span class="token operator">=</span> <span class="token number">1</span>
store<span class="token punctuation">:</span> EVMIO
<span class="token comment">#############
</span> <span class="token comment"># 0s: Stop and Arithmetic Operations
</span> <span class="token comment">#############
</span> @staticmethod
<span class="token keyword">def</span> STOP<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
OPCODE<span class="token punctuation">:</span> <span class="token number">0x00</span>
<span class="token string">""</span>"
Interpreter<span class="token punctuation">.</span>over <span class="token operator">=</span> <span class="token number">1</span>
<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"========Program STOP========="</span><span class="token punctuation">)</span>
@staticmethod
<span class="token keyword">def</span> ADD<span class="token punctuation">(</span>x<span class="token punctuation">:</span>int<span class="token punctuation">,</span> y<span class="token punctuation">:</span>int<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token string">""</span>"
OPCODE<span class="token punctuation">:</span> <span class="token number">0x01</span>
<span class="token string">""</span>"
r <span class="token operator">=</span> <span class="token punctuation">(</span>x <span class="token operator">+</span> y<span class="token punctuation">)</span> <span class="token operator">%</span> Interpreter<span class="token punctuation">.</span>MAX
Interpreter<span class="token punctuation">.</span>store<span class="token punctuation">.</span>stack<span class="token punctuation">.</span>push<span class="token punctuation">(</span>r<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
- MAX變量用來控制計算的結果在256bits的範圍內
- over變量用來標識程序是否執行結束
- store用來訪問runtime變量: STACK, MEM, STORAGE
在這種設計模式下,當解釋響應的OPCODE,可以直接使用
args = [stack.pop() for _ in OpCode.removed]
getattr(Interpreter, OpCode.name)(*args)
特殊指令的處理思路
在OPCODE中有幾類特殊的指令:
1. 獲取區塊信息的指令,比如:
NUMBER: Get the block’s number
該指令是獲取當前交易打包進的區塊的區塊數(區塊高度),解決這個指令有幾種方案:
- 設置默認值
- 設置一個配置文件,在配置文件中設置該指令的返回值
- 調試者手動利用調試器設置該值
- 設置RPC地址,從區塊鏈中獲取該值
文章的開頭提過了對我編寫的調試器的定位問題,也正是因爲遇到該類的指令,纔去思考調試器的定位。既然已經打包進了區塊,說明是有交易地址的,既然有交易地址,那完全可以使用Remix的調試器進行調試。
所以對我編寫的調試器有了離線調試器的定位,採用上述方法中的前三個方法,優先級由高到低分別是,手動設置>配置文件設置>默認設置
2. 獲取環境信息指令,比如:
ADDRESS: Get address of currently executing account.
獲取當前合約的地址,解決方案如下:
- 設置默認值
- 設置一個配置文件,在配置文件中設置該指令的返回值
- 調試者手動利用調試器設置該值
獲取環境信息的指令,因爲調試的是OPCODE,沒有源碼,不需要部署,所以是沒法通過RPC獲取到的,只能由調試者手動設置
3. 日誌指令
LOG0-LOG4: Append log record with no topics.
把日誌信息添加到交易的回執單中
> eth.getTransactionReceipt("0xe32b3751a3016e6fa5644e59cd3b5072f33f27f10242c74980409b637dbb3bdc")
{
blockHash: "0x04b838576b0c3e44ece7279b3b709e336a58be5786a83a6cf27b4173ce317ad3",
blockNumber: 6068600,
contractAddress: null,
cumulativeGasUsed: 7171992,
from: "0x915d631d71efb2b20ad1773728f12f76eeeeee23",
gasUsed: 81100,
logs: [],
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
status: "0x1",
to: "0xd1ceeeefa68a6af0a5f6046132d986066c7f9426",
transactionHash: "0xe32b3751a3016e6fa5644e59cd3b5072f33f27f10242c74980409b637dbb3bdc",
transactionIndex: 150
}
上述就是獲取一個交易的回執單,其中有一個
logs
列表,就是用來儲存日誌信息
既然是在調試OPCODE,那麼記錄日誌的操作就是沒有必要的,因爲調試的過程中能看到儲存器/參數的情況,所以對於這類指令的操作,完全可以直接輸出,或者不做任何處理(直接pass)
4. 系統操作指令
這類指令主要是外部調用相關的指令,比如可以創建合約的
CREATE
, 比如能調用其他合約的
CALL
, 比如銷燬自身,並把餘額全部轉給別人的
SELFDESTRUCT
這類的指令我認爲的解決辦法只有: 調試者手動利用調試器設置該指令的返回值
調用這類函數的時候,我們完全能看到詳細的參數值,所以完全可以手動的進行創建合約,調用合約等操作
總結
在完成一個OPCODE的解釋器後,一個調試器就算完成了
3/4
, 剩下的工作就是實現自己想實現的調試器功能,比如下斷點,查看棧內存儲存數據等
下面放一個接近成品的演示gif圖:
智能合約審計服務
針對目前主流的以太坊應用,知道創宇提供專業權威的智能合約審計服務,規避因合約安全問題導致的財產損失,爲各類以太坊應用安全保駕護航。
知道創宇404智能合約安全審計團隊: https://www.scanv.com/lca/index.html
聯繫電話:(086) 136 8133 5016(沈經理,工作日:10:00-18:00)
歡迎掃碼諮詢:
區塊鏈行業安全解決方案
黑客通過DDoS攻擊、CC攻擊、系統漏洞、代碼漏洞、業務流程漏洞、API-Key漏洞等進行攻擊和入侵,給區塊鏈項目的管理運營團隊及用戶造成巨大的經濟損失。知道創宇十餘年安全經驗,憑藉多重防護+雲端大數據技術,爲區塊鏈應用提供專屬安全解決方案。
歡迎掃碼諮詢:
本文由 Seebug Paper 發佈,如需轉載請註明來源。本文地址: https://paper.seebug.org/693/