调试无源码合约

先用 Execution Trace 缩小范围,再用 Opcode、PC、Stack 和运行时数据解释关键调用。

案例目标

在目标合约没有已验证源码时,定位把 WETH 解包所得 ETH 转出的指令,并确认目标地址与金额。

示例交易与背景

0xbdec39a74e620fc624f90483aff067b17044f81138e6c30038daf7f873159db4

在 Skylens 中打开交易

目标执行合约为 0x64dc84faa300b5f27c7ebae8d867d039337e5999。在 WETH 解包后,它向费用接收方和交易发起人发起两次原生 ETH 调用。

预期回答的问题

  1. 哪个 Trace 包含目标 ETH 转账?
  2. Bytecode Debugger 中应检查哪条 Opcode 和哪些 Stack 参数?
  3. 没有源码时,如何交叉验证目标地址与金额?

分析步骤

  1. 在 Execution Trace 搜索执行合约和 WETH 地址,将范围缩小到 WETH.withdraw 之后。
  2. 先记录 Trace 中显示的调用方、目标地址、value 和返回状态。
  3. 从目标 Trace 进入 Bytecode Debugger,在 Instructions 中定位 CALL;结合 PC 记录稳定的字节码位置。
  4. 在执行 CALL 前检查 Stack 顶部七个参数:gas、to、value、inOffset、inSize、outOffset、outSize。
  5. 检查 Memory 中是否存在 calldata,并用调用返回值与后续余额结果验证执行是否成功。

关键证据

Trace行为地址与金额
610调用 WETH.withdraw解包 0.680568466516036487 WETH
616WETH 向执行合约发送原生 ETH0.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。其中 Trace 619 把 0.679568466516036487 ETH 转给交易发起人,Trace 618 支付 0.001 ETH 费用。即使没有源码,Opcode、Stack、Trace 运行时参数和金额守恒仍能形成可复核的证据链。

注意事项

  • PC 是特定 bytecode 版本中的位置;合约升级或重新部署后需要重新定位。
  • Stack 以 32 字节字为单位,地址取低 20 字节,value 使用 wei。
  • CALL 成功只说明 EVM 返回成功,还要检查余额和后续逻辑。
  • 演示夹具用于说明操作方法,分析报告必须引用真实交易中的运行时数据。

相关文档

调试无源码合约 | Skylens Docs