欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入理解Python虛擬機(jī)中的反序列化pyc文件

 更新時間:2023年05月17日 08:22:03   作者:一無是處的研究僧  
再這篇文章中我們將主要對?Code?Object?進(jìn)行分析,并且詳細(xì)它是如何被反序列化的,通過本篇文章我們將能夠把握整個?pyc?文件結(jié)構(gòu),感興趣的可以了解一下

在前面的文章當(dāng)中我們詳細(xì)的對于 pyc 文件的結(jié)構(gòu)進(jìn)行了分析,pyc 文件主要有下面的四個部分組成:魔術(shù)、 Bite Filed 、修改日期和 Code Object 組成。在前面的文章當(dāng)中我們已經(jīng)對前面三個部分進(jìn)行了字節(jié)角度的分析,直接從 pyc 文件當(dāng)中讀取對應(yīng)的數(shù)據(jù)并且打印出來了。而在本篇文章當(dāng)中我們將主要對 Code Object 進(jìn)行分析,并且詳細(xì)它是如何被反序列化的,通過本篇文章我們將能夠把握整個 pyc 文件結(jié)構(gòu)。

marshal 模塊的魔力

序列化和反序列化 python 對象

marshal 是 python 自帶的一個模塊,他可以將一些 python 內(nèi)置對象進(jìn)行序列化和反序列化操作,甚至我們可以在一個文件當(dāng)中序列話一個函數(shù)的 Code Object 對象,然后在另外一個文件反序列化這個 Code Object 對象并且執(zhí)行它。

我們可以使用下面的代碼將 python 當(dāng)中的一些對象序列化操作,直接將 python 對邊變成一個字節(jié)流,保存到磁盤當(dāng)中:

import marshal
if __name__ == '__main__':
    with open("pyobjects.bin", "wb") as fp:
        marshal.dump(1, fp)
        marshal.dump(1.5, fp)
        marshal.dump("Hello World", fp)
        marshal.dump((1, 2, 3), fp)
        marshal.dump([1, 2, 3], fp)
        marshal.dump({1, 2, 3}, fp)
        marshal.dump(1+1j, fp)
        marshal.dump({1: 2, 3: 4}, fp)

在上面的代碼當(dāng)中需要注意的是需要使用二進(jìn)制方式 rb 打開文件,上面的程序執(zhí)行完成之后會生成一個 pyobjects.bin 的二進(jìn)制文件,我們可以使用 python 代碼再將上面的 python 對象,比如整數(shù)、浮點數(shù)、字符串和列表元組等等反序列化出來。

import marshal
if __name__ == '__main__':
    with open("pyobjects.bin", "rb") as fp:
        print(marshal.load(fp))
        print(marshal.load(fp))
        print(marshal.load(fp))
        print(marshal.load(fp))
        print(marshal.load(fp))
        print(marshal.load(fp))
        print(marshal.load(fp))
        print(marshal.load(fp))

上面的代碼輸出結(jié)果如下所示:

1
1.5
Hello World
(1, 2, 3)
[1, 2, 3]
{1, 2, 3}
(1+1j)
{1: 2, 3: 4}

從上面代碼的輸出結(jié)果我們可以看到我們可以將所有的被寫入到二進(jìn)制文件當(dāng)中的數(shù)據(jù)全部解析了出來。

序列化和反序列化 CodeObject

除了上面使用 marshal 對 python 的基本對象進(jìn)行序列化和反序列化,我們可以使用 marshal 模塊對 CodeObject 進(jìn)行同樣的操作,如果是這樣的話,那么就可以將一個文件的代碼序列化,然后另外一個程序反序列化再進(jìn)行調(diào)用:

import marshal
def add(a, b):
    print("Hello World")
    return a+b
with open("add.bin", "wb") as fp:
    marshal.dump(add.__code__, fp)

