跳转到帖子

链上追踪视角下的 EVM 钱包风险识别:从授权、资金路径到自动化告警

精选回复

发布于

背景

做钱包风控时,很多人只盯着“余额是否被转走”,但实际攻击往往在更早阶段就已经出现信号:异常授权、资金来源污染、交互合约高危、批量小额试探、跨链桥跳转等。尤其在 EVM 链上,攻击者通常不会一上来就清空钱包,而是先通过钓鱼签名、Permit 授权、恶意合约交互拿到可操作权限,再等待合适时机转移资产。

这篇文章主要从链上追踪和钱包风控角度,整理一套比较实用的分析方法:如何识别钱包风险、如何拆解交易行为、如何用脚本做基础自动化扫描。内容偏实战,不讨论泛泛的“不要点钓鱼链接”。

技术分析

1. 钱包风险通常不是单点事件

一个钱包是否高危,不能只看某一笔交易。更合理的做法是综合以下维度:

  • 授权风险:是否对陌生合约做过无限授权,尤其是 ERC20 approve、Permit、Permit2。
  • 资金来源:资金是否来自混币器、被盗地址、钓鱼归集地址或高风险 CEX 充值地址。
  • 交互对象:是否频繁调用未验证合约、短时间新部署合约、代理合约异常升级。
  • 行为模式:是否出现测试转账、小额 gas 补充、批量分发、固定间隔扫钱。
  • 跨链路径:是否通过桥、DEX、聚合器快速切换资产形态,增加追踪成本。

单独一条信号不一定代表被盗,但多条信号叠加时,就应该进入人工复核或自动拦截流程。

2. 授权是最容易被忽视的风险面

很多钱包被盗并不是私钥泄露,而是签过危险授权。典型包括:

  • approve(spender, uint256.max):无限授权给恶意 spender。
  • increaseAllowance:逐步提高额度,用户不容易察觉。
  • permit:链下签名,链上由攻击者提交,用户可能没有明确看到 approve 交易。
  • Permit2:统一授权管理,如果 spender 是高危地址,影响范围会更大。

风控时建议至少维护三个列表:

  • 已知钓鱼 spender 地址列表。
  • 高频被盗资产接收地址列表。
  • 异常合约创建者地址列表。

如果发现用户钱包向这些地址授权过资产,即使资金尚未转走,也应标记为高危。

3. 资金路径追踪不能只看一跳

攻击者常见处理路径:

  1. 受害钱包资产转入临时地址 A。
  2. A 将 Token 通过 DEX 换成 ETH、USDT、USDC 等高流动性资产。
  3. 资金进入地址 B、C 分散。
  4. 部分资金进入跨链桥或交易所充值地址。

如果只看第一跳,很容易漏掉后续归集地址。实际追踪建议至少展开 2 到 3 层,但要控制噪声:DEX Router、桥合约、CEX 热钱包这类中间地址需要特殊处理,不能简单当成最终控制地址。

操作步骤

步骤一:收集目标钱包交易

可以通过节点 RPC、区块浏览器 API、索引服务获取目标地址历史交易。最低要求是拿到:

  • 普通交易:from、to、value、input、timestamp。
  • ERC20 Transfer 事件。
  • ERC20 Approval 事件。
  • 内部交易或 trace 数据。

如果只依赖普通交易,会漏掉大量合约内部转账。

步骤二:解析 Approval 事件

ERC20 Approval 事件签名为:

Approval(address,address,uint256)
keccak256 = 0x8c5be1e5ebec7d5bd14f714f9b5c7f89c5e1e5f0b6f2b8f2b6f2b8f2b6f2b8f2  // 示例不要硬编码,实际应计算或使用标准 topic

实际开发中不要复制网上不确定的 topic,建议直接用 ABI 解码,避免 topic 写错导致漏报。重点关注:

  • owner 是否为目标钱包。
  • spender 是否为未知合约或高危地址。
  • value 是否接近 2^256 - 1。
  • 授权后是否很快发生 TransferFrom。

步骤三:分析 Token 流向

对每一笔 ERC20 Transfer,建立图结构:

  • 节点:地址。
  • 边:转账记录,包含 token、amount、tx_hash、block_time。
  • 权重:金额折算、风险标签、时间间隔。

这样可以快速看到资金是否集中流向同一批地址,或者是否出现典型“被盗归集”形态。

步骤四:设置风险评分

可以先从简单规则开始,不一定一开始就上复杂模型。例如:

  • 无限授权给未知合约:+30
  • spender 命中黑名单:+50
  • 授权后 10 分钟内发生资产转出:+40
  • 资金一跳进入 DEX 换币:+15
  • 两跳内进入已知混币或跨链桥:+25
  • 目标合约创建时间小于 7 天:+20

