Dạy bạn chat với ETH
lời tựa
tiêu đề phụ
lời tựaCách đây ít lâu có vụ đánh cắp Poly Network, một địa chỉ chuyển tiền đến địa chỉ của hacker và thông báo USDT trong dữ liệu đầu vào đã bị đóng băng, không sử dụng USDT, sau khi hacker biết chuyện, anh ta đã chuyển 13,37 ETH đến địa chỉ.Biết Phòng thí nghiệm bảo mật chuỗi khối Chuangyu
Input data
Trả lời cho bạn.
tiêu đề phụ
Trong giao thức Ethereum, khi giao dịch (transaction) là tạo hợp đồng, dữ liệu đầu vào là mã EVM của chương trình khởi tạo tài khoản;
Và khi giao dịch là một cuộc gọi tin nhắn, dữ liệu đầu vào là dữ liệu cuộc gọi chức năng hợp đồng.
Trong các trường hợp bình thường, một cuộc gọi tin nhắn đơn giản, chẳng hạn như gọi chức năng chuyển tiền, cần điền địa chỉ _nơi bạn muốn chuyển và số tiền _số tiền bạn muốn chuyển.Những thông tin cơ bản này được bao gồm trong dữ liệu đầu vào.
Hãy cùng tìm hiểu cấu trúc của dữ liệu đầu vào khi một thông báo được gọi thông qua phân tích cụ thể về giao dịch chuyển tiền gọi là hợp đồng.
Hình thức phân tích:
Mẫu ban đầu:
Chúng tôi chia dữ liệu đầu vào ban đầu thành ba phần để phân tích:
0xa9059cbb: định danh chức năng
000000000000000000000000345d8e3a1f62ee6b1d483890976fd66168e390f2: Tham số đầu tiên là địa chỉ, là địa chỉ bạn muốn chuyển và nó sẽ được điền vào 32 byte, tức là 64 ký tự thập lục phân
00000000000000000000000000000000000000000000054b7d8ed70650b290000: Tham số thứ hai là giá trị, là số tiền bạn muốn chuyển và nó sẽ được điền vào 32 byte, tức là 64 ký tự thập lục phân
Qua phân tích so sánh, chúng ta có thể thấy cấu trúc cơ bản của dữ liệu đầu vào là định danh hàm + tham số.
định danh chức năng"transfer(adddress,uint256)"Mã định danh hàm ở đây là bộ chọn hàm.Theo tài liệu chính thức, bộ chọn hàm là 4 byte đầu tiên của hàm băm Keccak (SHA-3) của chữ ký hàm (big endian với bit cao ở bên trái).
Chúng ta có thể chuyển mã bytess4(keccake256(
)) hoặc các công cụ trực tuyến để lấy chữ ký chức năng này.
Hình bên dưới cho thấy bốn byte đầu tiên của kết quả mã hóa (a9059cbb) nhất quán với mã định danh chức năng trong dữ liệu đầu vào.
Lý do tại sao chữ ký chức năng bị cắt bớt bốn byte là để xem xét chi phí Gas.
Trong một giao dịch, 0 byte cần trả 4 gas, trong khi các byte khác 0 cần 68 gas, gấp 17 lần 0 byte.
Chuỗi ngẫu nhiên 32 byte được tạo trong mã hóa SHA-3 có xu hướng có nhiều byte khác không hơn, do đó, chi phí gần đúng là 32x68=2176 gas và chi phí cắt ngắn là khoảng 4x68=272 gas, điều này cho thấy rằng nó bị cắt bớt thành bốn byte Nó có thể tiết kiệm khoảng 8 lần phí gas.
Chức năng của mã định danh chức năng là chỉ định chức năng nào sẽ gọi, trong cùng một hợp đồng, xác suất 4 byte đầu tiên của chữ ký SHA-3 của hai chức năng khác nhau giống nhau là rất nhỏ, do đó, việc cắt bớt 4 byte sẽ không thành công. thực sự Ảnh hưởng đến các cuộc gọi chức năng.
tham số
Tổng hợp các kiểu mảng tĩnh thông dụng: uint, bool, Address, bytes[0-32], các kiểu mảng động: bytes, string, address[], bytes32[].....
loại tĩnh
Chúng tôi sử dụng chức năng mã hóa ABI của pyethereum để nghiên cứu cách mã hóa các loại dữ liệu khác nhau.
import rlp
from ethereum.abi import encode_abi
loại tĩnh
> encode_abi(["address", "uint256"],
[345d8e3a1f62ee6b1d483890976fd66168e390f2,1]).hex()
000000000000000000000000345d8e3a1f62ee6b1d483890976fd66168e390f2
0000000000000000000000000000000000000000000000000000000000000001
Đầu tiên nhập hàm encode_abi
> encode_abi(["int8[3]"],[[1, 2, 3]).hex()
Hãy lấy hàm transfer(address,uint 256) làm ví dụ
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000003
Các mảng có độ dài cố định nhỏ hơn 32 byte sẽ tự động được điền vào 32 byte:
// tự động điền 0
> encode_abi(
["uint256[]", "uint256[]", "uint256[]"],[[0xa1, 0xa2, 0xa3], [0xb1, 0xb2, 0xb3], [0xc1, 0xc2, 0xc3]]
).hex()
kiểu động
Mã hóa kiểu động phức tạp hơn một chút và độ lệch cần được tính toán trước để xử lý trình giữ chỗ. Hãy sử dụng một ví dụ đơn giản để minh họa.
// Độ lệch của tham số 1: 32*3=96 Hệ thập lục phân 0x60000000000000000000000000000000000000000000000000000000000000060
// Độ lệch của tham số 2 = độ lệch của tham số 1 + độ dài phần dữ liệu của tham số 1 = 96+32*4=224 hệ thập lục phân 0xE000000000000000000000000000000000000000000000000000000000000000000000
// Độ lệch của tham số 3 = độ lệch của tham số 2 + độ dài phần dữ liệu của tham số 2 = 224+32*4=352 thập lục phân 0x160000000000000000000000000000000000000000000000000000000000000160
// Dữ liệu của tham số 1 bắt đầu được truyền vào tại offset 0x60
000000000000000000000000000000000000000000000000000000000000000000003//phần tử
0000000000000000000000000000000000000000000000000000000000000000a1//Phần tử mảng đầu tiên
0000000000000000000000000000000000000000000000000000000000000000a2//Phần tử mảng thứ hai
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000b1
00000000000000000000000000000000000000000000000000000000000000b2
00000000000000000000000000000000000000000000000000000000000000b3
0000000000000000000000000000000000000000000000000000000000000000a3//Phần tử mảng thứ ba
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000c1
00000000000000000000000000000000000000000000000000000000000000c2
00000000000000000000000000000000000000000000000000000000000000c3
// vị trí 0xe0. Dữ liệu cho tham số 2
// vị trí 0x160. Dữ liệu cho tham số 3
tấn công địa chỉ ngắn
Sau khi phân tích trước đó, khi độ dài của loại tĩnh như địa chỉ nhỏ hơn 32 byte, EVM sẽ điền độ dài thành 32 byte theo quy tắc. Nếu địa chỉ truyền kết thúc bằng 00, chẳng hạn như 0x641988625108585185752230bde001b3ebd0fc00, hãy xóa hai số 0 sau địa chỉ khi truyền, EVM sẽ vẫn nghĩ rằng address_to là 32 bit, do đó, nó sẽ lấy 0 từ bit cao của _value để bổ sung và số lượng bit trong số lượng sẽ nhiều hơn hai bit, nghĩa là nó sẽ được nhân với 256.
Quá trình tấn công như sau:
Xóa số 0 trong byte cuối cùng của địa chỉ truyền độc hại
000000000000000000000000641988625108585185752230bde001b3ebd0fc
Định danh chức năng: a9059cbb
00000000000000000000000000000000000000000000000000000000000000001
Địa chỉ chuyển khoản:
Số tiền chuyển khoản:
Do quy tắc điền của EVM, kết quả phân tích cú pháp là: 0xa9059cbb00000000000000000000000641988625108585185752230bde001b3ebd0fc000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000100
Sau khi phân tách, chúng tôi thấy rằng số tiền chuyển đã được tăng lên hai chữ số, tức là một byte, gấp 256 lần số tiền chuyển ban đầu.
000000000000000000000000641988625108585185752230bde001b3ebd0fc00
Địa chỉ chuyển khoản:
00000000000000000000000000000000000000000000000000000000000000100
Số tiền chuyển khoản:
tiêu đề phụ
Cách đính kèm thông tin vào dữ liệu đầu vào
Trường dữ liệu đầu vào cho các giao dịch chuyển tiền trực tiếp trong Ethereum mặc định không có nội dung, nhưng chúng ta có thể thực hiện "chức năng trò chuyện" ở đầu bài viết bằng cách thiết lập ví.
Chúng tôi lấy ví MetaMask làm ví dụ để chỉ ra cách đính kèm một số thông tin bổ sung vào trường dữ liệu đầu vào thông qua chuyển khoản.
3. Sau khi chuyển thành công, bạn có thể xem thông tin đính kèm trong etherscan
tóm tắt
tiêu đề phụ