在上面的代碼當(dāng)中,我們打開了文件 add.bin 然后將 add 函數(shù)的 CodeObject 對象寫入到文件當(dāng)中去,而 CodeObject 當(dāng)中保存了函數(shù) add 的所有執(zhí)行所需要的信息,因此我們可以在另外一個文件當(dāng)中打開這個文件,然后將 CodeObject 對象反序列化出來在執(zhí)行這個代碼,我們看下面的代碼:

import marshal
def name():
    pass
with open("add.bin", "rb+") as fp:
    code = marshal.load(fp)
name.__code__ = code
print(name(1, 2))

上面的代碼執(zhí)行結(jié)果如下所示:

Hello World
3

可以看到反序列化之后的函數(shù) add 復(fù)制到了 name 上,然后我們調(diào)用了函數(shù) name 真的實現(xiàn)了打印和相加的效果,從這一點來看確實實現(xiàn)了我們在前面所提到的效果。

Python 對象反序列化

在本節(jié)當(dāng)中將主要分析 python 對象序列化之后的二進(jìn)制文件格式,我們到底應(yīng)該如何解析這個文件,解析文件的規(guī)則是什么。在 cpython 當(dāng)中對于每個數(shù)據(jù)類型的解析都是不一樣的,marshal 支持 python 當(dāng)中所有的基本數(shù)據(jù)類型,額外還支持 CodeObject ,在上面的驗證代碼當(dāng)中我們已經(jīng)使用 marshal 去做了一些序列化和反序列化操作。

在對 python 對象進(jìn)行序列化的時候,每一個 python 對象主要是由兩個部分組成的:

其中 type 占一個字節(jié),用于表示接下里啊的 python 的對象類型,比如如字典、元組、集合之類的,而后面的 PyObject 就是實際的 python 數(shù)據(jù)類型了,需要注意的是對于 None、False、True 這種在虛擬機(jī)當(dāng)中只有一個備份的對象,PyObject 是沒有的,也就只有 type 這一個字段。

type 的種類具體如下所示,它只占用一個字節(jié):

class TYPE(Enum):
    TYPE_NULL                 = ord('0')
    TYPE_NONE                 = ord('N')
    TYPE_FALSE                = ord('F')
    TYPE_TRUE                 = ord('T')
    TYPE_STOPITER             = ord('S')
    TYPE_ELLIPSIS             = ord('.')
    TYPE_INT                  = ord('i')
    TYPE_INT64                = ord('I')
    TYPE_FLOAT                = ord('f')
    TYPE_BINARY_FLOAT         = ord('g')
    TYPE_COMPLEX              = ord('x')
    TYPE_BINARY_COMPLEX       = ord('y')
    TYPE_LONG                 = ord('l')
    TYPE_STRING               = ord('s')
    TYPE_INTERNED             = ord('t')
    TYPE_REF                  = ord('r')
    TYPE_TUPLE                = ord('(')
    TYPE_LIST                 = ord('[')
    TYPE_DICT                 = ord('{')
    TYPE_CODE                 = ord('c')
    TYPE_UNICODE              = ord('u')
    TYPE_UNKNOWN              = ord('?')
    TYPE_SET                  = ord('<')
    TYPE_FROZENSET            = ord('>')
    FLAG_REF                  = 0x80
    TYPE_ASCII                = ord('a')
    TYPE_ASCII_INTERNED       = ord('A')
    TYPE_SMALL_TUPLE          = ord(')')
    TYPE_SHORT_ASCII          = ord('z')
    TYPE_SHORT_ASCII_INTERNED = ord('Z')

我們接下來對上面的類型進(jìn)行一一解釋,首先我們需要了解下面幾個方法,我們在后面的解析過程當(dāng)中會使用到下面的內(nèi)容:

