python pvm组成
PVM 由三个部分组成,引擎(或者叫指令分析器),栈区、还有一个 Memo (这个我也不知道怎么解释,我们姑且叫它 “标签区“)
1.引擎的作用
从头开始读取流中的操作码和参数,并对其进行处理,zai在这个过程中改变 栈区 和 标签区,处理结束后到达栈顶,形成并返回反序列化的对象
2.栈区的作用
作为流数据处理过程中的暂存区,在不断的进出栈过程中完成对数据流的反序列化,并最终在栈上生成发序列化的结果
3.标签区的作用
数据的一个索引或者标记
如图所示:
此处输入图片的描述
注意:PVM 指令的书写规范
(1)操作码是单字节的
(2)带参数的指令用换行符定界
3.PVM 操作码
为了表达的清楚完整我直接附上截图
此处输入图片的描述
此处输入图片的描述
这里面要重点关注几个
S : 后面跟的是字符串
( :作为命令执行到哪里的一个标记
t :将从 t 到标记的全部元素组合成一个元祖,然后放入栈中
c :定义模块名和类名(模块名和类名之间使用回车分隔)
R :从栈中取出可调用函数以及元祖形式的参数来执行,并把结果放回栈中
. :点号是结束符(图中没有,这里补充)
4.反序列化流程
序列化就是一个将对象转化成字符串的过程,这个我们能直接使用 pickle 实现,这个过程我们也无需利用和分析,这里不做深究,我这里就是说一下如何进行的反序列化
我们将下面这个字符串存储为一个文件 shell.pickle
cos system (S'/bin/sh' tR.
当我们使用下面这个函数对其进行加载的时候
>>> import pickle
>>> pickle.load(open('shell.pickle'))执行结果如图所示:
可以看到成功返回了 sh 的 shell
我们现在来结合我们上面讲述的 PVM 的操作码看这个文件中的字符串是怎么一步一步执行的
(1)c 后面是模块名,换行后是类名,于是将 os.system 放入栈中
(2)( 这个是标记符,我们将一个 Mark 放入栈中
(3)S 后面是字符串,我们放入栈中
(4)t 将栈中 Mark 之前的内容取出来转化成元祖,再存入栈中 (’/bin/sh’,),同时标记 Mark 消失
(5)R 将元祖取出,并将 callable 取出,然后将元祖作为 callable 的参数,并执行,对应这里就是 os.system(‘/bin/sh’),然后将结果再存入栈中
注意:
其实并不是所有的对象都能使用 pickle 进行序列化和反序列化,比如说 文件对象和网络套接字对象以及代码对象就不可以


