教你用ETH進行聊天
前言
二級標題
前言二級標題知道創宇區塊鏈安全實驗室
Input data
前段時間,Poly Network 被盜事件的一個小插曲,一地址向黑客地址轉賬在input data 中告知其USDT 已被凍結,不要使用USDT,黑客知曉後向該地址轉賬13.37 ETH。
二級標題
知道創宇區塊鏈安全實驗室
二級標題
為您解答。
而當交易(transaction)為消息調用時,input data 是合約函數調用數據。
解析形式:
原始形式:
正常情況下簡單的消息調用如調用轉賬函數時需要填寫你要轉賬的地址_to 和你要轉賬的數量_amount,這些基本信息都包含在input data 裡面。
我們通過一個調用合約的轉賬交易具體分析,來理解消息調用時input data 的結構。
解析形式:
原始形式:
我們將原始的input data 分為三個部分進行分析:
0xa9059cbb:函數標識符"transfer(adddress,uint256)"000000000000000000000000345d8e3a1f62ee6b1d483890976fd66168e390f2: 第一個參數為address 即你要轉賬的地址,並補位到32 字節即64 個16 進製字符
0000000000000000000000000000000000000000000054b7d8ed70650b290000: 第二個參數為value 即你要轉賬的數量,並補位到32 字節即64 個16 進製字符
通過對比分析我們可以發現input data 的基本結構為函數標識符+參數。
函數標識符
這裡的函數標識符即為函數選擇器,根據官方文檔可知函數選擇器是某個函數簽名的Keccak(SHA-3)哈希的前4 字節(高位在左的大端序)。
我們可以通過代碼bytess4(keccake256(
)) 或者在線工具獲取這種函數簽名。
下圖可以看出加密結果的前四個字節(a9059cbb) 跟input data 中函數標識符一致。
這里之所以要將函數簽名截斷到四個字節是考慮到Gas 成本問題。
在SHA-3 加密中生成的32 字節隨機字符串更傾向於多的非0 字節,所以大概成本是32x68=2176 gas,而截斷成本大概為4x68=272 gas,可見截斷到四個字節能夠節省約8 倍的gas 費。
靜態類型
參數
import rlp
from ethereum.abi import encode_abi
靜態類型
> encode_abi(["address", "uint256"],
[345d8e3a1f62ee6b1d483890976fd66168e390f2,1]).hex()
000000000000000000000000345d8e3a1f62ee6b1d483890976fd66168e390f2
0000000000000000000000000000000000000000000000000000000000000001
歸納下常見的靜態類型:uint,bool,Address,bytes[0-32], 動態數組類型:bytes,string,address[],bytes32[].....
> encode_abi(["int8[3]"],[[1, 2, 3]).hex()
靜態類型
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000003
我們通過pyethereum的ABI編碼函數來研究不同數據類型的編碼方式。
靜態類型
> encode_abi(
["uint256[]", "uint256[]", "uint256[]"],[[0xa1, 0xa2, 0xa3], [0xb1, 0xb2, 0xb3], [0xc1, 0xc2, 0xc3]]
).hex()
先導入encode_abi 函數
我們以函數transfer(address,uint 256) 為例
對於小於32 字節的定長數組會被自動填充到32 字節:
// 自動填充 0
動態類型
動態類型編碼要稍微複雜一些,需要先計算偏移量進行佔位處理,我們通過一個簡單的例子來具體說明。
// 參數1 的偏移量:32*3=96 十六進制0x600000000000000000000000000000000000000000000000000000000000000060
// 參數2的偏移量=參數1 偏移量+參數1 數據部分長度=96+32*4=224 十六進制0xE000000000000000000000000000000000000000000000000000000000000000e0
// 參數3的偏移量=參數2 偏移量+參數2 數據部分長度=224+32*4=352 十六進制0x1600000000000000000000000000000000000000000000000000000000000000160
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000b1
00000000000000000000000000000000000000000000000000000000000000b2
00000000000000000000000000000000000000000000000000000000000000b3
// 偏移量0x60 位置開始傳入參數1 的數據
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000c1
00000000000000000000000000000000000000000000000000000000000000c2
00000000000000000000000000000000000000000000000000000000000000c3
0000000000000000000000000000000000000000000000000000000000000003//元素個
00000000000000000000000000000000000000000000000000000000000000a1//第一個數組元素
00000000000000000000000000000000000000000000000000000000000000a2//第二個數組元素
00000000000000000000000000000000000000000000000000000000000000a3//第三個數組元素
// 0xe0位置。參數2 的數據
//0x160 位置。參數3 的數據
000000000000000000000000641988625108585185752230bde001b3ebd0fc
短地址攻擊
00000000000000000000000000000000000000000000000000000000000000001
經過前面的分析當靜態類型如address長度不足32字節時EVM會根據規則將長度補齊到32字節,如果當轉賬的地址以00結尾,如0x641988625108585185752230bde001b3ebd0fc00,轉賬時將地址後面的兩個零去掉,EVM依然會認為address_to是32 位的,所以它會從_value的高位取0 來補充,amount 的位數會多兩位也就是會乘以256。
攻擊過程如下:
將惡意轉賬地址最後一個字節的0 去掉
函數標識符:a9059cbb
000000000000000000000000641988625108585185752230bde001b3ebd0fc00
轉賬金額:
00000000000000000000000000000000000000000000000000000000000000100
我們分解後發現,轉賬金額已經多了兩位也就是多了一個字節,即為原來轉賬的256倍
二級標題
轉賬地址:
二級標題
轉賬金額:
我們以MetaMask 錢包為例展示如何通過轉賬在input data 字段附著一些額外的信息。
總結
二級標題