class ByteStreamReader(object):
    @staticmethod
    def read_int(buf: bytes):
        return struct.unpack("<i", buf)[0]
    @staticmethod
    def read_byte(buf):
        return struct.unpack("<B", buf)[0]
    @staticmethod
    def read_float(buf):
        return struct.unpack("<f", buf)[0]
    @staticmethod
    def read_double(buf):
        return struct.unpack("<d", buf)[0]
    @staticmethod
    def read_long(buf):
        return struct.unpack("<q", buf)[0]

上面的的幾個函數(shù)主要是將字節(jié)變成 byte、int 或者浮點數(shù)。接下來我們會實現(xiàn)一個類 PyObjectLoader,用于對 marshal 序列化之后的文件進(jìn)行解析。類的構(gòu)造函數(shù)如下所示:

class PyObjectLoader(object):
    def __init__(self, filename):
        self.fp = open(filename, "rb")
        self.flag = 0
        self.refs = []

現(xiàn)在來對一個對象進(jìn)行解析,根據(jù)我們前面談到的內(nèi)容首先我們需要讀入一個字節(jié)的內(nèi)容用于判斷是那種數(shù)據(jù)類型:

在上面的代碼當(dāng)中使用函數(shù) do_parse 對一個 python 對象進(jìn)行解析操作,使用到了 TYPE.FLAG_REF,這個字段的作用表示這個 python 對象是不是一個可引用的,除了 None 、True、False、StopIteration、Ellipsis 是不可引用對象,集合、字典、不可變集合、字符串、字節(jié)、CodeObject 等是可引用對象,可引用對象的 type 的最高位是 1(也就是 type 的第 8 個比特位是 1),非可引用對象就是 0 。如果是可引用對象需要將這個對象加入到引用列表當(dāng)中,因為可能會存在一個對象引用其他對象的情況,需要將對象加入到引用隊列當(dāng)中,如果需要對對象進(jìn)行引用操作直接使用下標(biāo)從引用數(shù)組當(dāng)中查找即可。所有的可引用對象在創(chuàng)建完成之后都需要加入到引用列表當(dāng)中。

  • TYPE_NULL,這個在 cpython 虛擬機(jī)當(dāng)中就會直接返回 NULL 。
  • TYPE_NONE,返回 python 對象 None 。
  • TYPE_FALSE,返回 python 對象 False 。
  • TYPE_TRUE,返回 python 對象 True 。
  • TYPE_STOPITER,返回 StopIteration 對象。
  • TYPE_ELLIPSIS,返回 對象 Ellipsis 。
  • TYPE_INT,如果是這個數(shù)據(jù)類型表示接下來的 4 個字節(jié)的數(shù)據(jù)是一個整數(shù)。
  • TYPE_INT64,這個類型表示接下來的 8 個字節(jié)表示一個整數(shù)。
  • TYPE_BINARY_FLOAT,浮點數(shù)對象,表示接下里啊的 8 個字節(jié)表示一個浮點數(shù)。
  • TYPE_BINARY_COMPLEX,復(fù)數(shù)對象,表示接下來有兩個 8 個字節(jié)的浮點數(shù),分別表示實部和虛部。
  • TYPE_STRING,這個表示一個 bytes 對象,接下來的四個字節(jié)表示一個整數(shù) size ,整數(shù) size 的含義表示還需要讀取的字節(jié)個數(shù),因此接下來的 size 個字節(jié)就是 bytes 對象的內(nèi)容。
  • TYPE_INTERNED,表示一個需要緩存到字符串常量池的字符串,解析方法和 TYPE_STRING 一樣首先讀取四個字節(jié)得到一個整數(shù) size,然后在讀取 size 個字節(jié),表示字符串的內(nèi)容,我們在 python 當(dāng)中可以直接使用.decode("utf-8") 進(jìn)行編碼。
  • TYPE_REF,表示需要引用一個對象,讀取四個字節(jié)作為整數(shù) size,然后從引用列表當(dāng)中獲取下標(biāo)為 size 的對象。
  • TYPE_TUPLE,表示一個元組,首先讀取四個字節(jié)的數(shù)據(jù)得到一個整數(shù) size ,然后使用 for 循環(huán)遞歸調(diào)用 do_parse 函數(shù)獲取 size 的對象。
  • TYPE_LIST,解析方式和 TYPE_TUPLE 一樣,只不過返回列表對象。
  • TYPE_DICT,這個解析的方式不斷的調(diào)用 do_parse 函數(shù),從 1 開始計數(shù),奇數(shù)對象當(dāng)作 key,偶數(shù)對象當(dāng)中 val,直到遇到 NULL,跳出循環(huán)停止解析,這個類型可以直接看下面的解析代碼,非常清晰。
  • TYPE_CODE,這個類型表示一個 CodeObject 對象,見下面的解析代碼,這部分代碼可以結(jié)合 CodeObject 的字段分析,前面24 個字節(jié)表示整數(shù)對象,用于表示 CodeObject 的 6 個字段,接下來的是 8 個 PyObject 對象,因此需要調(diào)用 do_parse 函數(shù)進(jìn)行解析,然后再解析一個 4 字節(jié)的整數(shù)表示第一行代碼的行號,最后再讀取一個 PyObject 對象。
  • TYPE_UNICODE,表示一個字符串,讀取方式和 TYPE_INTERNED 一樣。
  • TYPE_SET,前 4 個自己表示集合當(dāng)中元素的個數(shù) size,接下來使用 for 循環(huán)讀?。ㄕ{(diào)用 do_parse) size 的元素加入到集合當(dāng)中。
  • TYPE_FROZENSET,和 TYPE_SET 讀取方式一樣,只不過返回 frozen set 。
  • TYPE_ASCII,和 TYPE_UNICODE 讀取方式一樣,也可以使用 utf-8 編碼,雖然讀取的是 ASCII 編碼的字符,但是 utf-8 兼容 ASCII 因此也可以。
  • TYPE_ASCII_INTERNED,和 TYPE_ASCII 解析方式一樣。
  • TYPE_SMALL_TUPLE,讀取一個字節(jié)的數(shù)據(jù)表示元組當(dāng)中的數(shù)據(jù)個數(shù),然后讀取對應(yīng)個數(shù)的對象。
  • TYPE_SHORT_ASCII,之前是讀取四個字節(jié)作為長度,現(xiàn)在只讀取一個字節(jié)作為字節(jié)個數(shù)。
  • TYPE_SHORT_ASCII_INTERNED,和 TYPE_SHORT_ASCII 讀取方式一樣,只不過加入到字符串常量池子。

