您可以使用pickletools module生成反汇编操作流,这将让您收集有关腌制数据需要访问哪些模块和名称的信息。我会用pickletools.genops()在这里发挥作用。
现在,该模块是aimed在从事 pickle 库工作的核心开发人员中,因此有关其发出的操作码的文档只能在模块源代码,并且许多都与协议的特定版本相关,但是GLOBAL and STACK_GLOBAL opcodes这里有一些有趣的操作码。如果是GLOBAL
,加载的名称是操作码参数,在其他情况下,您需要查看堆栈。然而,堆栈比压入和弹出操作稍微复杂一些,因为可变长度项(列表、字典等)使用标记对象来允许 unpickler 检测此类对象何时完成,并且有记忆功能,以避免必须重复命名流中的项目。
模块代码详细说明了堆栈、备忘录和各种操作码的工作方式,但如果您只需要知道引用了哪些名称,则通常可以忽略其中的大部分内容。
因此,对于您的流,并假设该流是总是格式良好,以下简化dis()
函数可以让你提取所有引用的名称GLOBAL
and STACK_GLOBAL
操作码:
import pickletools
def get_names(stream):
"""Generates (module, qualname) tuples from a pickle stream"""
stack, markstack, memo = [], [], []
mo = pickletools.markobject
for op, arg, pos in pickletools.genops(stream):
# simulate the pickle stack and marking scheme, insofar
# necessary to allow us to retrieve the names used by STACK_GLOBAL
before, after = op.stack_before, op.stack_after
numtopop = len(before)
if op.name == "GLOBAL":
yield tuple(arg.split(1, None))
elif op.name == "STACK_GLOBAL":
yield (stack[-2], stack[-1])
elif mo in before or (op.name == "POP" and stack and stack[-1] is mo):
markpos = markstack.pop()
while stack[-1] is not mo:
stack.pop()
stack.pop()
try:
numtopop = before.index(mo)
except ValueError:
numtopop = 0
elif op.name in {"PUT", "BINPUT", "LONG_BINPUT", "MEMOIZE"}:
if op.name == "MEMOIZE":
memo.append(stack[-1])
else:
memo[arg] = stack[-1]
numtopop, after = 0, [] # memoize and put do not pop the stack
elif op.name in {"GET", "BINGET", "LONG_BINGET"}:
arg = memo[arg]
if numtopop:
del stack[-numtopop:]
if mo in after:
markstack.append(pos)
if len(after) == 1 and op.arg is not None:
stack.append(arg)
else:
stack.extend(after)
以及示例输入的简短演示:
>>> pickled_bar = pickle.dumps(bar)
>>> for mod, qualname in get_names(pickled_bar):
... print(f"module: {mod}, name: {qualname}")
...
module: __main__, name: bar
或者一个稍微复杂一点的例子inspect.Signature()实例对于相同的:
>>> import inspect
>>> pickled_sig_set = pickle.dumps({inspect.signature(bar)})
>>> for mod, qualname in get_names(pickled_sig_set):
... print(f"module: {mod}, name: {qualname}")
...
module: inspect, name: Signature
module: inspect, name: _empty
后者利用记忆来重新使用inspect
的名称inspect.Signature.empty
参考,以及跟踪集合元素开始位置的标记。