智能合约逆向心法2(案例篇)——34C3_CTF题目分析续篇
字数 1683 2025-08-22 12:22:15

智能合约逆向工程实战教学:34C3 CTF题目深度解析

1. 题目背景与基础知识

1.1 以太坊单位系统

题目中涉及多种以太坊单位,这些单位都是以对密码学和计算机科学做出重大贡献的人物命名:

  • Wei: 以太坊最小单位 (1 Wei = 10⁻¹⁸ ETH),以密码学先驱Wei Dai命名
  • Babbage: 1 Babbage = 10³ Wei = 1 KWei,以通用计算机之父查尔斯·巴贝奇命名
  • Lovelace: 1 Lovelace = 10⁶ Wei = 1 MWei,以计算机程序创始人洛芙莱斯命名
  • Shannon: 1 Shannon = 10⁹ Wei = 1 GWei,以信息论之父香农命名
  • Szabo: 1 Szabo = 10¹² Wei = 1 Microether,以比特金概念提出者尼克·萨博命名
  • Finney: 1 Finney = 10¹⁵ Wei = 1 Milliether,以比特币最早支持者哈尔·芬尼命名
  • Ether: 标准ETH单位 (1 Ether = 10¹⁸ Wei)

1.2 题目关键信息

题目给出以下转账金额要求:

1505 szabo + 457282 babbage + 649604 wei

换算为ETH:

  • 1505 szabo = 1505 × 10¹² wei = 0.001505 ETH
  • 457282 babbage = 457282 × 10³ wei = 0.000457282 ETH
  • 649604 wei = 0.000000649604 ETH
    总和 = 0.001505 + 0.000457282 + 0.000000649604 = 0.001962931604 ETH

目标合约地址:0x949a6ac29b9347b3eb9a420272a9dd7890b787a3

2. 合约逆向分析

2.1 合约函数概览

合约包含三个主要函数:

  1. func_00cc: 用于返回flag的条件函数
  2. Withdraw: 提款函数(本题目中不重要)
  3. Receive: 接收ETH并设置flag的函数

2.2 合约存储布局

通过逆向分析可以推断出合约的状态变量:

address var_addr;      // 存储在storage slot 0
bytes32 var_bytes;     // 存储在storage slot 1
mapping var_map;       // 存储在后续slot中

2.3 func_00cc函数分析

function func_00CC(var arg0) returns (var r0) {
    // 检查输入参数的最后2字节是否等于storage[1]的最后2字节
    if (arg0 & 0xffff != storage[0x01] & 0xffff) {
        return 0x00;
    }
    
    // 返回var_map[msg.sender]的值
    memory[0x00:0x20] = msg.sender;
    memory[0x20:0x40] = 0x02;
    return storage[keccak256(memory[0x00:0x40])];
}

2.4 Receive函数分析

function Receive() {
    // 第一部分:准备call调用
    memory[memory[0x40:0x60] + 0x20:memory[0x40:0x60] + 0x20 + 0x20] = 0x00;
    var temp0 = memory[0x40:0x60];
    memory[temp0:temp0 + 0x20] = msg.value;
    var var3 = temp0 + 0x20;
    
    // 执行call调用
    var temp2;
    temp2, memory[temp1:temp1 + 0x20] = address(0x02).call.gas(msg.gas - 0x646e)(memory[temp1:temp1 + var3 - temp1]);
    
    // 检查调用是否成功
    if (!temp2) {
        revert(memory[0x00:0x00]);
    }
    
    // 将返回值与storage[1]异或后存入映射
    var temp3 = memory[memory[0x40:0x60]:memory[0x40:0x60] + 0x20] ~ storage[0x01];
    memory[0x00:0x20] = msg.sender;
    memory[0x20:0x40] = 0x02;
    storage[keccak256(memory[0x00:0x40])] = temp3;
}

3. 解题步骤

3.1 获取storage slot 1的值

使用web3.js获取storage slot 1的值:

await web3.eth.getStorageAt("0x949a6ac29b9347b3eb9a420272a9dd7890b787a3", 1)

假设返回值为0x...c1cb,则最后2字节为0xc1cb

3.2 构造input data

func_00cc的函数选择器是0x2a0f7696,加上参数最后2字节0xc1cb,构造input data:

0x2a0f7696c1cb