余下的對象的解析不在一一解釋,大家可以直接看下方代碼,都是比較清晰易懂的。

class PyObjectLoader(object):
    def __init__(self, filename):
        self.reader = ByteStreamReader()
        self.fp = open(filename, "rb")
        self.flag = 0
        self.refs = []
    def do_parse(self):
        c = self.fp.read(1)
        assert len(c) != 0, "can not read more data from file descriptor"
        t = ByteStreamReader.read_byte(c) & (~TYPE.FLAG_REF.value)
        self.flag = ByteStreamReader.read_byte(c) & TYPE.FLAG_REF.value
        match t:
            case TYPE.TYPE_NULL.value:
                return None
            case TYPE.TYPE_NONE.value:
                return None
            case TYPE.TYPE_FALSE.value:
                return False
            case TYPE.TYPE_TRUE.value:
                return True
            case TYPE.TYPE_STOPITER.value:
                return StopIteration
            case TYPE.TYPE_ELLIPSIS.value:
                return Ellipsis
            case TYPE.TYPE_INT.value:
                ret = ByteStreamReader.read_int(self.fp.read(4))
                self.refs.append(ret)
                return TYPE.TYPE_INT, ret
            case TYPE.TYPE_INT64.value:
                ret = ByteStreamReader.read_long(self.fp.read(8))
                self.refs.append(ret)
                return TYPE.TYPE_INT64, ret
            case TYPE.TYPE_FLOAT.value:
                raise RuntimeError("Unsupported TYPE TYPE_FLOAT")
            case TYPE.TYPE_BINARY_FLOAT.value:
                ret = ByteStreamReader.read_double(self.fp.read(8))
                self.refs.append(ret)
                return TYPE.TYPE_FLOAT, ret
            case TYPE.TYPE_COMPLEX.value:
                raise RuntimeError("Unsupported TYPE TYPE_COMPLEX")
            case TYPE.TYPE_BINARY_COMPLEX.value:
                ret = complex(self.do_parse(), self.do_parse())
                self.refs.append(ret)
                return TYPE.TYPE_BINARY_COMPLEX, ret
            case TYPE.TYPE_LONG.value:
                raise RuntimeError("Unsupported TYPE TYPE_LONG")
            case TYPE.TYPE_STRING.value:
                size = ByteStreamReader.read_int(self.fp.read(4))
                ret = self.fp.read(size)
                self.refs.append(ret)
                return TYPE.TYPE_STRING, ret
            case TYPE.TYPE_INTERNED.value:
                size = ByteStreamReader.read_int(self.fp.read(4))
                ret = self.fp.read(size).decode("utf-8")
                self.refs.append(ret)
                return TYPE.TYPE_INTERNED, ret
            case TYPE.TYPE_REF.value:
                size = ByteStreamReader.read_int(self.fp.read(4))
                return TYPE.TYPE_REF, self.refs[size]
            case TYPE.TYPE_TUPLE.value:
                size = ByteStreamReader.read_int(self.fp.read(4))
                ret = []
                self.refs.append(ret)
                for i in range(size):
                    ret.append(self.do_parse())
                return TYPE.TYPE_TUPLE, tuple(ret)
            case TYPE.TYPE_LIST.value:
                size = ByteStreamReader.read_int(self.fp.read(4))
                ret = []
                self.refs.append(ret)
                for i in range(size):
                    ret.append(self.do_parse())
                return TYPE.TYPE_LIST, ret
            case TYPE.TYPE_DICT.value:
                ret = dict()
                self.refs.append(ret)
                while True:
                    key = self.do_parse()
                    if key is None:
                        break
                    val = self.do_parse()
                    if val is None:
                        break
                    ret[key] = val
                return TYPE.TYPE_DICT, ret
            case TYPE.TYPE_CODE.value:
                ret = dict()
                idx = len(self.refs)
                self.refs.append(None)
                ret["argcount"] = ByteStreamReader.read_int(self.fp.read(4))
                ret["posonlyargcount"] = ByteStreamReader.read_int(self.fp.read(4))
                ret["kwonlyargcount"] = ByteStreamReader.read_int(self.fp.read(4))
                ret["nlocals"] = ByteStreamReader.read_int(self.fp.read(4))
                ret["stacksize"] = ByteStreamReader.read_int(self.fp.read(4))
                ret["flags"] = ByteStreamReader.read_int(self.fp.read(4))
                ret["code"] = self.do_parse()
                ret["consts"] = self.do_parse()
                ret["names"] = self.do_parse()
                ret["varnames"] = self.do_parse()
                ret["freevars"] = self.do_parse()
                ret["cellvars"] = self.do_parse()
                ret["filename"] = self.do_parse()
                ret["name"] = self.do_parse()
                ret["firstlineno"] = ByteStreamReader.read_int(self.fp.read(4))
                ret["lnotab"] = self.do_parse()
                self.refs[idx] = ret
                return TYPE.TYPE_CODE, ret
            case TYPE.TYPE_UNICODE.value:
                size = ByteStreamReader.read_int(self.fp.read(4))
                ret = self.fp.read(size).decode("utf-8")
                self.refs.append(ret)
                return TYPE.TYPE_INTERNED, ret
            case TYPE.TYPE_UNKNOWN.value:
                raise RuntimeError("Unknown value " + str(t))
            case TYPE.TYPE_SET.value:
                size = ByteStreamReader.read_int(self.fp.read(4))
                ret = set()
                self.refs.append(ret)
                for i in range(size):
                    ret.add(self.do_parse())
                return TYPE.TYPE_SET, ret
            case TYPE.TYPE_FROZENSET.value:
                size = ByteStreamReader.read_int(self.fp.read(4))
                ret = set()
                idx = len(self.refs)
                self.refs.append(None)
                for i in range(size):
                    ret.add(self.do_parse())
                self.refs[idx] = ret
                return TYPE.TYPE_SET, frozenset(ret)
            case TYPE.TYPE_ASCII.value:
                size = ByteStreamReader.read_int(self.fp.read(4))
                ret = self.fp.read(size).decode("utf-8")
                self.refs.append(ret)
                return TYPE.TYPE_INTERNED, ret
            case TYPE.TYPE_ASCII_INTERNED.value:
                size = ByteStreamReader.read_int(self.fp.read(4))
                ret = self.fp.read(size).decode("utf-8")
                self.refs.append(ret)
                return TYPE.TYPE_ASCII_INTERNED, ret
            case TYPE.TYPE_SMALL_TUPLE.value:
                size = ByteStreamReader.read_byte(self.fp.read(1))
                ret = []
                self.refs.append(ret)
                for i in range(size):
                    ret.append(self.do_parse())
                return TYPE.TYPE_SMALL_TUPLE, tuple(ret)
            case TYPE.TYPE_SHORT_ASCII.value:
                size = ByteStreamReader.read_byte(self.fp.read(1))
                ret = self.fp.read(size).decode("utf-8")
                self.refs.append(ret)
                return TYPE.TYPE_SHORT_ASCII, ret
            case TYPE.TYPE_SHORT_ASCII_INTERNED.value:
                size = ByteStreamReader.read_byte(self.fp.read(1))
                ret = self.fp.read(size).decode("utf-8")
                self.refs.append(ret)
                return TYPE.TYPE_SHORT_ASCII_INTERNED, ret
            case _:
                raise RuntimeError("can not parse " + str(t))
    def __del_(self):
        self.fp.close()

