EVM虛擬機在解析合約的字節碼時,依賴的是ABI的定義,從而去識別各個字段位於字節碼的什麼地方。關於ABI,能夠閱讀這個文檔:前端
通常ERC-20 TOKEN標準的代幣都會實現transfer方法,這個方法在ERC-20標籤中的定義爲:function transfer(address to, uint tokens) public returns (bool success);github
第一參數是發送代幣的目的地址,第二個參數是發送token的數量。函數
當咱們調用transfer函數向某個地址發送N個ERC-20代幣的時候,交易的input數據分爲3個部分:工具
4 字節,是方法名的哈希:a9059cbbui
32字節,放以太坊地址,目前以太坊地址是20個字節,高危補0 000000000000000000000000abcabcabcabcabcabcabcabcabcabcabcabcabca.net
32字節,是須要傳輸的代幣數量,這裏是1*10^18 GNT 0000000000000000000000000000000000000000000000000de0b6b3a76400003d
全部這些加在一塊兒就是交易數據:cdn
a9059cbb000000000000000000000000abcabcabcabcabcabcabcabcabcabcabcabcabca0000000000000000000000000000000000000000000000000de0b6b3a7640000blog
當調用transfer方法提幣時,若是容許用戶輸入了一個短地址,這裏一般是交易所這裏沒有作處理,好比沒有校驗用戶輸入的地址長度是否合法。
若是一個以太坊地址以下,注意到結尾爲0:
0x1234567890123456789012345678901234567800
當咱們將後面的00省略時,EVM會從下一個參數的高位拿到00來補充,這就會致使一些問題了。
這時,token數量參數其實就會少了1個字節,即token數量左移了一個字節,使得合約多發送不少代幣出來。咱們看個例子:
這裏調用sendCoin方法時,傳入的參數以下:
0x90b98a11 00000000000000000000000062bec9abe373123b9b635b75608f94eb8644163e 0000000000000000000000000000000000000000000000000000000000000002
這裏的0x90b98a11是method的hash值,第二個是地址,第三個是amount參數。
若是咱們調用sendCoin方法的時候,傳入地址0x62bec9abe373123b9b635b75608f94eb8644163e,把這個地址的「3e」丟掉,即扔掉末尾的一個字節,參數就變成了:
0x90b98a11 00000000000000000000000062bec9abe373123b9b635b75608f94eb86441600 00000000000000000000000000000000000000000000000000000000000002 ^^ 缺失1個字節
這裏EVM把amount的高位的一個字節的0填充到了address部分,這樣使得amount向左移位了1個字節,即向左移位8。
這樣,amount就成了2 << 8 = 512。
(1)首先生成一個ETH的靚號,這個帳號末尾爲2個0
使用一些跑號工具就能夠作到,好比MyLinkToken工具,能夠很輕易跑出末尾兩個0的。
(2)找一個交易所錢包,該錢包裏token數量爲256000
(3)往這個錢包發送1000個幣
(4)而後再從這個錢包中提出1000個幣,固然這時候寫地址的時候把最後兩個0去掉
若是交易所並無校驗用戶填入的以太坊地址,則EVM會把全部函數的參數一塊兒打包,會把amount參數的高位1個字節吃掉。
(5)這三個參數會被傳入到msg.data中,而後調用合約的transfer方法,此時,amount因爲高位的1個字節被吃掉了,所以amount = amount << 8,即擴大了256倍,這樣就把25600個幣所有提出來了。
針對這個漏洞,說實話以太坊有不可推卸的責任,由於EVM並無嚴格校驗地址的位數,而且還擅自自動補充消失的位數。此外,交易所在提幣的時候,須要嚴格校驗用戶輸入的地址,這樣能夠儘早在前端就禁止掉惡意的短地址。
Reference
內容來源:知道創宇
做者:隱形人真忙