部署到链上的智能合约并不携带 ABI,它携带的只有字节码。但几乎所有 Solidity 或 Vyper 合约的字节码开头都有一段分发器(dispatcher):它把调用数据的前 4 个字节和合约支持的各个外部函数的选择器逐一比较,然后跳转到对应的函数体。所以,即使没有源码,也能从字节码里把这些选择器,以及每个函数的不少信息读出来。
这个工具就在你的浏览器里做这件事。粘贴 runtime 字节码或者选择一个文件,页面会列出它找到的选择器,以及能推断出的参数类型、状态可变性和存储布局。字节码自始至终不会离开你的电脑。
能做什么
输入。 在文本框里粘贴十六进制的 runtime 字节码。0x 前缀可带可不带,空白字符(空格、换行)会被忽略,所以复制来的整块内容直接粘贴就行。也可以选择一个文件,文件内容是十六进制文本或者原始字节都可以,页面会自动处理。上限:字节码最多 1 MiB,输入最多 4 MiB,分析超时时间 30 秒。
输出。 对分析器找到的每个函数,你会看到:
- 4 字节选择器,例如
0xa9059cbb - 函数体在字节码中的字节偏移
- dispatch 类型,说明这个函数是怎样从分发器到达的
- 推断的参数类型,例如
address,uint256 - 推断的状态可变性:
pure、view、payable或nonpayable
函数列表之外,报告还包括:
- 存储布局:代码访问的存储槽(slot),每个槽的偏移(offset)和推断类型,以及哪些选择器读、哪些选择器写这个槽。transient storage(
TLOAD/TSTORE)也包含在内。 - 编译器 metadata:solc 等编译器附加在 runtime 代码末尾的 CBOR 数据块,能解码的会解码出来,常见内容是 solc 版本号和 metadata 文件的 IPFS 哈希。
整个结果可以导出为 JSON,方便留档、和以后的版本做对比,或者交给其他工具继续处理。
函数名从哪里来
选择器是函数签名的 keccak256 哈希的前 4 个字节,例如 keccak256("transfer(address,uint256)")。哈希不可逆,所以函数名不可能从字节码里“解码”出来,唯一的办法是拿选择器去已知签名表里查。
页面内置了一份离线签名表,收录两百多个常见签名:ERC-20、ERC-721、ERC-1155、Ownable、AccessControl、Uniswap 等常用接口。工具不会查询 4byte.directory 或任何在线数据库。在表里查到的选择器标为离线库匹配,查不到的标为未知。如果签名表里的参数和从字节码推断出的参数对不上,页面会提示这可能是选择器碰撞或者推断偏差。4 字节选择器确实会碰撞,所以名字匹配只是一个假设,不是证明。
原理
分析引擎是 EVMole 0.9.3,编译成 WebAssembly,在 Web Worker 中运行,因此页面不会卡住。EVMole 不需要源码,也不需要 ABI。它对分发器做符号执行,找出选择器比较和对应的跳转目标;然后沿着每个函数体继续分析:函数怎样读取调用数据(据此推断参数类型)、是否检查 CALLVALUE(据此判断是否 payable)、会执行到哪些读写存储和改变状态的操作码(据此判断 view、pure 还是 nonpayable)。对 SLOAD、SSTORE、TLOAD、TSTORE 的同一套追踪,就得到了存储布局。
metadata 解码则是读取代码最后两个字节表示的长度,再解析它前面的 CBOR 映射,solc 就是在这里写入 solc、ipfs 等字段的。
REA 命令行的 inspect-evm-interface 用的也是同一个 EVMole 版本,所以你可以离线用 REA 复核浏览器里的结果,见文末的相关教程。
隐私是怎么保证的
字节码不会被上传,这是页面的实现方式决定的:
- 分析在你设备上的 Web Worker 里进行。EVMole 的 WebAssembly 文件(约 470 KB)在页面加载时就从本站
/_astro/路径下预先下载好了,所以之后即使断网,分析也照样能做。 - Worker 运行在一条内容安全策略(CSP)之下,只允许向本站同源地址发请求,这是为了加载那个
.wasm文件。Worker 发往其他任何地址的网络请求都会被浏览器拦截。 - 唯一的统计事件是
evm_analyze,只包含成功或错误码、找到的函数数量区间、耗时区间,绝不包含字节码、选择器、函数名或任何其他内容。完整的事件清单见隐私政策。 - 工具页不展示广告。
如何自己验证
- 在粘贴任何内容之前,打开浏览器开发者工具,切到 Network(网络) 面板。做一次分析,逐条查看出现的请求:没有任何一条带着你的字节码,本站的请求只有
.wasm文件这类页面资源。 - 或者先打开本页,等它加载完成,然后断开网络(关闭 Wi-Fi,或在 Network 面板勾选 Offline),再做分析。分析照样能完成,因为没有任何东西需要离开你的电脑。
局限
请把输出当作“从字节码推断出的结果”来读,而不是经过验证的 ABI。
- 状态可变性是从字节码推断的,可能和源码里的声明不同。 例如,一个声明为
view但实际上没有读取任何状态的函数,可能被推断成pure。编译器和优化器也会改变字节码里能看到的东西。 - 暂不支持 EOF 合约。 以
0xEF00开头的代码(EVM Object Format)会被拒绝。 - 需要 runtime(已部署)字节码,而不是 creation/init 代码。creation 代码里有构造函数,它负责把 runtime 代码复制到内存再返回,它的开头并不是你要找的分发器。用 solc 时请用
--bin-runtime,不要用--bin。 - 代理合约只能看到代理自身的选择器。 代理会用
DELEGATECALL把它不认识的调用转发给实现合约。要看真正的接口,请分析实现合约的字节码。 - 参数类型是推断的。 简单类型通常是对的;嵌套结构体、数组等复杂或动态类型可能不准确。
- 不同编译器效果不同。 Vyper 的产物或手写字节码,结果可能与常见的 solc 产物不同,有时不完整。
- 不产出源码,也不反编译。 工具还原的是接口和存储布局,不是业务逻辑,不会生成 Solidity 或其他任何源码。
- 列表为空或很短,不能证明没有别的可调用入口。 不常见的分发器写法可能让部分函数逃过分析。
常见问题
函数名会去网上查吗?
不会。函数名只来自页面内置的离线签名表,表里没有的选择器显示为“未知”。如果你自己去公开数据库查,请把查到的结果当作假设,因为选择器会碰撞。
源码里写的是 view,为什么显示 pure?
可变性是根据字节码实际做了什么推断出来的。如果一个 view 函数从不读取存储或环境数据,代码里就没有任何东西能把它和 pure 区分开。反过来的情况也可能出现。手上有 ABI 时,请以 ABI 为准对照。
粘贴了字节码,却几乎没有结果,为什么?
最常见的原因是:粘贴的是 creation 代码而不是 runtime 代码、这是一个代理合约,或者这是 EOF 合约(以 0xEF00 开头)。先确认你复制的是哪一种字节码。
runtime 字节码从哪里拿?
从你自己的构建产物里拿(solc --bin-runtime,或者 Hardhat、Foundry 产物里的 deployedBytecode 字段),或者对你有权分析的地址,用自己的节点调用 eth_getCode 获取。工具本身不发起任何 RPC 或区块浏览器请求。
这里的存储布局和 solc 输出的 storage layout 一样吗?
不一样。这里是根据代码实际访问的槽位重建出来的:能看到槽位、偏移、推断类型和哪些选择器访问了它,但看不到变量名;运行时才算出来的槽位(例如 mapping 的元素)不一定能完整呈现。
导出的 JSON 里有什么?
和页面上显示的内容一样:选择器、偏移、dispatch 类型、参数、可变性、名字匹配情况、存储布局和解码后的 metadata。JSON 在浏览器里生成,直接保存到你的设备上。
合规说明
请只用本工具分析你自己编写的合约、你获得授权审查的合约,或者字节码已经公开在链上的公开合约。请遵守所分析代码的许可协议以及适用于你的法律法规。还原出接口并不意味着你对这些代码享有任何权利,本工具也不提供任何攻击合约的建议。
相关教程
- 离线分析 EVM 字节码:用 REA 还原合约函数选择器和接口:在命令行用
inspect-evm-interface跑同样的 EVMole 0.9.3 分析,并用 solc 的输出逐项核对选择器、参数和可变性。 - 用 REA 和 AI agent 解 CTF 逆向题:从 crackme 到可验证的 flag:解题的通用流程,也适用于合约类关卡。
- REA MCP 安装指南:接入 Claude Code、Cursor 与 Codex:安装本地工具,做更深入的离线分析。