我們現(xiàn)在使用下面的代碼生成一些二進(jìn)制文件:

import marshal
def add(a, b):
    print("Hello World")
    return a+b
if __name__ == '__main__':
    with open("add.bin", "wb") as fp:
        marshal.dump(add.__code__, fp)
    with open("int.bin", "wb") as fp:
        marshal.dump(1, fp)
    with open("float.bin", "wb") as fp:
        marshal.dump(1.5, fp)
    with open("tuple.bin", "wb") as fp:
        marshal.dump((1, 2, 3), fp)
    with open("set.bin", "wb") as fp:
        marshal.dump({1, 2, 3}, fp)
    with open("list.bin", "wb") as fp:
        marshal.dump([1, 2, 3], fp)
    with open("dict.bin", "wb") as fp:
        marshal.dump({1: 2, 3: 4}, fp)
    with open("code.bin", "wb") as fp:
        marshal.dump(add.__code__, fp)
    with open("string.bin", "wb") as fp:
        marshal.dump("Hello World", fp)

當(dāng)我們使用 marshal 對函數(shù) add 的 code 進(jìn)行序列化的時候?qū)嶋H上就是序列化一個 CodeObject 對象,這個對象的結(jié)果實際上和 pyc 的結(jié)構(gòu)是一樣的。

