区块链取证
区块链取证
本文适合
已经理解 日志分析基础、编码、哈希与加密,并知道地址、交易哈希、合约和事件这些基本概念的 Misc 学习者。学完你能:从交易哈希、地址、事件日志、calldata 和 storage 中还原资金流、状态变化或隐藏数据
一句话判断
题目给地址、交易哈希、区块高度、合约地址、ABI、RPC endpoint、event log 或链上浏览器截图时,就按区块链取证处理。
区块链取证题通常不是让你审计复杂合约,而是让你把链上历史“按时间线还原出来”,找到关键转账、事件、函数参数或存储槽。
题目中常见信号
常见材料:
0x... address
0x... transaction hash
contract address
ABI
RPC endpoint
event logs
private key fragment
block numberEVM 常见关键词:
calldata
selector
event
topic
storage slot
internal transaction
ERC20 Transfer常用工具:
区块浏览器
cast
web3.py
ethers.js
Foundry
Tenderly
evm.storage核心概念
链上取证常看五类对象:
说明:EOA 或合约
取证价值:追踪身份和资金流
说明:一次链上操作
取证价值:还原时间线
说明:调用输入
取证价值:解出函数和参数
说明:合约日志
取证价值:直接暴露状态变化或线索
说明:合约存储
取证价值:找 owner、secret、flag、状态变量
EVM 交易 input data 前 4 字节是函数选择器,后续数据按 ABI 编码。如果没有 ABI,可以通过函数选择器数据库、源码、反编译或事件主题辅助判断。
最小分析流程
1. 确认链和网络
先确认是:
- 以太坊主网。
- 测试网。
- 私链 / 题目链。
- 兼容 EVM 的其他链。
如果题目给 RPC:
cast chain-id --rpc-url http://target:85452. 从交易或地址建立时间线
如果有交易哈希:
cast tx 0xTXHASH --rpc-url http://target:8545
cast receipt 0xTXHASH --rpc-url http://target:8545如果有地址,先列出:
- 普通转账。
- Token 转账。
- Internal transaction。
- 合约创建。
- 事件日志。
3. 解码 calldata
有 ABI:
cast calldata-decode "transfer(address,uint256)" 0x...没有 ABI,先取前 4 字节:
0xa9059cbb再查函数选择器,或和源码里的函数签名比对。
4. 看事件日志
Receipt 里重点看:
topics[0]:事件签名哈希。- indexed 参数。
- data 字段。
ERC20 Transfer(address,address,uint256)、自定义 FlagSubmitted、SecretUpdated 这类事件经常直接给线索。
5. 必要时读 storage
cast storage 0xCONTRACT 0 --rpc-url http://target:8545
cast storage 0xCONTRACT 1 --rpc-url http://target:8545如果源码给出变量顺序,可以按 storage layout 推 slot。私有变量不等于链上不可见。
最小验证示例
解码 ERC20 Transfer 事件
topics[0] 为:
keccak256("Transfer(address,address,uint256)")topics[1]、topics[2] 常是 from/to,data 是 value。
Python 验证:
from eth_utils import event_signature_to_log_topic
topic0 = event_signature_to_log_topic("Transfer(address,address,uint256)")
print(topic0.hex())和日志中的 topics[0] 一致,就说明这是 ERC20 转账事件。
解码 calldata 选择器
from eth_utils import function_signature_to_4byte_selector
print(function_signature_to_4byte_selector("submit(bytes32)").hex())如果输出和交易 input 前 4 字节一致,后续 32 字节就是 submit 的参数。
常见利用 / 解题路线
路线总览:
路线一:资金流追踪
适合:
- 题目问钱去了哪里。
- 给多个地址或交易。
- 有 Token 转账或 internal transaction。
步骤:
- 从起点地址列交易。
- 按区块高度和交易序号排序。
- 分开记录原生币、ERC20、internal transfer。
- 找最终接收地址或异常中转。
- 用表格写清楚每一步。
路线二:事件日志取证
适合:
- 合约主动 emit 线索。
- 题目给 receipt 或 tx hash。
步骤:
- 获取 receipt。
- 匹配 event signature。
- 解码 topics 和 data。
- 将十六进制转 ASCII、数字或地址。
- 找 flag、secret、提交记录。
路线三:calldata 解码
适合:
- 交易 input data 很长。
- 有 ABI 或可猜函数签名。
- 关键参数不在事件里。
步骤:
- 取前 4 字节 selector。
- 查函数签名。
- 按 ABI 解码参数。
- 检查参数中是否包含 hash、bytes、string、地址。
路线四:storage 读取
适合:
- 合约变量保存 secret。
- 源码或反编译能推 slot。
- 事件和 calldata 没有明文。
步骤:
- 读 storage slot。
- 判断是否是地址、uint、bytes32、短字符串。
- 对 mapping 使用
keccak(key . slot)推位置。 - 解码并验证。
路线五:私链题复现
适合:
- 题目给 RPC endpoint。
- 需要和合约交互读取状态。
步骤:
cast chain-id确认可连。cast code确认合约存在。cast call调 view 函数。cast storage读关键 slot。- 必要时发送交易触发事件。
常见失败原因
- 只看余额不看历史:取证题答案常在过去某笔交易里。
- 忽略 internal transaction:合约内部转账不会总显示在普通转账列表。
- 不看 event log:flag 或 secret 常直接在事件 data 里。
- 不解 calldata:函数参数可能比余额更重要。
- 把 private 当隐藏:链上 storage 公开可读。
- 链选错:主网、测试网、私链浏览器和 RPC 不一致会查不到。
- 十六进制解码漏一步:bytes32 可能需要去零、ASCII、base64 或 hash 比对。
迷你案例
题目给:
RPC: http://target:8545
Contract: 0x1234...
Tx: 0xabcd...第一步看交易回执:
cast receipt 0xabcd... --rpc-url http://target:8545日志里出现一个自定义事件,data 为:
0x666c61677b6576656e745f6c6f677d000000000000000000000000000000第二步解码:
x = "666c61677b6576656e745f6c6f677d000000000000000000000000000000"
print(bytes.fromhex(x).rstrip(b"\x00").decode())输出:
flag{event_log}WP 要写清楚:
从 tx hash 获取 receipt
检查 event log 而不是只看转账
data 字段是 bytes32
去掉右侧 00 后按 ASCII 解码
得到 flag{event_log}这就是区块链取证的基本闭环:定位链、建立时间线、解码证据、给出可复现命令。