智能合约逆向心法1(案例篇)——34C3_CTF题目分析
字数 1536 2025-08-22 18:37:15

智能合约逆向分析教学文档:34C3 CTF题目解析

一、智能合约逆向基础

1.1 智能合约逆向分析工具

  1. Etherscan:用于查看合约字节码和交易记录
  2. EtherVM.io:提供反编译(Decompilation)和反汇编(Disassembly)功能
    • 自动调用4byte.directory解析函数签名
    • 支持查看合约存储状态
  3. 4byte.directory:函数签名数据库,用于识别未知函数

1.2 关键概念

  1. 字节码结构
    • 函数选择器:调用时msg.data前4字节
    • 参数编码:遵循ABI规范
  2. 存储布局
    • storage槽使用规则
    • 变量在storage中的位置

二、34C3 CTF题目分析

2.1 题目信息

  • 合约地址:0x949a6ac29b9347b3eb9a420272a9dd7890b787a3
  • 交易信息
    1. 0x2a0f7696
    2. 0x2a0f7696
    3. 0x2a0f7696000000000000000000000000000000000000000000000000000000000000c1cb
    4. 0x9f1b3bad
    5. 0x2a0f7696000000000000000000000000000000000000000000000000000000000000c1cb

2.2 合约功能分析

2.2.1 主调度函数(main)

function main() {
    memory[0x40:0x60] = 0x60;
    if (msg.data.length < 0x04) {
        revert(memory[0x00:0x00]);
    }
    
    // 提取函数选择器
    var var0 = msg.data[0x00:0x20] / 0x0100000000000000000000000000000000000000000000000000000000 & 0xffffffff;
    
    // 函数分发逻辑
    if (var0 == 0x2a0f7696) { // func_00CC
        if (msg.value) { revert(memory[0x00:0x00]); }
        var var1 = 0x0081;
        var var2 = msg.data[0x04:0x24] & 0xffff;
        var1 = func_00CC(var2);
        var temp0 = memory[0x40:0x60];
        memory[temp0:temp0 + 0x20] = var1;
        var temp1 = memory[0x40:0x60];
        return memory[temp1:temp1 + (temp0 + 0x20) - temp1];
    } 
    else if (var0 == 0x5b6b431d) { // Withdraw
        if (msg.value) { revert(memory[0x00:0x00]); }
        var1 = 0x00c0;
        var2 = msg.data[0x04:0x24];
        Withdraw(var2);
        stop();
    } 
    else if (var0 == 0x9f1b3bad) { // Receive
        var1 = 0x00ca;
        Receive();
        stop();
    } 
    else {
        revert(memory[0x00:0x00]);
    }
}

2.2.2 func_00CC函数分析

function func_00CC(var arg0) returns (var r0) {
    var var0 = 0x00;
    if (arg0 & 0xffff != storage[0x01] & 0xffff) {
        return 0x00;
    }
    memory[0x00:0x20] = msg.sender;
    memory[0x20:0x40] = 0x02;
    return storage[keccak256(memory[0x00:0x40])];
}

