保姆级教程:Taiko测试网上
深入EVM-合约分类这件小事背后的风险
在智能合约领域,"以太坊虚拟机 EVM" 以及其算法和数据结构就是第一性原理。
本文从合约为什么要分类出发,结合每个场景可能面对怎样的恶意攻击,最终给出一套达成相对安全的合约分类分析算法。
虽然技术含量较高,但亦可作为杂谈读物,一览去中心化系统间博弈的黑暗森林。
1、合约为什么要分类?
因为太重要了,可谓是交易所、钱包、区块链浏览器、数据分析平台等等 Dapp 的基石!
一笔交易之所以是 ERC 20 转账,是因为他的行为符合 ERC 20 标准,至少得有:
交易的状态是成功
To 地址为某个符合 ERC 20 标准的合约
调用了 Transfer 函数,其特点是该交易 CallData 的前 4 位为0x a 9059 cbb
执行后,在该 To 地址上发出了transfer的事件
分类有误则交易行为会误判
以交易行为为基石,则 To 地址能否被准确分类则对其 CallData 的判断会有截然不然的结论。对 Dapp 而言,链上链下的信息沟通高度依赖于交易事件的监听,而同样的事件编码也只有在符合标准的合约中发出,才具有可信度。
分类有误则交易会误入黑洞
如果用户进行一笔 Token 转移,转入到某个合约中,如果该合约没有预设 Token 转出的函数方法,则资金会雷同于 Burn 一样被锁定,无法控制
且如今大量项目开始增加内置的钱包支持,要为用户管理钱包也就不可避免的,需要时刻从链上实时分类出最新部署的合约,是否能够吻合资产标准。
2、分类会有怎样的风险?
链上是一个没有身份没有法治的地方,你无法制止一笔正常的交易,哪怕他是恶意的。
他可以是冒充外婆的狼,做出多数符合你预期的外婆行为,但目的是进屋抢劫。
声明标准,但可能实质不符合
常见的分类方式是直接采用 EIP-165 标准,读取该地址是否支持 ERC 20 等,当然,这是一个高效率的方法,但是毕竟合约是对方控制,所以终究是可以伪造出一份申明。
165 标准的查询,只是在链上有限的操作码中,用最低成本去防止资金转入黑洞的方法。
这也是为什么我们之前分析 NFT 的时候,特地提及标准中会有一类SafeTransferFrom的方法,其中Safe就是指代了采用 165 标准判断出对方声明自己具备了 NFT 的转移能力。
唯有从合约字节码出发,做源码层面的静态分析,从合约预期的行为出发才有更精准的可能性。
3、合约分类方案设计
接下来咱们将系统的分析整体方案,注意我们的目的是“精度”和“效率”两项核心指标。
要知道即使方向是对的,但要抵达大洋的彼岸路途也并不明朗,要做字节码分析的第一站是获取代码
3.1、如何获取到代码?
从上链后的角度讲有getCode,一个 RPC 方法,可以从链上指定的地址里获取到字节码,单论读取的话这是非常快捷的,因为从 EVM 的账号结构中就把 codeHash 放在最顶端的位置。
但是这个方法等于是单独对某个地址做获取,想要进一步提升精度和效率呢?
如果是部署合约的交易,如何在其刚执行完甚至他还在内存池中便获取部署的代码?
如果该笔交易是合约工厂的模式,则交易的 Calldata 里是否存在源码呢?
最后的我的方式是,是分类进行一种类似筛子的模式
对于非合约部署的交易,则直接用getCode获取其中涉及的地址进行分类,
对于最新内存池的交易,筛选出 to 地址为空的交易,其 CallData 则是带有构造函数的源代码
对于合约工厂模式的交易,由于其中可能是合约部署出的合约再循环调用其他合约来执行部署,则递归的去分析该笔交易的子交易,记录每个 type 为CREATE或者为CREATE 2 的 Call。
我做了个 demo 实现的时候,发现还好现在 rpc 的版本比较高,因为整个过程最难的便是执行 3 的时候,如何递归找到指定 type 的 call,最底层的方式是通过 opcode 还原上下文,我吃了一惊!