我們使用下面的代碼進(jìn)行反序列化:

if __name__ == '__main__':
    assert sys.version_info.major == 3 and sys.version_info.minor == 10, "only python3.10 works"
    loader = PyObjectLoader("int.bin")
    print(loader.do_parse())
    loader = PyObjectLoader("float.bin")
    print(loader.do_parse())
    loader = PyObjectLoader("set.bin")
    print(loader.do_parse())
    loader = PyObjectLoader("dict.bin")
    print(loader.do_parse())
    loader = PyObjectLoader("tuple.bin")
    print(loader.do_parse())
    loader = PyObjectLoader("list.bin")
    print(loader.do_parse())
    loader = PyObjectLoader("string.bin")
    print(loader.do_parse())
    loader = PyObjectLoader("code.bin")
    pprint(loader.do_parse())

需要注意的是本篇文章代碼需要在 python 3.10 上運行,如果需要在 3.8 3.9 運行的話可以將 match 語句改成 if-else 語句。但是由于 python 3.11 當(dāng)中的 CodeObject 對象的字段發(fā)生了一些微小的變化,因此上面的代碼是不能在 python 3.11 上執(zhí)行的。上面的代碼執(zhí)行結(jié)果如下所示:

(<TYPE.TYPE_INT: 105>, 1)
(<TYPE.TYPE_FLOAT: 102>, 1.5)
(<TYPE.TYPE_SET: 60>, {(<TYPE.TYPE_INT: 105>, 1), (<TYPE.TYPE_INT: 105>, 2), (<TYPE.TYPE_INT: 105>, 3)})
(<TYPE.TYPE_DICT: 123>, {(<TYPE.TYPE_INT: 105>, 1): (<TYPE.TYPE_INT: 105>, 2), (<TYPE.TYPE_INT: 105>, 3): (<TYPE.TYPE_INT: 105>, 4)})
(<TYPE.TYPE_SMALL_TUPLE: 41>, ((<TYPE.TYPE_INT: 105>, 1), (<TYPE.TYPE_INT: 105>, 2), (<TYPE.TYPE_INT: 105>, 3)))
(<TYPE.TYPE_LIST: 91>, [(<TYPE.TYPE_INT: 105>, 1), (<TYPE.TYPE_INT: 105>, 2), (<TYPE.TYPE_INT: 105>, 3)])
(<TYPE.TYPE_SHORT_ASCII: 122>, 'Hello World')
(<TYPE.TYPE_CODE: 99>,
 {'argcount': 2,
  'cellvars': (<TYPE.TYPE_REF: 114>, 'print'),
  'code': (<TYPE.TYPE_STRING: 115>,
           b't\x00d\x01\x83\x01\x01\x00|\x00|\x01\x17\x00S\x00'),
  'consts': (<TYPE.TYPE_SMALL_TUPLE: 41>,
             (None, (<TYPE.TYPE_SHORT_ASCII: 122>, 'Hello World'))),
  'filename': (<TYPE.TYPE_SHORT_ASCII: 122>,
               '/Users/xxxxxxx/Desktop/workdir/dive-into-cpython/code/marshal_demos/add.py'),
  'firstlineno': 5,
  'flags': 67,
  'freevars': (<TYPE.TYPE_SMALL_TUPLE: 41>, ()),
  'kwonlyargcount': 0,
  'lnotab': (<TYPE.TYPE_STRING: 115>, b'\x08\x01\x08\x01'),
  'name': (<TYPE.TYPE_SHORT_ASCII_INTERNED: 90>, 'add'),
  'names': (<TYPE.TYPE_SMALL_TUPLE: 41>,
            ((<TYPE.TYPE_SHORT_ASCII_INTERNED: 90>, 'print'),)),
  'nlocals': 2,
  'posonlyargcount': 0,
  'stacksize': 2,
  'varnames': (<TYPE.TYPE_SMALL_TUPLE: 41>,
               ((<TYPE.TYPE_SHORT_ASCII_INTERNED: 90>, 'a'),
                (<TYPE.TYPE_SHORT_ASCII_INTERNED: 90>, 'b')))})