总分超过阈值后进入告警。实际线上建议保留命中原因,方便人工复核,而不是只给一个分数。

代码示例

下面是一个基础示例:使用 web3.py 查询某个钱包在指定区块范围内的 ERC20 Approval 日志,并标记无限授权。示例偏简化,适合作为风控任务的起点。

from web3 import Web3
from eth_abi import decode

RPC_URL = "
w3 = Web3(Web3.HTTPProvider(RPC_URL))

wallet = Web3.to_checksum_address("0xYourWalletAddressHere")
from_block = 19000000
to_block = 19010000

# Approval(address indexed owner, address indexed spender, uint256 value)
approval_topic = Web3.keccak(text="Approval(address,address,uint256)").hex()

MAX_UINT256 = 2 ** 256 - 1
RISKY_SPENDERS = {
    Web3.to_checksum_address("0x0000000000000000000000000000000000000000")
}

def topic_to_address(topic_hex: str) -> str:
    return Web3.to_checksum_address("0x" + topic_hex[-40:])

logs = w3.eth.get_logs({
    "fromBlock": from_block,
    "toBlock": to_block,
    "topics": [approval_topic, "0x" + "0" * 24 + wallet[2:].lower()]
})

for log in logs:
    token = Web3.to_checksum_address(log["address"])
    owner = topic_to_address(log["topics"][1].hex())
    spender = topic_to_address(log["topics"][2].hex())
    value = int.from_bytes(log["data"], byteorder="big")

    risk_flags = []

    if value > MAX_UINT256 // 2:
        risk_flags.append("INFINITE_APPROVAL")

    if spender in RISKY_SPENDERS:
        risk_flags.append("RISKY_SPENDER")

    if risk_flags:
        print({
            "tx_hash": log["transactionHash"].hex(),
            "token": token,
            "owner": owner,
            "spender": spender,
            "value": str(value),
            "risk_flags": risk_flags,
            "block": log["blockNumber"]
        })

这个脚本有几个地方需要注意:

  • 这里只查了指定钱包作为 owner 的 Approval,不包含 Permit 类链下签名授权。
  • 公共 RPC 可能限制日志范围,生产环境建议使用自建节点或稳定索引服务。
  • 只看 Approval 不够,还要关联后续 Transfer 事件判断是否已被利用。
  • 黑名单地址不要硬编码在脚本里,最好放在可更新的数据源中。

进一步的风控实现建议

1. 加入合约画像

对于 spender 或交互合约,可以记录以下信息:

  • 合约是否开源验证。
  • 部署时间和部署者地址。
  • 是否代理合约,implementation 是否近期变更。
  • 是否存在高危函数选择器,例如批量转账、任意调用、delegatecall 包装。
  • 是否与已知钓鱼地址存在资金或部署关联。

一个新部署、未验证、由高危地址部署、短时间收到大量授权的合约,基本应该进入重点监控。

2. 处理 DEX Router 噪声

资金流图里,Uniswap、1inch、0x、Curve 等 Router 会造成大量中间节点。如果不做识别,路径会非常乱。建议:

  • 维护常见 Router 白名单。
  • 把 Router 视为“交易动作”,而不是最终收款方。
  • 解析 Swap 事件,记录换入换出资产。
  • 继续追踪 swap 后资产的接收地址。

这样才能区分正常交易和盗币后的快速洗换。

3. 注意时间窗口

很多盗币路径的时间特征很明显:

  • 授权后几分钟内转出。
  • 转出后立即 swap。
  • swap 后几笔分散到新地址。
  • 最终在几十分钟内进入桥或交易所。

因此风控规则里一定要加入时间窗口。只看静态地址关系,误报会比较多。

注意事项

  • 不要把所有无限授权都当成盗币。很多正常 DeFi 协议也使用无限授权,关键看 spender 画像和后续行为。
  • Permit 风险需要单独处理。它可能没有传统 approve 交易,但最终链上仍会出现由攻击者提交的授权或转账动作。
  • 交易所地址要谨慎标注。CEX 热钱包不是攻击者地址,但充值地址可能是资金去向线索。
  • 跨链后不要断点。桥事件通常能提供目标链、接收地址、资产信息,要继续在目标链追踪。
  • 保留证据链。包括 tx hash、block number、日志索引、事件参数,方便后续复核和申诉。

总结

EVM 钱包风控的核心不是简单判断“这个地址是不是黑地址”,而是把授权、交互合约、资金路径、时间窗口放在一起看。实际落地时,可以先从 Approval 监控和 Transfer 路径追踪做起,再逐步加入合约画像、跨链追踪、风险评分和人工复核。