关键点

  1. 参数类型:arg0是2字节数据(& 0xffff
  2. 验证逻辑:输入参数必须等于storage[0x01]存储的值
  3. 返回值:基于msg.sender和固定值0x02计算storage位置后返回

2.2.3 Receive函数分析

function Receive() {
    var var0 = 0x00;
    var var1 = var0;
    var var2 = 0x02;
    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;
    var temp1 = memory[0x40:0x60];
    var temp2;
    temp2, memory[temp1:temp1 + 0x20] = address(var2).call.gas(msg.gas - 0x646e)(memory[temp1:temp1 + var3 - temp1]);
    if (!temp2) { revert(memory[0x00:0x00]); }
    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;
}

关键点

  1. 修改了storage[0x01]的值
  2. msg.sender设置了storage值
  3. 使得后续func_00CC调用能够通过验证

2.3 解题步骤

  1. 分析交易流程

    • 前三次调用func_00CC都返回0x00,说明验证失败
    • 第四次调用Receive函数修改了存储状态
    • 第五次调用func_00CC成功返回有效数据
  2. 提取flag

    from Crypto.Util.number import *
    a = 0x333443335f6772616e646d615f626f756768745f736f6d655f626974636f696e
    print(long_to_bytes(a))
    

    输出:b'34C3_grandma_bought_some_bitcoin'

三、智能合约逆向技巧总结

3.1 分析方法

  1. 从主调度函数入手

    • 识别函数选择器
    • 分析参数提取方式
    • 跟踪函数调用流程
  2. 存储分析

    • 识别关键storage位置
    • 跟踪storage读写操作
    • 理解storage布局
  3. 内存分析

    • 识别内存使用模式
    • 跟踪内存读写操作

3.2 常见模式

  1. 访问控制

    • msg.sender验证
    • storage中的权限标记
  2. 状态验证

    • 输入参数与storage值比较
    • 条件返回值
  3. 资金操作

    • .call.value()调用模式
    • 余额检查

四、参考资料

  1. 题目地址: https://archive.aachen.ccc.de/34c3ctf.ccc.ac/challenges/index.html
  2. 合约地址: https://etherscan.io/address/0x949a6ac29b9347b3eb9a420272a9dd7890b787a3
  3. 反编译地址: https://ethervm.io/decompile?address=0x949A6aC29B9347B3eB9a420272A9DD7890B787A3
  4. Writeup: https://github.com/kuqadk3/CTF-and-Learning/blob/master/34c3ctf/crypto/chaingang/readme.md
智能合约逆向分析教学文档:34C3 CTF题目解析 一、智能合约逆向基础 1.1 智能合约逆向分析工具 Etherscan :用于查看合约字节码和交易记录 EtherVM.io :提供反编译(Decompilation)和反汇编(Disassembly)功能 自动调用4byte.directory解析函数签名 支持查看合约存储状态 4byte.directory :函数签名数据库,用于识别未知函数 1.2 关键概念 字节码结构 : 函数选择器:调用时msg.data前4字节 参数编码:遵循ABI规范 存储布局 : storage槽使用规则 变量在storage中的位置 二、34C3 CTF题目分析 2.1 题目信息 合约地址 :0x949a6ac29b9347b3eb9a420272a9dd7890b787a3 交易信息 : 0x2a0f7696 0x2a0f7696 0x2a0f7696000000000000000000000000000000000000000000000000000000000000c1cb 0x9f1b3bad 0x2a0f7696000000000000000000000000000000000000000000000000000000000000c1cb 2.2 合约功能分析 2.2.1 主调度函数(main) 2.2.2 func_ 00CC函数分析 关键点 : 参数类型: arg0 是2字节数据( & 0xffff ) 验证逻辑:输入参数必须等于 storage[0x01] 存储的值 返回值:基于 msg.sender 和固定值 0x02 计算storage位置后返回 2.2.3 Receive函数分析 关键点 : 修改了 storage[0x01] 的值 为 msg.sender 设置了storage值 使得后续 func_00CC 调用能够通过验证 2.3 解题步骤 分析交易流程 : 前三次调用 func_00CC 都返回 0x00 ,说明验证失败 第四次调用 Receive 函数修改了存储状态 第五次调用 func_00CC 成功返回有效数据 提取flag : 输出: b'34C3_grandma_bought_some_bitcoin' 三、智能合约逆向技巧总结 3.1 分析方法 从主调度函数入手 : 识别函数选择器 分析参数提取方式 跟踪函数调用流程 存储分析 : 识别关键storage位置 跟踪storage读写操作 理解storage布局 内存分析 : 识别内存使用模式 跟踪内存读写操作 3.2 常见模式 访问控制 : msg.sender 验证 storage中的权限标记 状态验证 : 输入参数与storage值比较 条件返回值 资金操作 : .call.value() 调用模式 余额检查 四、参考资料 题目地址: https://archive.aachen.ccc.de/34c3ctf.ccc.ac/challenges/index.html 合约地址: https://etherscan.io/address/0x949a6ac29b9347b3eb9a420272a9dd7890b787a3 反编译地址: https://ethervm.io/decompile?address=0x949A6aC29B9347B3eB9a420272A9DD7890B787A3 Writeup: https://github.com/kuqadk3/CTF-and-Learning/blob/master/34c3ctf/crypto/chaingang/readme.md