智能合约逆向心法1(案例篇)——34C3_CTF题目分析
字数 1536 2025-08-22 18:37:15
智能合约逆向分析教学文档: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)
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])];
}
关键点:
- 参数类型:
arg0是2字节数据(& 0xffff) - 验证逻辑:输入参数必须等于
storage[0x01]存储的值 - 返回值:基于
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;
}
关键点:
- 修改了
storage[0x01]的值 - 为
msg.sender设置了storage值 - 使得后续
func_00CC调用能够通过验证
2.3 解题步骤
-
分析交易流程:
- 前三次调用
func_00CC都返回0x00,说明验证失败 - 第四次调用
Receive函数修改了存储状态 - 第五次调用
func_00CC成功返回有效数据
- 前三次调用
-
提取flag:
from Crypto.Util.number import * a = 0x333443335f6772616e646d615f626f756768745f736f6d655f626974636f696e print(long_to_bytes(a))输出:
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