Teach you to chat with ETH
foreword
secondary title
forewordSome time ago, there was an episode of the theft of Poly Network. An address transferred money to the hacker’s address and informed the USDT in the input data that it had been frozen. Do not use USDT. After the hacker knew about it, he transferred 13.37 ETH to the address.Know Chuangyu Blockchain Security Lab
Input data
Answer for you.
secondary title
In the Ethereum protocol, when the transaction (transaction) is a contract creation, the input data is the EVM code of the account initialization program;
And when the transaction is a message call, the input data is the contract function call data.
Under normal circumstances, a simple message call, such as calling a transfer function, needs to fill in the address _to you want to transfer and the amount _amount you want to transfer. These basic information are included in the input data.
Let's understand the structure of input data when a message is called through a specific analysis of a transfer transaction that calls a contract.
Analytical form:
Original form:
We divide the original input data into three parts for analysis:
0xa9059cbb: function identifier
00000000000000000000000345d8e3a1f62ee6b1d483890976fd66168e390f2: The first parameter is address, which is the address you want to transfer, and it will be filled to 32 bytes, that is, 64 hexadecimal characters
00000000000000000000000000000000000000000054b7d8ed70650b290000: The second parameter is value, which is the amount you want to transfer, and it will be filled to 32 bytes, that is, 64 hexadecimal characters
Through comparative analysis, we can find that the basic structure of input data is function identifier + parameter.
function identifier"transfer(adddress,uint256)"The function identifier here is the function selector. According to the official document, the function selector is the first 4 bytes of the Keccak (SHA-3) hash of a function signature (big endian with the high bit on the left).
We can pass the code bytess4(keccake256(
)) or online tools to get this function signature.
The figure below shows that the first four bytes of the encryption result (a9059cbb) are consistent with the function identifier in the input data.
The reason why the function signature is truncated to four bytes is to consider the Gas cost.
The 32-byte random string generated in SHA-3 encryption tends to have more non-zero bytes, so the approximate cost is 32x68=2176 gas, and the truncation cost is about 4x68=272 gas, which shows that it is truncated to four bytes It can save about 8 times the gas fee.
parameter
The function of the function identifier is to specify which function to call. In the same contract, the probability of the first 4 bytes of the SHA-3 signature of two different functions being the same is very small, so truncating to four bytes will not actually Affects function calls.
parameter
Summarize common static types: uint, bool, Address, bytes[0-32], dynamic array types: bytes, string, address[], bytes32[].....
static type
We use pyethereum's ABI encoding function to study how different data types are encoded.
import rlp
from ethereum.abi import encode_abi
static type
> encode_abi(["address", "uint256"],
[345d8e3a1f62ee6b1d483890976fd66168e390f2,1]).hex()
000000000000000000000000345d8e3a1f62ee6b1d483890976fd66168e390f2
0000000000000000000000000000000000000000000000000000000000000001
First import the encode_abi function
> encode_abi(["int8[3]"],[[1, 2, 3]).hex()
Let's take the function transfer(address,uint 256) as an example
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000003
Fixed-length arrays smaller than 32 bytes will be automatically filled to 32 bytes:
// autofill 0
> encode_abi(
["uint256[]", "uint256[]", "uint256[]"],[[0xa1, 0xa2, 0xa3], [0xb1, 0xb2, 0xb3], [0xc1, 0xc2, 0xc3]]
).hex()
dynamic type
Dynamic type encoding is a little more complicated, and the offset needs to be calculated first for placeholder processing. Let's use a simple example to illustrate it.
// Offset of parameter 1: 32*3=96 Hexadecimal 0x60000000000000000000000000000000000000000000000000000000000000060
// Offset of parameter 2 = offset of parameter 1 + data part length of parameter 1 = 96+32*4=224 hexadecimal 0xE0000000000000000000000000000000000000000000000000000000000000000000000000
// Offset of parameter 3 = offset of parameter 2 + data part length of parameter 2 = 224+32*4=352 hexadecimal 0x16000000000000000000000000000000000000000000000000000000000000160
// The data of parameter 1 starts to be passed in at offset 0x60
00000000000000000000000000000000000000000000000000000000000000003//elements
0000000000000000000000000000000000000000000000000000000000000a1//The first array element
0000000000000000000000000000000000000000000000000000000000000a2//The second array element
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000b1
00000000000000000000000000000000000000000000000000000000000000b2
00000000000000000000000000000000000000000000000000000000000000b3
0000000000000000000000000000000000000000000000000000000000000a3//The third array element
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000c1
00000000000000000000000000000000000000000000000000000000000000c2
00000000000000000000000000000000000000000000000000000000000000c3
// 0xe0 position. Data for parameter 2
//0x160 position. Data for parameter 3
short address attack
After the previous analysis, when the length of the static type such as address is less than 32 bytes, EVM will fill the length to 32 bytes according to the rules. If the address of the transfer ends with 00, such as 0x641988625108585185752230bde001b3ebd0fc00, remove the two zeros after the address when transferring , EVM will still think that address_to is 32 bits, so it will take 0 from the high bit of _value to supplement, and the number of bits in amount will be two more bits, that is, it will be multiplied by 256.
The attack process is as follows:
Remove the 0 in the last byte of the malicious transfer address
000000000000000000000000641988625108585185752230bde001b3ebd0fc
Function identifier: a9059cbb
00000000000000000000000000000000000000000000000000000000000000001
Transfer address:
transfer amount:
Due to the filling rules of EVM, the parsing result is: 0xa9059cbb00000000000000000000000641988625108585185752230bde001b3ebd0fc000000000000000000000000000000000000000 0000000000000000000000000100
After decomposing, we found that the transfer amount has been increased by two digits, that is, by one byte, which is 256 times the original transfer amount.
000000000000000000000000641988625108585185752230bde001b3ebd0fc00
Transfer address:
00000000000000000000000000000000000000000000000000000000000000100
transfer amount:
secondary title
How to attach information to input data
The input data field for direct transfer transactions in Ethereum has no content by default, but we can implement the "chat function" at the beginning of the article by setting up the wallet.
We take the MetaMask wallet as an example to show how to attach some additional information to the input data field through transfer.
3. After the transfer is successful, you can see the attached information in etherscan
Summarize
secondary title

