返回文章库

EVM 底层剖析:从 Solidity 到字节码的执行哲学

查找币 学术研究 安全研究 Web3安全 区块链安全
EVM 底层剖析:从 Solidity 到字节码的执行哲学

By:Flush@查找币安全团队


导语:理解 EVM 是安全与开发的基石

在智能合约的世界里,以太坊虚拟机(EVM)及其算法与数据结构构成了最底层的“第一性原理”。我们编写的每一行 Solidity 代码,最终都会被编译并部署到 EVM 上执行。无论你是希望成为顶尖的 Solidity 开发者,还是立志于智能合约安全审计的专业人员,深入理解 EVM 的内部运行机制都是不可或缺的必修课。

本系列文章将基于对知名安全研究员 noxx 研究成果的深度解读,带领大家逐步揭开 EVM 的神秘面纱。本篇作为开篇,将聚焦于一个核心问题:当用户调用一个智能合约函数时,EVM 是如何从字节码中定位并执行对应函数的?


核心流程:Solidity → 字节码 → 操作码

在深入技术细节之前,我们先建立一个基础认知框架。智能合约代码(如 Solidity)在部署到以太坊网络之前,必须经过编译阶段,生成二进制字节码。EVM 本身并不理解 Solidity 语法,它只识别并执行这些字节码。而字节码又由一系列更底层的操作码(Opcode) 组成,每个操作码代表一个原子级的计算或存储操作。

编译后的字节码包含了整个合约的全部逻辑,包括多个可调用的函数。那么,EVM 是如何在茫茫字节码中,精准地找到用户想要调用的那个函数呢?答案就藏在合约字节码的函数选择器(Function Selector) 机制中。


实战分析:1_Storage.sol 的字节码与操作码

我们使用 Remix IDE 编译一个经典的 Storage 合约,作为分析样本。

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

contract Storage {
    uint256 number;

    function store(uint256 num) public {
        number = num;
    }

    function retrieve() public view returns (uint256) {
        return number;
    }
}

该合约包含两个函数:store() 和 retrieve()。在 Remix 的编译面板中,我们可以查看完整的字节码。其中,有一段关键的字节码片段,正是 EVM 用于判断函数调用目标的选择器逻辑。

操作码解析

通过 Ethervm.io 我们可以查阅 EVM 操作码列表。一个操作码长度为 1 个字节(byte),理论上支持 256 种不同的指令,但 EVM 目前仅使用了其中的 140 个。当我们将上述字节码解析为操作码序列时,EVM 会按顺序在调用栈上执行这些指令。


函数调用的底层原理:ABI 编码与选择器

在深入操作码之前,我们需要理解智能合约函数调用是如何在底层传递的。用户发起一个交易调用合约函数时,calldata 中包含了编码后的函数签名和参数。Solidity 提供了多种 ABI 编码方式:

以调用 store(10) 为例,使用 abi.encodeWithSignature("store(uint256)", 10) 生成的编码数据如下:

0x6057361d000000000000000000000000000000000000000000000000000000000000000a

EVM 如何匹配选择器

当 EVM 收到这笔交易时,它会执行字节码中的选择器逻辑。本质上,这是一组简单的“if-else”语句:将 calldata 的前 4 个字节与合约中每个函数的 keccak256 签名前 4 字节逐一比对。匹配成功后,EVM 通过 JUMPI 操作码跳转到对应函数的字节码起始位置。

例如:


交互式学习:EVM Playground 实操

为了更直观地理解上述过程,推荐使用 EVM Playground 进行交互式学习。

  1. 将目标合约的字节码粘贴到 Playground 中。
  2. 设置 calldata 为 0x6057361d000000000000000000000000000000000000000000000000000000000000000a。
  3. 点击“运行”,通过右上角的“单步调试”按钮,逐条执行操作码。

在调试过程中,你可以清晰观察到:

尝试将 calldata 改为 retrieve() 的选择器 0x2e64cec1,观察执行路径的差异。这种交互式体验,能帮助你从“纸上谈兵”进阶为“眼见为实”。


总结与预告

通过本篇文章,我们揭示了 EVM 处理函数调用的核心机制:函数选择器 作为字节码中的“路由表”,通过简单的条件跳转,将执行流导向正确的函数代码块。这是 EVM 设计中最精妙也最基础的部分之一。

理解这一过程,对于智能合约开发者而言,意味着能更准确地控制合约行为;对于安全研究人员而言,则是识别重入攻击、选择器碰撞等漏洞的关键前提。

在下一篇文章《EVM 深入探讨 - Part 2》中,我们将继续深入,探索 合约内存(Memory) 的本质,以及它在 EVM 中的工作方式。敬请期待!


本文由查找币安全团队整理发布

前往社区查看和回复