3.3 执行解题流程

  1. 向合约地址0x949a6ac29b9347b3eb9a420272a9dd7890b787a3发送0.001505000457931604 ETH

    • 精确值:1505 szabo + 457282 babbage + 649604 wei
    • 调用Receive函数
  2. 使用input data 0x2a0f7696c1cb调用func_00cc函数

  3. 获取返回的flag:

    • 返回的十六进制值:0x333443335f6772616e646d615f626f756768745f736f6d655f626974636f696e
    • 转换为ASCII:34C3_grandma_bought_some_bitcoin

4. 关键知识点总结

4.1 以太坊单位转换

  • 必须精确理解各单位的换算关系
  • 题目中给出的金额需要精确计算,不能四舍五入

4.2 存储布局分析

  • storage slot是理解合约状态变量的关键
  • mapping类型的变量使用keccak256哈希计算存储位置

4.3 函数调用分析

  • 通过函数选择器识别调用的函数
  • 理解input data的构造方式(函数选择器+参数)

4.4 逆向工程技巧

  • 从字节码还原伪代码的能力
  • 识别常见模式(如mapping访问、storage布局等)

5. 参考资料

  1. 题目地址
  2. 合约地址
  3. 反编译地址
  4. Writeup参考
智能合约逆向工程实战教学:34C3 CTF题目深度解析 1. 题目背景与基础知识 1.1 以太坊单位系统 题目中涉及多种以太坊单位,这些单位都是以对密码学和计算机科学做出重大贡献的人物命名: Wei : 以太坊最小单位 (1 Wei = 10⁻¹⁸ ETH),以密码学先驱Wei Dai命名 Babbage : 1 Babbage = 10³ Wei = 1 KWei,以通用计算机之父查尔斯·巴贝奇命名 Lovelace : 1 Lovelace = 10⁶ Wei = 1 MWei,以计算机程序创始人洛芙莱斯命名 Shannon : 1 Shannon = 10⁹ Wei = 1 GWei,以信息论之父香农命名 Szabo : 1 Szabo = 10¹² Wei = 1 Microether,以比特金概念提出者尼克·萨博命名 Finney : 1 Finney = 10¹⁵ Wei = 1 Milliether,以比特币最早支持者哈尔·芬尼命名 Ether : 标准ETH单位 (1 Ether = 10¹⁸ Wei) 1.2 题目关键信息 题目给出以下转账金额要求: 换算为ETH: 1505 szabo = 1505 × 10¹² wei = 0.001505 ETH 457282 babbage = 457282 × 10³ wei = 0.000457282 ETH 649604 wei = 0.000000649604 ETH 总和 = 0.001505 + 0.000457282 + 0.000000649604 = 0.001962931604 ETH 目标合约地址: 0x949a6ac29b9347b3eb9a420272a9dd7890b787a3 2. 合约逆向分析 2.1 合约函数概览 合约包含三个主要函数: func_00cc : 用于返回flag的条件函数 Withdraw : 提款函数(本题目中不重要) Receive : 接收ETH并设置flag的函数 2.2 合约存储布局 通过逆向分析可以推断出合约的状态变量: 2.3 func_ 00cc函数分析 2.4 Receive函数分析 3. 解题步骤 3.1 获取storage slot 1的值 使用web3.js获取storage slot 1的值: 假设返回值为 0x...c1cb ,则最后2字节为 0xc1cb 3.2 构造input data func_ 00cc的函数选择器是 0x2a0f7696 ,加上参数最后2字节 0xc1cb ,构造input data: 3.3 执行解题流程 向合约地址 0x949a6ac29b9347b3eb9a420272a9dd7890b787a3 发送 0.001505000457931604 ETH 精确值:1505 szabo + 457282 babbage + 649604 wei 调用Receive函数 使用input data 0x2a0f7696c1cb 调用func_ 00cc函数 获取返回的flag: 返回的十六进制值: 0x333443335f6772616e646d615f626f756768745f736f6d655f626974636f696e 转换为ASCII: 34C3_grandma_bought_some_bitcoin 4. 关键知识点总结 4.1 以太坊单位转换 必须精确理解各单位的换算关系 题目中给出的金额需要精确计算,不能四舍五入 4.2 存储布局分析 storage slot是理解合约状态变量的关键 mapping类型的变量使用keccak256哈希计算存储位置 4.3 函数调用分析 通过函数选择器识别调用的函数 理解input data的构造方式(函数选择器+参数) 4.4 逆向工程技巧 从字节码还原伪代码的能力 识别常见模式(如mapping访问、storage布局等) 5. 参考资料 题目地址 合约地址 反编译地址 Writeup参考