调试无源码合约

先用 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 顶部七个参数:gastovalueinOffsetinSizeoutOffsetoutSize
  5. 检查 Memory 中是否存在 calldata,并用调用返回值与后续余额结果验证执行是否成功。

关键证据

Trace行为地址与金额
610调用 WETH.withdraw解包 0.680568466516036487 WETH
616WETH 向执行合约发送原生 ETH0.680568466516036487 ETH
618执行合约支付费用0x95222290dd7278aa3ddd389cc1e1d165cc4bafe50.001 ETH
619执行合约向发起人转账0x269ff4d056252a30cad249a4cd75cb9bcfb1f46c0.679568466516036487 ETH

Skylens 的文档字节码演示夹具在 PC 122 放置了 CALL,用于演示如何读取 Instructions、Stack 和 Memory。它不是完整链上 Stack 的替代品;实际目标地址与金额应以 Trace 619 的运行时数据为准,并在真实调试会话的 CALL 前再次核对 Stack:

to    = 0x269ff4d056252a30cad249a4cd75cb9bcfb1f46c
value = 679568466516036487 wei

金额守恒同样成立:Trace 618619 的两笔输出之和等于 Trace 616 的输入。

案例结论

Execution Trace 将问题缩小到 WETH 解包后的两个 CALL。其中 Trace 6190.679568466516036487 ETH 转给交易发起人,Trace 618 支付 0.001 ETH 费用。即使没有源码,Opcode、Stack、Trace 运行时参数和金额守恒仍能形成可复核的证据链。

注意事项

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

相关文档