调试无源码合约
先用 Execution Trace 缩小范围,再用 Opcode、PC、Stack 和运行时数据解释关键调用。
案例目标
在目标合约没有已验证源码时,定位把 WETH 解包所得 ETH 转出的指令,并确认目标地址与金额。
示例交易与背景
0xbdec39a74e620fc624f90483aff067b17044f81138e6c30038daf7f873159db4目标执行合约为 0x64dc84faa300b5f27c7ebae8d867d039337e5999。在 WETH 解包后,它向费用接收方和交易发起人发起两次原生 ETH 调用。
预期回答的问题
- 哪个 Trace 包含目标 ETH 转账?
- Bytecode Debugger 中应检查哪条 Opcode 和哪些 Stack 参数?
- 没有源码时,如何交叉验证目标地址与金额?
分析步骤
- 在 Execution Trace 搜索执行合约和 WETH 地址,将范围缩小到
WETH.withdraw之后。 - 先记录 Trace 中显示的调用方、目标地址、value 和返回状态。
- 从目标 Trace 进入 Bytecode Debugger,在 Instructions 中定位
CALL;结合 PC 记录稳定的字节码位置。 - 在执行
CALL前检查 Stack 顶部七个参数:gas、to、value、inOffset、inSize、outOffset、outSize。 - 检查 Memory 中是否存在 calldata,并用调用返回值与后续余额结果验证执行是否成功。
关键证据
| Trace | 行为 | 地址与金额 |
|---|---|---|
610 | 调用 WETH.withdraw | 解包 0.680568466516036487 WETH |
616 | WETH 向执行合约发送原生 ETH | 0.680568466516036487 ETH |
618 | 执行合约支付费用 | 0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5,0.001 ETH |
619 | 执行合约向发起人转账 | 0x269ff4d056252a30cad249a4cd75cb9bcfb1f46c,0.679568466516036487 ETH |
Skylens 的文档字节码演示夹具在 PC 122 放置了 CALL,用于演示如何读取 Instructions、Stack 和 Memory。它不是完整链上 Stack 的替代品;实际目标地址与金额应以 Trace 619 的运行时数据为准,并在真实调试会话的 CALL 前再次核对 Stack:
to = 0x269ff4d056252a30cad249a4cd75cb9bcfb1f46c
value = 679568466516036487 wei金额守恒同样成立:Trace 618 与 619 的两笔输出之和等于 Trace 616 的输入。
案例结论
Execution Trace 将问题缩小到 WETH 解包后的两个
CALL。其中 Trace619把0.679568466516036487 ETH转给交易发起人,Trace618支付0.001 ETH费用。即使没有源码,Opcode、Stack、Trace 运行时参数和金额守恒仍能形成可复核的证据链。
注意事项
- PC 是特定 bytecode 版本中的位置;合约升级或重新部署后需要重新定位。
- Stack 以 32 字节字为单位,地址取低 20 字节,value 使用 wei。
CALL成功只说明 EVM 返回成功,还要检查余额和后续逻辑。- 演示夹具用于说明操作方法,分析报告必须引用真实交易中的运行时数据。
