资讯动态

一篇文章能上手智能合约Solidity入门指南

发布时间:2026/8/12 18:18:52 来源:尧图企业网站定制
从质量工程师转型合约审计Solidity 不只是“能写”而是要练到“看每一行代码都能联想到可能的攻击路径”。这篇指南专为审计导向的入门设计——以最实用的方式带你打下扎实基础并从一开始就植入安全思维。 学习路径总览审计师眼中的 Solidity阶段核心目标关键产出1. 快速上手能读、写、部署简单合约用 Remix 完成一个计数器合约并理解每一部分2. 核心语法夯实精通存储位置、数据类型、继承、接口手动复现一个简化版 ERC20画出存储布局图3. EVM 与底层理解编译后发生了什么看清滑步用evm.codes单步调试一笔转账读懂操作码4. 安全特攻掌握 10 类漏洞的 Solidity 写法为每个漏洞手写最小示例并用 Foundry 编写 PoC 测试5. 审计实战像审计师一样分析陌生合约完整审计一个小型 DeFi 项目出具审计报告下面从零开始每一部分都附带“审计师怎么看”的安全提示。 第一阶段30 分钟快速上手 —— 写出你的第一个合约工具直接在浏览器打开 Remix IDE无需安装任何东西。1. 第一份合约Counter.solsolidity// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; // 指定编译器版本 contract Counter { // 状态变量永久存储在区块链上 uint256 public count; // 函数增加 count function increment() public { count 1; } // 函数读取当前值view 表示只读不消耗 gas function getCount() public view returns (uint256) { return count; } }在 Remix 的“Solidity Compiler”中编译然后在“Deploy Run Transactions”中部署点一下increment再点getCount你就完成了第一次链上交互。审计师的初印象谁都能调increment这是开放权限问题。count会溢出吗0.8.x 版本自带溢出保护但如果写进unchecked块内就会绕过。public变量自动生成 getter 函数这是 Solidity 的语法糖省掉写getCount但可能会隐藏其实现细节。 第二阶段核心语法 —— 从 ERC20 开始造轮子2.1 数据类型、存储位置与引用Solidity 有三种数据存储位置这是漏洞的滋生地。storage永久存储状态变量默认位置。操作昂贵。memory临时数据函数调用期间有效便宜。calldata只读的临时数据外部函数参数默认位置最便宜比 memory 更省 gas。经典坑storage指针在复杂结构中的误用。solidityuint256[] public numbers; function badPush() public { uint256[] storage arr numbers; // arr 指向 numbers 的 storage arr.push(123); // 实际修改了 numbers但可能会产生意想不到的 gas 成本 }审计师重点看函数内声明storage变量时它只是一个指向状态变量的指针任何修改都会直接写进链上尤其在映射、结构体嵌套中容易造成意外覆盖或存储冲突。2.2 映射与数组mapping是 DeFi 的骨架但有很多隐藏行为无法遍历不能获取长度。键本身不存储而是通过哈希得到存储槽你会在第三阶段深入理解。嵌套映射 (mapping(address mapping(address uint256))) 常用于授权容易因删除上一级键不完全造成“幽灵授权”。实战手动写一个极简 ERC20。soliditycontract SimpleToken { mapping(address uint256) public balanceOf; mapping(address mapping(address uint256)) public allowance; function transfer(address to, uint256 amount) public returns (bool) { require(balanceOf[msg.sender] amount, insufficient balance); balanceOf[msg.sender] - amount; balanceOf[to] amount; return true; } function approve(address spender, uint256 amount) public returns (bool) { allowance[msg.sender][spender] amount; return true; } function transferFrom(address from, address to, uint256 amount) public returns (bool) { require(allowance[from][msg.sender] amount, insufficient allowance); require(balanceOf[from] amount, insufficient balance); allowance[from][msg.sender] - amount; balanceOf[from] - amount; balanceOf[to] amount; return true; } }问题点你现在可能还不清楚但审计时必查approve的安全风险如果已有一个未使用的授权量直接覆盖会引发“approve 竞态”攻击应改为先置0再设新值。没有返回值检验EIP-20 要求transfer返回 bool有些代币不返回需要用 SafeERC20 库兼容。2.3 可见性与修饰符public、external、internal、private。审计师最关注external函数的入口以及private是否真能隐藏信息链上所有数据都公开private只限制合约间访问。modifier用来做权限检查如onlyOwner。如果修饰符逻辑写错比如用require(tx.origin owner)则完全破坏了安全模型后文会说tx.origin漏洞。⚙️ 第三阶段EVM 心智模型 —— 看懂字节码才懂漏洞3.1 存储槽计算每个合约有 2^256 个存储槽每个槽 32 字节。状态变量按声明顺序紧凑存储小于 32 字节的变量可能合并到一个槽。mapping和动态数组的存储位置通过keccak256计算。审计师必须会给定合约代码手算出任意mapping键值所在的存储槽。这是理解代理升级存储冲突、delegatecall漏洞的基础。工具打开 evm.codes用它的 playground 调试存储操作。或者在 Foundry 中用cast storage 合约地址 槽直接读取。3.2delegatecall与代理原理这是审计的最高频考点之一。delegatecall是“借用”其他合约的代码但使用当前合约的上下文msg.sender、msg.value、存储。solidity// 代理合约 contract Proxy { address implementation; fallback() external payable { (bool ok, ) implementation.delegatecall(msg.data); require(ok); } }如果逻辑合约的存储布局与代理不一致就可能错误地覆盖关键数据。EIP-1967 等标准通过使用固定伪随机槽来隔离实现地址。实战学习打开 OpenZeppelin 的 Proxy 合约一行行读懂并自己写一个最小可运行版本故意制造一次存储冲突观察如何破坏合约。️ 第四阶段安全特攻 —— 把漏洞写成 Solidity 代码这是你从“会写”到“能审”的质变阶段。你需要亲手为每一个经典漏洞写出脆弱合约 攻击合约 Foundry 测试。必须亲手复现的漏洞清单按学习顺序重入攻击solidity// 脆弱合约 withdraw() function withdraw() public { uint256 amount balances[msg.sender]; (bool ok, ) msg.sender.call{value: amount}(); require(ok); balances[msg.sender] 0; // 状态更新在后 }攻击合约在receive()中递归调用withdraw()。整数溢出uncheckedsolidityfunction batchTransfer(address[] calldata receivers, uint256 amount) public { uint256 total receivers.length * amount; // 可能溢出 // 如果 total 溢出为小数字可转账少量代币覆盖所有人 }访问控制漏洞tx.origin钓鱼require(tx.origin owner)会因用户调用攻击合约而被绕过。ecrecover返回0签名恢复可能返回零地址若未检查攻击者可绕过验证。抢先跑 / 时间戳操纵solidityuint256 random uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao)));这绝对不能用于抽奖。矿工可操纵时间戳且结果可被预测。存储碰撞代理升级自建一个透明代理和一个逻辑合约在 v2 中新增一个变量放在原有变量之前然后观察旧数据被错乱覆盖。练习资源EthernautOpenZeppelin 的闯关游戏按顺序通关每关的漏洞就是你审计的弹药。Damn Vulnerable DeFiDeFi 安全攻防逼迫你像黑客一样思考闪电贷攻击。DeFiHackLabsFork 真实区块在 Foundry 测试里重放 Curve、PolyNetwork 等历史攻击。 第五阶段用审计方式写测试 —— 从 Test 到 Audit你已有模糊测试平台经验现在需要把它和 Solidity 深度绑定。在 Foundry 中用invariant testing不变性测试来锁定业务逻辑漏洞。例如对于上述简易 ERC20可以写solidity// 在 Foundry 测试合约中 function invariant_totalSupplyEqualsSumOfBalances() public { uint256 total; for (uint i 0; i holders.length; i) { total token.balanceOf(holders[i]); } assertEq(total, token.totalSupply()); }用 Echidna 或 Foundry 跑上百万次随机调用这会暴露类似“铸造时没更新totalSupply”的低级错误也会让复杂的舍入误差浮现。 学习资源地图持续更新类型推荐资源用途官方文档Solidity Docs遇到任何不确定的语法直接查。尤其注意“Security Considerations”小节。交互教程Solidity by Example每个概念配代码适合快速过一遍语法。EVM 可视化evm.codes把合约字节码和存储操作变成彩色方块神级理解工具。安全清单Solcurity Standard社区维护的 Solidity 代码安全规范审计时逐项核对。漏洞百科全书SWC Registry每个 SWC 编号对应一类漏洞带描述、代码、测试方法。实战赛事Code4rena 的报告区看 Top 审计员怎么找漏洞、怎么写报告比任何教材都贴近真实业务。进阶书籍《Smart Contract Security Field Guide》(cmichel)免费的电子书系统讲解 DeFi 安全建议学完以上再读。✅ 从入门到审计的 8 周践行计划周期核心任务产出物第1周完成 Solidity by Example 全部示例Remix 写至少 5 个合约自己的代码仓库第2周精读 OpenZeppelin ERC20 和 Ownable 源码画存储布局图手绘/电子版存储槽分配图第3周通关 Ethernaut 全部 27 关写 Foundry PoC每个关卡的测试脚本第4周用 Foundry 完成 Damn Vulnerable DeFi 所有关卡完整的攻击合约和测试第5周复现 DeFiHackLabs 中的 5 个历史攻击攻击流程注释文档第6周为一个真实的简单项目如去中心化投票写全量不变性测试一份测试报告第7周参加一次 Code4rena 的 Look back只读旧比赛模仿写一份 High/Med 级别的审计报告仿写审计报告第8周总结并输出你自己的 Solidity 安全手册用于公司内部培训公司内部 Wiki到这一步你已经不是小白而是一个能发现关键漏洞的审计型工程师。记住Solidity 的“精通”不是记下所有语法而是每看到一行代码脑子里就开始排列可能的攻击场景。带着这个思维去写每一行 Solidity你的审计之路会越走越宽。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价