【问题标题】:Given a python .pyc file, is there a tool that let me view the bytecode?给定一个 python .pyc 文件,是否有一个工具可以让我查看字节码?
【发布时间】:2012-06-23 21:04:42
【问题描述】:

CPython 解释器会自动将 Python 模块编译成 .pyc 文件。包含字节码的 .pyc 文件是二进制格式(编组代码?)。是否有 GUI(或命令行)工具可以让我查看字节码?

【问题讨论】:

  • 可以导入模块吗?这当然会执行它。
  • 你能澄清一下“查看字节码”吗?
  • 你想将其反编译成python代码(.py)还是要了解每个字节的含义?
  • 我想看看和研究字节码。我特别想知道模块全局对象是如何表示的。
  • 做了一点谷歌搜索,我发现 Python 带有一个名为“dis”的模块,可以让我这样做。 “导入 dis;导入 mymodule;dis.dis(module)”。仍然想知道是否有用于此的 GUI 工具。

标签: python bytecode pyc


【解决方案1】:

有一个名为 PyChrisanthemum 的可视化 Python 反汇编程序。

如 OP 已经发现的那样,您可以使用模块 dis (python 2.7.3, python 3.2.3) 以命令行方式执行此操作。

【讨论】:

  • 这个项目现在已经死了,所以我不希望它能够(完美地)读取最近 Python 版本生成的字节码。
【解决方案2】:

每个 *.pyc 文件都是一个包含以下内容的二进制文件:

  • 一个四字节的幻数 - 它只是随着编组代码的每次更改而更改的字节;
  • 四字节修改时间戳 - 是生成 .pyc 的源文件的 Unix 修改时间戳,以便在源更改时重新编译;
  • 从 Python3.3+ 版本开始,接下来的四个字节是将源文件的大小编码为 long 的字段;
  • 编组的代码对象。

为什么不直接使用 CPython 的内置功能来完成这项任务?


一个文件view_pyc_file.py

import platform
import time
import sys
import binascii
import marshal
import dis
import struct


def view_pyc_file(path):
    """Read and display a content of the Python`s bytecode in a pyc-file."""

    file = open(path, 'rb')

    magic = file.read(4)
    timestamp = file.read(4)
    size = None

    if sys.version_info.major == 3 and sys.version_info.minor >= 3:
        size = file.read(4)
        size = struct.unpack('I', size)[0]

    code = marshal.load(file)

    magic = binascii.hexlify(magic).decode('utf-8')
    timestamp = time.asctime(time.localtime(struct.unpack('I', b'D\xa5\xc2X')[0]))

    dis.disassemble(code)

    print('-' * 80)
    print(
        'Python version: {}\nMagic code: {}\nTimestamp: {}\nSize: {}'
        .format(platform.python_version(), magic, timestamp, size)
    )

    file.close()


if __name__ == '__main__':
    view_pyc_file(sys.argv[1])

用下一个 CPython 版本测试:

  • 2.7.9
  • 3.4.2
  • 3.5.2

演示

文件内容main.py

$ cat main.py
print("Never give up")

通过python2.7创建和读取pyc文件

setivolkylany$~/Downloads/temp/temp$ python2.7 -m py_compile main.py 
setivolkylany$~/Downloads/temp/temp$ python2.7 view_pyc_file.py ./main.pyc
  1           0 LOAD_CONST               0 ('Never give up')
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       
              5 LOAD_CONST               1 (None)
              8 RETURN_VALUE        
--------------------------------------------------------------------------------
Python version: 2.7.9
Magic code: 03f30d0a
Timestamp: Fri Mar 10 15:08:20 2017
Size: None

通过python3.4创建和读取pyc文件

setivolkylany$~/Downloads/temp/temp$ python3.4 -m py_compile main.py 
setivolkylany$~/Downloads/temp/temp$ python3.4 view_pyc_file.py __pycache__/main.cpython-34.pyc 
  1           0 LOAD_NAME                0 (print)
              3 LOAD_CONST               0 ('Never give up')
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
              9 POP_TOP
             10 LOAD_CONST               1 (None)
             13 RETURN_VALUE
--------------------------------------------------------------------------------
Python version: 3.4.2
Magic code: ee0c0d0a
Timestamp: Fri Mar 10 15:08:20 2017
Size: 23

通过python3.5创建和读取pyc文件

setivolkylany$~/Downloads/temp/temp$ python3.5 -m py_compile main.py 
setivolkylany$~/Downloads/temp/temp$ python3.5 view_pyc_file.py __pycache__/main.cpython-35.pyc 
  1           0 LOAD_NAME                0 (print)
              3 LOAD_CONST               0 ('Never give up')
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
              9 POP_TOP
             10 LOAD_CONST               1 (None)
             13 RETURN_VALUE
--------------------------------------------------------------------------------
Python version: 3.5.2
Magic code: 160d0d0a
Timestamp: Fri Mar 10 15:08:20 2017
Size: 23

基于:

【讨论】:

  • 现在需要更新 python.org/dev/peps/pep-0552 (Python 3.7)。
  • 我无法在 cmets 中添加所需的修改,因此我将其添加为新答案,但@PADYMKO 请随时使用它编辑您的答案。
【解决方案3】:

根据@Apteryx 关于 PEP 的注释扩展代码形式 @PADYMKO:

def view_pyc_file(path):
    """Read and display a content of the Python`s bytecode in a pyc-file."""

    file = open(path, 'rb')

    magic = file.read(4)
    bit_field = None
    timestamp = None
    hashstr = None
    size = None

    if sys.version_info.major == 3 and sys.version_info.minor >=7:
        bit_field = int.from_bytes(file.read(4), byteorder=sys.byteorder)
        if 1 & bit_field == 1:
            hashstr = file.read(8)
        else:
            timestamp = file.read(4)
            size = file.read(4)
            size = struct.unpack('I', size)[0]
    elif sys.version_info.major == 3 and sys.version_info.minor >= 3:
        timestamp = file.read(4)
        size = file.read(4)
        size = struct.unpack('I', size)[0]
    else:
        timestamp = file.read(4)

    code = marshal.load(file)

    magic = binascii.hexlify(magic).decode('utf-8')
    timestamp = time.asctime(time.localtime(struct.unpack('I', b'D\xa5\xc2X')[0]))

    dis.disassemble(code)

    print('-' * 80)
    print(
        'Python version: {}\nMagic code: {}\nTimestamp: {}\nSize: {}\nHash: {}\nBitfield: {}'
        .format(platform.python_version(), magic, timestamp, size, hashstr, bit_field)
    )

    file.close()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-17
    • 1970-01-01
    • 2013-05-22
    • 2011-03-19
    • 1970-01-01
    • 2021-06-07
    • 2011-05-27
    • 2022-01-07
    相关资源
    最近更新 更多