对合约进行逆向工程
简介
区块链上没有秘密,发生的一切都是持续的、可验证的、公开的。 理想情况下,应将智能合约的源代码发布到 Etherscan 上进行验证(opens in a new tab)。 然而,情况并非总是如此(opens in a new tab)。 在本文中,你将研究一份没有源代码的合约 0x2510c039cc3b061d79e564b38836da87e31b342f
(opens in a new tab),从而学习如何对合约进行逆向工程。
有一些反编译器,但它们不一定能提供有用的结果(opens in a new tab)。 在本文中,你将从操作码(opens in a new tab)入手,学习如何对合约手动进行逆向工程并理解合约,以及如何解读反编译器生成的结果。
为了能够理解本文,你应当已经了解以太坊虚拟机基础知识,并至少对以太坊虚拟机汇编器有几分熟悉。 点击此处了解这些主题(opens in a new tab)。
准备可执行代码
你可以在 Etherscan 上获得合约的操作码,操作如下:点击 Contract 选项卡,然后切换至 Opcodes 视图。 你将看到每行有一条操作码。
但是,为了能够理解跳转,你需要知道每条操作码在代码中的位置。 为此,一种方式是打开 Google Spreadsheet 并把操作码粘贴到 C 列。你可以创建这个已制作好的电子表格的副本,从而跳过以下步骤(opens in a new tab)。
下一步是获得正确的操作码位置,以便我们能够理解跳转。 我们将操作码大小放入 B 列,操作码位置(十六进制形式)放入 A 列。在单元格 B1
中输入下面的函数,然后复制并粘贴到 B 列其余单元格中,直到代码结束。 完成后,你就可以隐藏 B 列。
1=1+IF(REGEXMATCH(C1,"PUSH"),REGEXEXTRACT(C1,"PUSH(\d+)"),0)2
首先该函数给操作码增加一个字节,然后查找 PUSH
操作码。 Push 操作码比较特殊,因为它们需要额外的字节表示压入的值。 如果操作码是 PUSH
,我们提取该字节的数值并在函数中增加相应的值。
在 A1
单元格中输入第一个偏移量 0。 然后在 A2
单元格中,输入下面的函数,并再次将它复制粘贴到 A 列其余他单元格中:
1=dec2hex(hex2dec(A1)+B1)2
我们需要此函数提供十六进制值,因为跳转(JUMP
和 JUMPI
)之前压入的值也是十六进制的。
入口点 (0x00)
智能合约总会从第一个字节开始执行。 下面是代码的开始部分:
偏移量 | 操作码 |
---|