以太坊 Input Data 解析
前段時間,Poly Network 被盜事件的一個小插曲,一地址向黑客地址轉账在 input data 中告知其 USDT 已被凍結,不要使用 USDT,黑客知曉後向該地址轉账 13.37 ETH。
事後很多人便通過 input Data 在區塊鏈上“聊天”向黑客“索要”虛擬貨幣,那么我們經常在區塊鏈瀏覽器中看到的 input Data 到底是什么?知道創宇區塊鏈安全實驗室 為您解答。
input data
在以太坊協議中,當交易(transaction)為合約創建時,input data 是账戶初始化程序的 EVM 代碼;
而當交易(transaction)為消息調用時,input data 是合約函數調用數據。
正常情況下簡單的消息調用如調用轉账函數時需要填寫你要轉账的地址 _to 和你要轉账的數量 _amount,這些基本信息都包含在 input data 裏面。
我們通過一個調用合約的轉账交易具體分析,來理解消息調用時 input data 的結構。
解析形式:
原始形式:
我們將原始的 input data 分為三個部分進行分析:
0xa9059cbb
:函數標識符000000000000000000000000345d8e3a1f62ee6b1d483890976fd66168e390f2
: 第一個參數為 address 即你要轉账的地址,並補位到 32 字節即 64 個 16 進制字符0000000000000000000000000000000000000000000054b7d8ed70650b290000
: 第二個參數為 value 即你要轉账的數量,並補位到 32 字節即 64 個 16 進制字符
通過對比分析我們可以發現 input data 的基本結構為函數標識符
+參數
函數標識符
這裏的函數標識符即為函數選擇器,根據官方文檔可知函數選擇器是某個函數籤名的 Keccak(SHA-3)哈希的前 4 字節(高位在左的大端序)。
我們可以通過代碼
bytess4(keccake256("transfer(adddress,uint256)"))
或者在线工具獲取這種函數籤名。
下圖可以看出加密結果的前四個字節 (a9059cbb) 跟 input data 中函數標識符一致。
這裏之所以要將函數籤名截斷到四個字節是考慮到 Gas 成本問題。
在一筆交易中0字節需要支付 4 gas,而非0字節需要 68 gas 也就是 0 字節的 17 倍。在 SHA-3 加密中生成的 32 字節隨機字符串更傾向於多的非 0 字節,所以大概成本是32x68=2176 gas,而截斷成本大概為 4x68=272 gas,可見截斷到四個字節能夠節省約 8 倍的 gas 費。
而函數標識符的作用是指定調用哪一個函數,在同一個合約中兩個不同函數的 SHA-3 籤名的前 4 字節相同的概率是十分小的,所以截斷到四個字節實際不會影響函數調用。
參 數
在 evm 執行字節碼的約定中,靜態類型左補齊零至 64 長度,而動態類型則是右補齊零至 64 長度。
歸納下常見的靜態類型:uint,bool,Address,bytes[0-32], 動態數組類型:bytes,string,address[],bytes32[].....
我們通過 pyethereum的ABI編碼函數 來研究不同數據類型的編碼方式。
靜態類型
先導入 encode_abi 函數
import rlp
from ethereum.abi import encode_abi
我們以函數 transfer(address,uint 256) 為例
> encode_abi(["address", "uint256"],[345d8e3a1f62ee6b1d483890976fd66168e390f2,1]).hex()
000000000000000000000000345d8e3a1f62ee6b1d483890976fd66168e390f2
0000000000000000000000000000000000000000000000000000000000000001
對於小於 32 字節的定長數組會被自動填充到 32 字節:
> encode_abi(["int8[3]"],[[1, 2, 3]).hex()
// 自動填充 0
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000003
動態類型
動態類型編碼要稍微復雜一些,需要先計算偏移量進行佔位處理,我們通過一個簡單的例子來具體說明。
> encode_abi(
["uint256[]", "uint256[]", "uint256[]"],
[[0xa1, 0xa2, 0xa3], [0xb1, 0xb2, 0xb3], [0xc1, 0xc2, 0xc3]]
).hex()
// 參數 1 的偏移量:32*3=96 十六進制 0x60
0000000000000000000000000000000000000000000000000000000000000060
// 參數2的偏移量=參數 1 偏移量+參數 1 數據部分長度=96+32*4=224 十六進制0xE0
00000000000000000000000000000000000000000000000000000000000000e0
// 參數3的偏移量=參數 2 偏移量+參數 2 數據部分長度=224+32*4=352 十六進制0x160
0000000000000000000000000000000000000000000000000000000000000160
// 偏移量 0x60 位置开始傳入參數 1 的數據
0000000000000000000000000000000000000000000000000000000000000003//元素個數
00000000000000000000000000000000000000000000000000000000000000a1//第一個數組元素
00000000000000000000000000000000000000000000000000000000000000a2//第二個數組元素
00000000000000000000000000000000000000000000000000000000000000a3//第三個數組元素
// 0xe0位置。參數 2 的數據
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000b1
00000000000000000000000000000000000000000000000000000000000000b2
00000000000000000000000000000000000000000000000000000000000000b3
//0x160 位置。參數 3 的數據
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000c1
00000000000000000000000000000000000000000000000000000000000000c2
00000000000000000000000000000000000000000000000000000000000000c3
短地址攻擊
經過前面的分析當靜態類型如 address 長度不足 32 字節時 EVM 會根據規則將長度補齊到 32 字節,如果當轉账的地址以00結尾,如0x641988625108585185752230bde001b3ebd0fc00
,轉账時將地址後面的兩個零去掉,EVM 依然會認為 address_to
是 32 位的,所以它會從_value
的高位取 0 來補充,amount的位數會多兩位也就是會乘以256。
攻擊過程如下:
將惡意轉账地址最後一個字節的 0 去掉
函數標識符:a9059cbb
轉账地址:
000000000000000000000000641988625108585185752230bde001b3ebd0fc
轉账金額:
00000000000000000000000000000000000000000000000000000000000000001
由於 EVM 的補位規則,解析結果為:
0xa9059cbb000000000000000000000000641988625108585185752230bde001b3ebd0fc0000000000000000000000000000000000000000000000000000000000000000100
我們分解後發現,轉账金額已經多了兩位也就是多了一個字節,即為原來轉账的 256倍
函數標識符:a9059cbb
轉账地址:
000000000000000000000000641988625108585185752230bde001b3ebd0fc00
轉账金額:
00000000000000000000000000000000000000000000000000000000000000100
如何在 input data 附着信息
在以太坊中直接進行轉账交易的 input data 字段默認是沒有內容的,但是我們可以通過設置錢包實現文章开頭的“聊天功能”。
我們以 MetaMask 錢包為例展示如何通過轉账在 input data 字段附着一些額外的信息。
1、首先我們需要打开錢包高級選項的顯示十六進制數據开關
2、在轉账時將你要附着的信息通過十六進制編碼後填入下方十六進制數據中,記得在开頭加上 0x 然後進行轉账
3、轉账成功後在 etherscan 中就能夠看到附着信息
總結
我們能夠通過交易中的 input data 將一些信息永久存儲在區塊鏈中,可以通過此項技術在食品藥品監管部門的產品防僞溯源、財稅部門的電子票據打假驗真、學術成果存證等方面實現應用落地。
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。
7月23:Mt. Gox 比特幣錢包在市場緊縮的情況下轉移了價值 28.2 億美元的 BTC
7月23:Mt. Gox 比特幣錢包在市場緊縮的情況下轉移了價值 28.2 億美元的 BTC一個引...
悅盈:比特幣68000的空完美落地反彈繼續看跌 以太坊破前高看回撤
一個人的自律中,藏着無限的可能性,你自律的程度,決定着你人生的高度。 人生沒有近路可走,但你走的每...