最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

marshal 模塊的魔力

序列化和反序列化 python 對象

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

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

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)

在上面的代碼當中需要注意的是需要使用二進制方式 rb 打開文件,上面的程序執(zhí)行完成之后會生成一個 pyobjects.bin 的二進制文件,我們可以使用 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é)果我們可以看到我們可以將所有的被寫入到二進制文件當中的數(shù)據(jù)全部解析了出來。

序列化和反序列化 CodeObject

除了上面使用 marshal 對 python 的基本對象進行序列化和反序列化,我們可以使用 marshal 模塊對 CodeObject 進行同樣的操作,如果是這樣的話,那么就可以將一個文件的代碼序列化,然后另外一個程序反序列化再進行調(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)

在上面的代碼當中,我們打開了文件 add.bin 然后將 add 函數(shù)的 CodeObject 對象寫入到文件當中去,而 CodeObject 當中保存了函數(shù) add 的所有執(zhí)行所需要的信息,因此我們可以在另外一個文件當中打開這個文件,然后將 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 復制到了 name 上,然后我們調(diào)用了函數(shù) name 真的實現(xiàn)了打印和相加的效果,從這一點來看確實實現(xiàn)了我們在前面所提到的效果。

Python 對象反序列化

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

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

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

我們接下來對上面的類型進行一一解釋,首先我們需要了解下面幾個方法,我們在后面的解析過程當中會使用到下面的內(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 序列化之后的文件進行解析。類的構造函數(shù)如下所示:

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

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

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

  • TYPE_NULL,這個在 cpython 虛擬機當中就會直接返回 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,復數(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 當中可以直接使用.decode("utf-8") 進行編碼。
  • TYPE_REF,表示需要引用一個對象,讀取四個字節(jié)作為整數(shù) size,然后從引用列表當中獲取下標為 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ù)對象當作 key,偶數(shù)對象當中 val,直到遇到 NULL,跳出循環(huán)停止解析,這個類型可以直接看下面的解析代碼,非常清晰。
  • TYPE_CODE,這個類型表示一個 CodeObject 對象,見下面的解析代碼,這部分代碼可以結(jié)合 CodeObject 的字段分析,前面24 個字節(jié)表示整數(shù)對象,用于表示 CodeObject 的 6 個字段,接下來的是 8 個 PyObject 對象,因此需要調(diào)用 do_parse 函數(shù)進行解析,然后再解析一個 4 字節(jié)的整數(shù)表示第一行代碼的行號,最后再讀取一個 PyObject 對象。
  • TYPE_UNICODE,表示一個字符串,讀取方式和 TYPE_INTERNED 一樣。
  • TYPE_SET,前 4 個自己表示集合當中元素的個數(shù) size,接下來使用 for 循環(huán)讀取(調(diào)用 do_parse) size 的元素加入到集合當中。
  • 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ù)表示元組當中的數(shù)據(jù)個數(shù),然后讀取對應個數(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)在使用下面的代碼生成一些二進制文件:

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)

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

我們使用下面的代碼進行反序列化:

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 當中的 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é)

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

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

相關文章

  • Python進行指數(shù)和對數(shù)曲線擬合詳解

    Python進行指數(shù)和對數(shù)曲線擬合詳解

    曲線擬合是構造曲線或數(shù)學函數(shù)的過程,其具有對一系列數(shù)據(jù)點的最佳擬合,可能受到約束,本文主要介紹了如何使用Python實現(xiàn)指數(shù)和對數(shù)曲線擬合,需要的可以參考下
    2024-04-04
  • pandas中fillna()函數(shù)填充NaN和None的實現(xiàn)

    pandas中fillna()函數(shù)填充NaN和None的實現(xiàn)

    本文主要介紹了pandas中fillna()函數(shù)填充NaN和None的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-01-01
  • Python docx庫用法示例分析

    Python docx庫用法示例分析

    這篇文章主要介紹了Python docx庫用法,結(jié)合實例形式分析了docx庫相關的docx文件讀取、文本添加、格式操作,需要的朋友可以參考下
    2019-02-02
  • python調(diào)用其他文件函數(shù)或類的示例

    python調(diào)用其他文件函數(shù)或類的示例

    今天小編就為大家分享一篇python調(diào)用其他文件函數(shù)或類的示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-07-07
  • Python轉(zhuǎn)為C語言并編譯生成二進制文件的教程詳解

    Python轉(zhuǎn)為C語言并編譯生成二進制文件的教程詳解

    這篇文章主要為大家詳細介紹了將Python轉(zhuǎn)為C語言并編譯生成二進制文件的相關教程,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下
    2023-12-12
  • python 實現(xiàn)刪除文件或文件夾實例詳解

    python 實現(xiàn)刪除文件或文件夾實例詳解

    這篇文章主要介紹了python 實現(xiàn)刪除文件或文件夾實例詳解的相關資料,這里附有實例代碼,需要的朋友可以參考下
    2016-12-12
  • python?subprocess執(zhí)行外部命令常用方法詳細舉例

    python?subprocess執(zhí)行外部命令常用方法詳細舉例

    這篇文章主要給大家介紹了關于python?subprocess執(zhí)行外部命令常用方法的相關資料,Python的subprocess模塊提供了一種在Python中調(diào)用外部命令的方法,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2023-12-12
  • 解決pytorch 數(shù)據(jù)類型報錯的問題

    解決pytorch 數(shù)據(jù)類型報錯的問題

    這篇文章主要介紹了解決pytorch 數(shù)據(jù)類型報錯的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • python 多線程中join()的作用

    python 多線程中join()的作用

    這篇文章主要介紹了python 多線程中join()的作用,通過代碼實踐來加深對 join()的認識,幫助大家更好的理解和學習python 多線程,感興趣的朋友可以了解下
    2020-10-10
  • python3實現(xiàn)彈彈球小游戲

    python3實現(xiàn)彈彈球小游戲

    這篇文章主要介紹了python3實現(xiàn)彈彈球小游戲,圖形用戶界面tkinter,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-11-11

最新評論

泰顺县| 梨树县| 章丘市| 裕民县| 玛多县| 柘城县| 阳谷县| 巴里| 诸城市| 南通市| 杨浦区| 德江县| 天台县| 平度市| 南充市| 鄂尔多斯市| 陆川县| 二连浩特市| 澎湖县| 美姑县| 鲁甸县| 阿图什市| 锦州市| 乐亭县| 西城区| 梅河口市| 铜梁县| 柞水县| 固原市| 遂宁市| 喜德县| 衢州市| 武平县| 丹寨县| 昭通市| 云浮市| 灵寿县| 贡觉县| 灵山县| 吉木萨尔县| 防城港市|