经验上,最有价值的告警通常来自组合信号:陌生合约无限授权 + 短时间资产转出 + DEX 换币 + 多跳分散。单点规则容易误报,组合规则更接近真实攻击路径。

网络流量与边界分析示意图
网络请求、日志与边界流量分析示意
这个方向挺实用,补充几个我在做 EVM 钱包风险识别时比较常用的落地点,尤其是“授权 + 资金路径 + 告警”怎么串起来。 第一类建议优先监控 `Approval` / `ApprovalForAll`,很多被盗前兆不是资金立刻转走,而是先给了异常合约无限授权。可以把授权额度、spender 风险、合约年龄放一起打分:
风险点:
1. ERC20 Approval value 接近 uint256 max
2. ERC721/ERC1155 setApprovalForAll = true
3. spender 合约创建时间很短
4. spender 没有源码验证,或 bytecode 与已知恶意样本相似
5. spender 近期集中收到多个新钱包授权
6. 授权后短时间内出现 transferFrom / safeTransferFrom
如果自己跑节点,可以直接扫日志,不一定依赖第三方 API。比如用 topic 过滤 ERC20 Approval:
from web3 import Web3

w3 = Web3(Web3.HTTPProvider("

APPROVAL_TOPIC = Web3.keccak(text="Approval(address,address,uint256)").hex()

logs = w3.eth.get_logs({
    "fromBlock": 19000000,
    "toBlock": "latest",
    "topics": [APPROVAL_TOPIC]
})

for log in logs:
    owner = "0x" + log["topics"][1].hex()[-40:]
    spender = "0x" + log["topics"][2].hex()[-40:]
    value = int(log["data"].hex(), 16)

    if value > 2**255:
        print("infinite approval", log["address"], owner, spender, value, log["transactionHash"].hex())
第二类是资金路径,不建议只看一跳转账。很多地址会经过临时中转、聚合器、跨链桥、DEX,再进混币或交易所。实际分析时我一般分三层:
1-hop:直接收款地址、spender、交互合约
2-hop:中转地址、DEX pair/router、bridge gateway
3-hop:交易所充值地址、混币合约、已知钓鱼团伙地址簇
这里可以维护一个简单的地址标签库,字段不需要太复杂:
{
  "address": "0x...",
  "chain": "eth",
  "type": "phishing_spender / drainer / bridge / cex_deposit / mixer / dex_router",
  "source": "internal_case / public_report / manual_verify",
  "confidence": 80,
  "first_seen": 1700000000,
  "last_seen": 1700000000
}
第三类是告警规则,最好别只靠单点命中,不然误报会很多。可以用组合条件,例如:
高危告警:
- 钱包向新合约发起 Approval,且额度为无限授权
- spender 合约创建时间 < 7 天
- 该 spender 在 1 小时内获得超过 10 个不同 owner 授权
- 授权后 10 分钟内出现 transferFrom 转出

中危告警:
- 钱包首次与未验证合约交互
- 交互函数选择器命中已知 drainer 常见 selector
- 交易 value = 0,但 calldata 很长,且目标不是常见协议合约
有一个细节容易忽略:不要只看成功交易,失败交易也有价值。钓鱼站测试钱包余额、试探 allowance、批量调用失败,这些都能作为前置信号。可以把 failed tx 的 `to`、selector、revert pattern 也纳入画像。 自动化上可以拆成三个队列比较稳:
1. Log Collector:
   拉取 Approval / Transfer / contract creation / trace

2. Enrichment:
   补充合约年龄、源码验证状态、标签、首次交互时间、历史授权次数

3. Rule Engine:
   根据组合规则出告警,写入 ES / ClickHouse / PostgreSQL
如果是蓝队侧接入企业钱包或金库地址,建议把“撤销授权”也做成闭环,不只是告警。比如告警出来后自动生成 revoke 建议:
token: 0x...
owner: 0x...
spender: 0x...
current_allowance: unlimited
suggest_action: approve(spender, 0)
priority: high
reason: new spender + infinite approval + associated phishing cluster
最后一点,跨链资产路径要单独处理。很多 EVM 失窃资金会从 ETH / BSC / Polygon 之间走桥,单链上看会像“资金断掉”。桥合约 deposit 事件、目标链 mint/claim 地址要建立映射,否则告警链路会断。这个部分建议至少覆盖常见 bridge router 和 canonical token bridge。总体上,授权风险是前兆,资金路径是溯源,自动化告警要做组合打分,三者放一起效果会比单纯黑名单好很多。

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。