從上面的解析結(jié)果來看我們是實現(xiàn)了正確的解析的。

總結(jié)

在本篇文章當(dāng)中主要給大家分析了 python 對象序列化之后我們該如何反序列化這些對象,并且使用 python 對二進(jìn)制文件進(jìn)行了分析,可以成功的將 python 對象解析出來,但是我們忽略了兩個稍微復(fù)雜一點的對象,他們的解析稍微有點復(fù)雜,但是我們平時的變成當(dāng)中很少使用到,因此本文的代碼解析一般的文件都是可以的。

到此這篇關(guān)于深入理解Python虛擬機(jī)中的反序列化pyc文件的文章就介紹到這了,更多相關(guān)Python虛擬機(jī)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python 批量合并多個txt文件的實例講解

    Python 批量合并多個txt文件的實例講解

    今天小編就為大家分享一篇Python 批量合并多個txt文件的實例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • python繪制三維圖的詳細(xì)新手教程

    python繪制三維圖的詳細(xì)新手教程

    通常我們用 Python 繪制的都是二維平面圖,但有時也需要繪制三維場景圖,下面這篇文章主要給大家介紹了關(guān)于python繪制三維圖的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • Python中的作用域==和is的區(qū)別及說明

    Python中的作用域==和is的區(qū)別及說明

    這篇文章主要介紹了Python中的作用域==和is的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • python 如何去除字符串頭尾的多余符號

    python 如何去除字符串頭尾的多余符號

    這篇文章主要介紹了python 如何去除字符串頭尾的多余符號,在python開發(fā)中經(jīng)常會遇到這樣的需求,今天小編通過實例代碼給大家講解,需要的朋友可以參考下
    2019-11-11
  • 火車票搶票python代碼公開揭秘!

    火車票搶票python代碼公開揭秘!

    火車票搶票python代碼公開揭秘!使用Python模擬搶票程序,給大家揭秘?fù)屍钡降资窃趺椿厥拢信d趣的小伙伴們可以參考一下
    2018-03-03
  • python妙用之編碼的轉(zhuǎn)換詳解

    python妙用之編碼的轉(zhuǎn)換詳解

    在日常滲透,漏洞挖掘,甚至是CTF比賽中會遇到各種編碼,常常伴隨著這些編碼之間的各種轉(zhuǎn)換。下面這篇文章主要介紹了python中編碼轉(zhuǎn)換妙用的相關(guān)資料,需要的朋友們可以參考借鑒,下面來一起看看吧。
    2017-04-04
  • 使用Python內(nèi)置的模塊與函數(shù)進(jìn)行不同進(jìn)制的數(shù)的轉(zhuǎn)換

    使用Python內(nèi)置的模塊與函數(shù)進(jìn)行不同進(jìn)制的數(shù)的轉(zhuǎn)換

    這篇文章主要介紹了使用Python內(nèi)置的模塊與函數(shù)進(jìn)行不同進(jìn)制的數(shù)的轉(zhuǎn)換的方法,Python也使得讀取純二進(jìn)制文件內(nèi)容非常方便,需要的朋友可以參考下
    2016-03-03
  • python Django連接MySQL數(shù)據(jù)庫做增刪改查

    python Django連接MySQL數(shù)據(jù)庫做增刪改查

    本文寫的是python Django連接MySQL數(shù)據(jù)庫的步驟,提供增刪改查的代碼
    2013-11-11
  • OpenCV-Python直方圖均衡化實現(xiàn)圖像去霧

    OpenCV-Python直方圖均衡化實現(xiàn)圖像去霧

    直方圖均衡化可以達(dá)到增強(qiáng)圖像顯示效果的目的。最常用的比如去霧。本文就來實現(xiàn)直方圖均衡化實現(xiàn)圖像去霧,感興趣的可以了解一下
    2021-06-06
  • torchxrayvision包安裝過程(附pytorch1.6cpu版安裝)

    torchxrayvision包安裝過程(附pytorch1.6cpu版安裝)

    這篇文章主要介紹了torchxrayvision包安裝過程(附pytorch1.6cpu版安裝),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08

最新評論