ETHとのチャットを教えます
序文
副題
序文少し前の Poly Network 盗難事件のエピソードでは、アドレスがハッカーのアドレスに転送され、入力データの USDT に「凍結されているので USDT を使用しないでください」と通知されました。ハッカーがそれを知った後、彼は転送しました。アドレスに13.37 ETH。Chuangyu ブロックチェーン セキュリティ ラボを知る
Input data
答えてください。
副題
イーサリアムプロトコルでは、トランザクション(トランザクション)が契約作成の場合、入力データはアカウント初期化プログラムのEVMコードになります。
また、トランザクションがメッセージコールの場合、入力データはコントラクト関数呼び出しデータとなります。
通常、転送関数の呼び出しなどの単純なメッセージ呼び出しでは、転送するアドレス _to と転送する金額 _amount を入力する必要があり、これらの基本情報は入力データに含まれています。
コントラクトを呼び出す転送トランザクションの具体的な分析を通じて、メッセージが呼び出されるときの入力データの構造を理解してみましょう。
分析形式:
原形:
元の入力データを分析のために 3 つの部分に分割します。
0xa9059cbb: 関数識別子
00000000000000000000000345d8e3a1f62ee6b1d483890976fd66168e390f2: 最初のパラメータは、転送するアドレスである address で、32 バイト、つまり 64 個の 16 進文字で埋められます。
00000000000000000000000000000000000000000054b7d8ed70650b290000: 2 番目のパラメータは value で、転送する量です。32 バイト、つまり 64 個の 16 進数文字で埋められます。
比較分析により、入力データの基本構造は関数識別子 + パラメーターであることがわかります。
関数識別子"transfer(adddress,uint256)"ここでの関数識別子は関数セレクターであり、公式ドキュメントによると、関数セレクターは関数署名の Keccak (SHA-3) ハッシュの最初の 4 バイトです (上位ビットが左側のビッグ エンディアン)。
コード bytes4(keccake256(
)) またはオンライン ツールを使用して、この関数の署名を取得します。
次の図は、暗号化結果 (a9059cbb) の最初の 4 バイトが入力データの関数識別子と一致していることを示しています。
関数のシグネチャが 4 バイトに切り捨てられる理由は、Gas コストを考慮するためです。
トランザクションでは、0 バイトには 4 ガスを支払う必要がありますが、ゼロ以外のバイトには 0 バイトの 17 倍である 68 ガスが必要です。
SHA-3 暗号化で生成される 32 バイトのランダム文字列には、ゼロ以外のバイトが多く含まれる傾向があるため、おおよそのコストは 32x68=2176 ガス、切り捨てコストは約 4x68=272 ガスとなり、4 に切り捨てられることがわかります。 bytes ガス代が約8倍節約できます。
関数識別子の機能は、どの関数を呼び出すかを指定することです。同じコントラクト内で、2 つの異なる関数の SHA-3 署名の最初の 4 バイトが同じになる確率は非常に小さいため、4 バイトに切り捨てても問題はありません。実際には関数呼び出しに影響します。
パラメータ
一般的な静的型を要約します: uint、bool、Address、bytes[0-32]、動的配列型: bytes、string、address[]、bytes32[]....
静的型
pyethereum の ABI エンコード関数を使用して、さまざまなデータ型がどのようにエンコードされるかを研究します。
import rlp
from ethereum.abi import encode_abi
静的型
> encode_abi(["address", "uint256"],
[345d8e3a1f62ee6b1d483890976fd66168e390f2,1]).hex()
000000000000000000000000345d8e3a1f62ee6b1d483890976fd66168e390f2
0000000000000000000000000000000000000000000000000000000000000001
まずencode_abi関数をインポートします
> encode_abi(["int8[3]"],[[1, 2, 3]).hex()
関数 transfer(address,uint 256) を例にしてみましょう
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000003
32 バイト未満の固定長配列は、自動的に 32 バイトに埋められます。
// 0を自動入力します
> encode_abi(
["uint256[]", "uint256[]", "uint256[]"],[[0xa1, 0xa2, 0xa3], [0xb1, 0xb2, 0xb3], [0xc1, 0xc2, 0xc3]]
).hex()
ダイナミックタイプ
動的型エンコーディングはもう少し複雑で、プレースホルダー処理のために最初にオフセットを計算する必要があるため、簡単な例を使って説明しましょう。
// パラメータ 1 のオフセット: 32*3=96 16 進数 0x6000000000000000000000000000000000000000000000000000000000000060
// パラメータ 2 のオフセット = パラメータ 1 のオフセット + パラメータ 1 のデータ部分の長さ = 96+32*4=224 16 進数 0xE0000000000000000000000000000000000000000000000000000000000000000000
// パラメータ 3 のオフセット = パラメータ 2 のオフセット + パラメータ 2 のデータ部の長さ = 224+32*4=352 16 進数 0x16000000000000000000000000000000000000000000000000000000000000160
// パラメータ 1 のデータはオフセット 0x60 で渡され始めます
00000000000000000000000000000000000000000000000000000000000000003//要素
0000000000000000000000000000000000000000000000000000000000000a1//最初の配列要素
0000000000000000000000000000000000000000000000000000000000000a2//2 番目の配列要素
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000b1
00000000000000000000000000000000000000000000000000000000000000b2
00000000000000000000000000000000000000000000000000000000000000b3
0000000000000000000000000000000000000000000000000000000000000a3//3 番目の配列要素
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000c1
00000000000000000000000000000000000000000000000000000000000000c2
00000000000000000000000000000000000000000000000000000000000000c3
// 0xe0 の位置。パラメータ2のデータ
//0x160 の位置。パラメータ3のデータ
ショートアドレス攻撃
前回の分析後、アドレスなどの静的型の長さが 32 バイト未満の場合、EVM はルールに従って長さを 32 バイトに埋めます。転送のアドレスが 0x641988625108585185752230bde001b3ebd0fc00 のように 00 で終わる場合は、転送時にアドレスの後にゼロが 2 つある場合でも、EVM は address_to が 32 ビットであると認識するため、補うために _value の上位ビットから 0 を取り、量のビット数はさらに 2 ビットになります。 256 を掛けます。
攻撃プロセスは次のとおりです。
悪意のある転送アドレスの最後のバイトの 0 を削除します。
000000000000000000000000641988625108585185752230bde001b3ebd0fc
関数識別子: a9059cbb
00000000000000000000000000000000000000000000000000000000000000001
転送アドレス:
払込金額:
EVM の充填ルールにより、解析結果は次のようになります: 0xa9059cbb00000000000000000000000641988625108585185752230bde001b3ebd0fc00000000000000000000000000000000000000 0 000000000000000000000000100
分解すると転送量が2桁、つまり1バイト増加しており、元の転送量の256倍となっていることが分かりました。
000000000000000000000000641988625108585185752230bde001b3ebd0fc00
転送アドレス:
00000000000000000000000000000000000000000000000000000000000000100
払込金額:
副題
入力データに情報を付加する方法
イーサリアムの直接送金トランザクションの入力データフィールドにはデフォルトでは何もありませんが、ウォレットを設定することで記事冒頭の「チャット機能」を実装することができます。
MetaMask ウォレットを例として、転送を通じて入力データ フィールドに追加情報を添付する方法を示します。
3. 転送が成功すると、etherscan で添付情報を確認できます。
要約する
副題

