pyc文件产生

  1. Python 是一种解释型、面向对象、动态数据类型的高级程序设计语言。;
  2. Python是解释型语言,没有严格意义上的编译和汇编过程。但是一般编写好的.py源文件,由python解释器翻译成以.pyc为结尾的字节码文件,该文件可由python虚拟机直接运行;
    3.pyc的内容,是跟python的版本相关的,不同版本编译后的pyc文件是不同的,2.5编译的pyc文件,2.4版本的python是无法执行的;
  3. 根据python源码中提供的opcode,可以根据pyc文件反编译出py文件源码;
  4. 加载模块时,如果同时存在.py和.pyc,Python会尝试使用.pyc,如果.pyc的编译时间早于.py的修改时间,则重新编译.py并更新.pyc;

PyCodeObject结构

Python代码的编译结果就是PyCodeObject对象。PyCodeObject对象可以由虚拟机加载后直接运行,而pyc文件就是PyCodeObject对象在硬盘上的保存形式。

PyCodeObject对象一般包含的属性名称及数据类型如下,每个属性在虚拟机执行pyc文件时都有其作用。

typedef struct {
    PyObject_HEAD
    int co_argcount;        /* 位置参数个数 */
    int co_nlocals;         /* 局部变量个数 */
    int co_stacksize;       /* 栈大小 */
    int co_flags;   
    PyObject *co_code;      /* 字节码指令序列 */
    PyObject *co_consts;    /* 所有常量集合 */
    PyObject *co_names;     /* 所有符号名称集合 */
    PyObject *co_varnames;  /* 局部变量名称集合 */
    PyObject *co_freevars;  /* 闭包用的的变量名集合 */
    PyObject *co_cellvars;  /* 内部嵌套函数引用的变量名集合 */
    /* The rest doesn’t count for hash/cmp */
    PyObject *co_filename;  /* 代码所在文件名 */
    PyObject *co_name;      /* 模块名|函数名|类名 */
    int co_firstlineno;     /* 代码块在文件中的起始行号 */
    PyObject *co_lnotab;    /* 字节码指令和行号的对应关系 */
    void *co_zombieframe;   /* for optimization only (see frameobject.c) */
} PyCodeObject;

python中使用marshal.dump的方法将PyCodeObject对象转化为对应的二进制文件结构。PyCodeObject对象中的每一个属性及值都会按照一定的顺序表示在二进制文件里。

PyCodeObject每个字段在二进制文件中的结构如图1:
PyCodeObject每个字段在二进制文件中的结构

byte表示占用1个字节,long表示占用4个字节,bytes表示该字段可能占用1到多个字节。

pyc文件结构主要包括两部分:pyc文件头部表示和PyCodeObject对象部分。上述即PyCodeObject对象的二进制部分,pyc文件头部较简单,在python2中只占用4个字节包含两个字段magic和mtime,完整的pyc文件结构见图2:
完整的pyc文件结构

python提供内置的类库实现把py文件编译为pyc文件, py_compile 模块。
```javascript
# -*- coding: cp936 -*-
#python 27
#办法一:IDLE(实现见图3)
import py_compile #加r前缀进行转义
py_compile.compile(r'D:\test.py') #py文件完整的路径.
办法二:#cmd命令符下操作步骤
1、打开cmd,切换到 cd c:\\python34
     1)python -m py_compile D:\test.py   #跟随完整路径
     2)python -m py_compile /root/src/{file1,file2}.py   #这是同时转换多个文件
3、会在需转译文件的目录下生成一个“__pycache__”目录/test.cpython-34.pyc文件
#-m 相当于脚本中的import,这里的-m py_compile 相当于上面的 import py_compile

打开题目pyc文件,依照上述分析文件结构,找到PyCodeObjectData

>>> f = open('8.pyc','rb')
>>> b_data = f.read()
>>> PyCodeObjectData = b_data[8:]
>>> b_data
'\x03\xf3\r\n[9\xd6_c\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00@\x00\x00\x00s)\x00\x00\x00q\x06\x00d\x90\x00d\x00\x00\x84\x00\x00Z\x00\x00e\x01\x00d\x01\x00k\x02\x00r\x1f\x00e\x00\x00\x83\x00\x00\x01n\x00\x00d\x02\x00S(\x03\x00\x00\x00c\x00\x00\x00\x00\x04\x00\x00\x00+\x00\x00\x00C\x00\x00\x00sD\x01\x00\x00t\x00\x00d\x01\x00\x83\x01\x00}\x00\x00d\x02\x00g\x01\x00d\x03\x00\x14}\x01\x00d\x04\x00d\x05\x00d\x06\x00d\x07\x00d\x08\x00d\t\x00d\n\x00d\x0b\x00d\x0b\x00d\x0c\x00d\r\x00d\x0e\x00d\x0c\x00d\x0f\x00d\x10\x00d\x11\x00d\x0f\x00d\x12\x00d\r\x00d\x10\x00d\n\x00d\x0e\x00d\x13\x00d\x0e\x00d\x10\x00d\x13\x00d\r\x00d\x14\x00d\x0c\x00d\x10\x00d\x15\x00d\n\x00d\x12\x00d\x14\x00d\x11\x00d\x16\x00d\x17\x00d\x18\x00d\x14\x00d\x14\x00d\x17\x00d\x0b\x00d\x19\x00g+\x00}\x02\x00t\x01\x00|\x00\x00\x83\x01\x00d\x03\x00k\x03\x00r\xc1\x00d\x1a\x00GHt\x02\x00\x83\x00\x00\x01n\x00\x00xw\x00t\x03\x00t\x01\x00|\x00\x00\x83\x01\x00\x83\x01\x00D]c\x00}\x03\x00t\x04\x00|\x00\x00|\x03\x00\x19\x83\x01\x00d\x1b\x00A|\x01\x00|\x03\x00<|\x01\x00|\x03\x00\x19d\x1c\x00?|\x01\x00|\x03\x00\x19d\x1c\x00>Ad\x1d\x00@|\x01\x00|\x03\x00<|\x01\x00|\x03\x00\x19|\x02\x00|\x03\x00\x19k\x03\x00r\xd4\x00d\x1e\x00GHt\x02\x00\x83\x00\x00\x01q\xd4\x00q\xd4\x00Wd\x1f\x00GHd\x00\x00S( \x00\x00\x00Ns\x14\x00\x00\x00plz input your flag:i\x00\x00\x00\x00i+\x00\x00\x00i\x87\x00\x00\x00i\x97\x00\x00\x00i\x17\x00\x00\x00if\x00\x00\x00iG\x00\x00\x00i\x94\x00\x00\x00i`\x00\x00\x00iP\x00\x00\x00i \x00\x00\x00i\x10\x00\x00\x00i\x05\x00\x00\x00ie\x00\x00\x00i\xf1\x00\x00\x00i\xa0\x00\x00\x00ip\x00\x00\x00i\xb0\x00\x00\x00iE\x00\x00\x00i5\x00\x00\x00i\x15\x00\x00\x00iu\x00\x00\x00i0\x00\x00\x00i\xf4\x00\x00\x00t\x05\x00\x00\x00errori2\x00\x00\x00i\x04\x00\x00\x00i\xff\x00\x00\x00t\x05\x00\x00\x00wrongt\x04\x00\x00\x00true(\x05\x00\x00\x00t\t\x00\x00\x00raw_inputt\x03\x00\x00\x00lent\x04\x00\x00\x00exitt\x05\x00\x00\x00ranget\x03\x00\x00\x00ord(\x04\x00\x00\x00t\x01\x00\x00\x00at\x04\x00\x00\x00tempt\x06\x00\x00\x00resultt\x01\x00\x00\x00i(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00\xd4\xb4\xc2\xeb.pyt\x05\x00\x00\x00check\x02\x00\x00\x00s\x1a\x00\x00\x00\x00\x01\x0c\x01\r\x01\x87\x02\x12\x01\x05\x01\n\x01\x19\x01\x18\x01"\x01\x14\x01\x05\x01\x0e\x01t\x08\x00\x00\x00__main__N(\x02\x00\x00\x00R\x0c\x00\x00\x00t\x08\x00\x00\x00__name__(\x00\x00\x00\x00(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00\xd4\xb4\xc2\xeb.pyt\x08\x00\x00\x00<module>\x02\x00\x00\x00s\x04\x00\x00\x00\t\x10\x0c\x01'
>>> b_data[0:8] // 编译版本号和修改时间
'\x03\xf3\r\n[9\xd6_'
>>> PyCodeObjectData
'c\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00@\x00\x00\x00s)\x00\x00\x00q\x06\x00d\x90\x00d\x00\x00\x84\x00\x00Z\x00\x00e\x01\x00d\x01\x00k\x02\x00r\x1f\x00e\x00\x00\x83\x00\x00\x01n\x00\x00d\x02\x00S(\x03\x00\x00\x00c\x00\x00\x00\x00\x04\x00\x00\x00+\x00\x00\x00C\x00\x00\x00sD\x01\x00\x00t\x00\x00d\x01\x00\x83\x01\x00}\x00\x00d\x02\x00g\x01\x00d\x03\x00\x14}\x01\x00d\x04\x00d\x05\x00d\x06\x00d\x07\x00d\x08\x00d\t\x00d\n\x00d\x0b\x00d\x0b\x00d\x0c\x00d\r\x00d\x0e\x00d\x0c\x00d\x0f\x00d\x10\x00d\x11\x00d\x0f\x00d\x12\x00d\r\x00d\x10\x00d\n\x00d\x0e\x00d\x13\x00d\x0e\x00d\x10\x00d\x13\x00d\r\x00d\x14\x00d\x0c\x00d\x10\x00d\x15\x00d\n\x00d\x12\x00d\x14\x00d\x11\x00d\x16\x00d\x17\x00d\x18\x00d\x14\x00d\x14\x00d\x17\x00d\x0b\x00d\x19\x00g+\x00}\x02\x00t\x01\x00|\x00\x00\x83\x01\x00d\x03\x00k\x03\x00r\xc1\x00d\x1a\x00GHt\x02\x00\x83\x00\x00\x01n\x00\x00xw\x00t\x03\x00t\x01\x00|\x00\x00\x83\x01\x00\x83\x01\x00D]c\x00}\x03\x00t\x04\x00|\x00\x00|\x03\x00\x19\x83\x01\x00d\x1b\x00A|\x01\x00|\x03\x00<|\x01\x00|\x03\x00\x19d\x1c\x00?|\x01\x00|\x03\x00\x19d\x1c\x00>Ad\x1d\x00@|\x01\x00|\x03\x00<|\x01\x00|\x03\x00\x19|\x02\x00|\x03\x00\x19k\x03\x00r\xd4\x00d\x1e\x00GHt\x02\x00\x83\x00\x00\x01q\xd4\x00q\xd4\x00Wd\x1f\x00GHd\x00\x00S( \x00\x00\x00Ns\x14\x00\x00\x00plz input your flag:i\x00\x00\x00\x00i+\x00\x00\x00i\x87\x00\x00\x00i\x97\x00\x00\x00i\x17\x00\x00\x00if\x00\x00\x00iG\x00\x00\x00i\x94\x00\x00\x00i`\x00\x00\x00iP\x00\x00\x00i \x00\x00\x00i\x10\x00\x00\x00i\x05\x00\x00\x00ie\x00\x00\x00i\xf1\x00\x00\x00i\xa0\x00\x00\x00ip\x00\x00\x00i\xb0\x00\x00\x00iE\x00\x00\x00i5\x00\x00\x00i\x15\x00\x00\x00iu\x00\x00\x00i0\x00\x00\x00i\xf4\x00\x00\x00t\x05\x00\x00\x00errori2\x00\x00\x00i\x04\x00\x00\x00i\xff\x00\x00\x00t\x05\x00\x00\x00wrongt\x04\x00\x00\x00true(\x05\x00\x00\x00t\t\x00\x00\x00raw_inputt\x03\x00\x00\x00lent\x04\x00\x00\x00exitt\x05\x00\x00\x00ranget\x03\x00\x00\x00ord(\x04\x00\x00\x00t\x01\x00\x00\x00at\x04\x00\x00\x00tempt\x06\x00\x00\x00resultt\x01\x00\x00\x00i(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00\xd4\xb4\xc2\xeb.pyt\x05\x00\x00\x00check\x02\x00\x00\x00s\x1a\x00\x00\x00\x00\x01\x0c\x01\r\x01\x87\x02\x12\x01\x05\x01\n\x01\x19\x01\x18\x01"\x01\x14\x01\x05\x01\x0e\x01t\x08\x00\x00\x00__main__N(\x02\x00\x00\x00R\x0c\x00\x00\x00t\x08\x00\x00\x00__name__(\x00\x00\x00\x00(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00\xd4\xb4\xc2\xeb.pyt\x08\x00\x00\x00<module>\x02\x00\x00\x00s\x04\x00\x00\x00\t\x10\x0c\x01'
>>> import marshal
>>> Pyobj = marshal.loads(PyCodeObjectData)
>>> Pyobj
<code object <module> at 0000000002CCA7B0, file "源码.py", line 2>
>>> dir(Pyobj)
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
>>> Pyobj
<code object <module> at 0000000002CCA7B0, file "源码.py", line 2>
>>> Pyobj. co_argcount
>>> Pyobj. co_nlocals
>>> Pyobj. co_filename
'\xd4\xb4\xc2\xeb.py'
>>> Pyobj. co_name
'<module>'
>>> Pyobj. co_names
('check', '__name__')
>>> Pyobj. co_code
'q\x06\x00d\x90\x00d\x00\x00\x84\x00\x00Z\x00\x00e\x01\x00d\x01\x00k\x02\x00r\x1f\x00e\x00\x00\x83\x00\x00\x01n\x00\x00d\x02\x00S'

使用python内置模块dis可以对PyCodeObject进行反编译,从而获取到python二进制字节码代码段的“汇编形式”。这样可以便于对字节码进行阅读。dis模块也可以单独对PyCodeObject中的co_data模块进行反编译,但是这样得到的是单纯的代码段字节码,缺少很多代码段中涉及的变量名字。

>>> import dis
>>> dis.dis(Pyobj)
  2           0 JUMP_ABSOLUTE            6  // 行号 指令偏移 指令名称 参数
              3 LOAD_CONST             144
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python27\lib\dis.py", line 43, in dis
    disassemble(x)
  File "C:\Python27\lib\dis.py", line 95, in disassemble
    print '(' + repr(co.co_consts[oparg]) + ')',
IndexError: tuple index out of range
>>> dis.dis(PyCodeObjectData)
          0 DUP_TOPX            0 
          3 STOP_CODE
    >>    4 STOP_CODE
          5 STOP_CODE
    >>    6 STOP_CODE
    >>    7 STOP_CODE
          8 STOP_CODE
          9 ROT_TWO
         10 STOP_CODE
         11 STOP_CODE
         12 STOP_CODE
         13 BINARY_AND
         14 STOP_CODE
         15 STOP_CODE
         16 STOP_CODE
         17 POP_JUMP_IF_TRUE    41

在JUMP_ABSOLUTE 处出现错误。
用16进制编辑器打开文件
在这里插入图片描述
pyc前四个字节是魔术字,魔术字是用来标记python版本的标识。此处03f30d0a是python2.7的标识。魔术字之后四个字节是时间戳,表示修改时间。去掉前8个字节,剩下的就是个code的对象,code对象的结构如下:

typedef struct {
    PyObject_HEAD
    int co_argcount;        /* 位置参数个数 */
    int co_nlocals;         /* 局部变量个数 */
    int co_stacksize;       /* 栈大小 */
    int co_flags;  
    PyObject *co_code;      /* 字节码指令序列 */
    PyObject *co_consts;    /* 所有常量集合 */
    PyObject *co_names;     /* 所有符号名称集合 */
    PyObject *co_varnames;  /* 局部变量名称集合 */
    PyObject *co_freevars;  /* 闭包用的的变量名集合 */
    PyObject *co_cellvars;  /* 内部嵌套函数引用的变量名集合 */
    PyObject *co_filename;  /* 代码所在文件名 */
    PyObject *co_name;      /* 模块名|函数名|类名 */
    int co_firstlineno;     /* 代码块在文件中的起始行号 */
    PyObject *co_lnotab;    /* 字节码指令和行号的对应关系 */
    void *co_zombieframe;   /* for optimization only (see frameobject.c) */
} PyCodeObject;

有关于python的字节码都是什么意思,可以参考dis库的帮助文档,以及此博文对pyc文件结构介绍
https://cloud.tencent.com/developer/article/1421880

pyc代码混淆

PyCodeObject 的第22个字节开始就是 Python 的 opcode 序列了,这部分是决定了程序的执行流程,它被作为 TYPE_STRING 类型的 PyObject 存到了 PyCodeObject 的 co_code 当中。

由于 pyc 文件有现成的工具( uncompyle 6 )可以还原成 Python 代码,所以说我们不了解 pyc 格式也没有关系。这样我们混淆 pyc 的思路就可以是欺骗像 uncompyle 6 这类反编译的工具,让它误以为指令的序列不合法,但是又不影响真正的Python 虚拟机执行。

Python 的虚拟机是根据 PyCodeObject 中的 co_code 这个字段中存储的 opcode 序列来决定程序的执行流程的。所以说一个混淆的手段就是修改 co_code 字段中的 opcode 序列,可以添加一些加载超出范围的变量的指令,再用一些指令去跳过这些会出错的指令,这样执行的时候就不会出错了,但是反编译工具就不能正常工作了。

其中 JUMP_ABSOLUTE 6 表示直接跳转到 offset 为6的位置去执行指令,也就是插入的第三条指令 LOAD_CONST 144 并不会被执行,所以所以也并不会报错。但是对于反编译工具来说,这就是一个错误了,直接导致了反编译的失败。

据上面的那个思路,我们可以插入许多这样类似的指令,任意的不合法指令(其实随机数据都可以),然后用一些 JUMP 指令去跳过这样的不合法指令,上面的 JUMP_ABSOLUTE 只是一个简单的例子。甚至我们可以跳转到一些自行添加的虚假分支再跳转到到真实的分支(参考 ROP 的思路)。
Python 的 opcode 中 JUMP 相关的有多条指令,具体用法及差别可查询帮助文档。

所以此处解题思路即为将JUMP指令及其后面的混淆指令删除,修改代码段长度29为23字节(python中一般三字节表示一条指令, 如71 06 00 表示JUMP
)。修改后如图:
在这里插入图片描述
文件反汇编即得到原码:

#!/usr/bin/env python
# visit http://tool.lu/pyc/ for more information
def check():
    a = raw_input('plz input your flag:')
    temp = [
        0] * 43
    result = [
        135,
        151,
        102,
        148,
        101,
        241,
        160,
        101,
        112,
        241,
        176,
        241,
        176,
        241,
        112,
        160,
        117,
        117,
        244]
    if len(a) != 43:
        print 'error'
        exit()
    for i in range(len(a)):
        temp[i] = ord(a[i]) ^ 50
        temp[i] = (temp[i] >> 4 ^ temp[i] << 4) & 255
        if temp[i] != result[i]:
            print 'wrong'
            exit()
            continue
    print 'true'
if __name__ == '__main__':
    check()

一个很简单的逆向运算:
exp如下:

flag=[0]*43
f=''
for i in range(len(a)):
                flag[i]=(a[i]>>4^a[i]<<4)&255
print(flag)
for i in range(len(flag)):
                f+=chr(flag[i]^50)
print(f)

查阅资料发现 Python 源码有几种保护的方式:
1.生成 pyc 文件:这感觉完全不能算保护,uncompyle6 一键反编译,支持 Python 1.0 到 3.8 全部版本(恐怖)
2.py 源码混淆:一般针对 py 源码混淆就是往代码里插入一些没有意义跳转分支,修改变量名和函数名等这些操作,但是这种虽然阅读起来很难理解,但是混淆效果并不好。
3.打包成可执行的二进制文件
4.自定义 opcode 的 Python 解释器

解题成功后对python反编译工具进行了探究:
1.对于以下代码:
71 06 00 // JUMP_ABSOLUTE 6
64 ff ff // LOAD_CONST 65535
上面这段混淆的指令如果是使用uncompyle的情况下,肯定是反编译失败的,因为插入的第二条指令加载了一个不存在的常量,导致下标超出,引发异常。
如果是使用dis库直接反编译code对象,也是反编译失败的,原因是dis库在反编译code对象的时候会尝试去常量表里面把变量取出来打印。但是可以dis库的进行反编译 code.co_code 属性,就可以反编译成功,原因是这个对象为纯粹的字节码,并不会包含常量表,所以不会出现下标异常。跟本题相类似。

2.观察大佬反汇编pyc文件时直接用一行命令dump出了python字节码,并找出程序错误:
在这里插入图片描述
查询pycdc 是一个用于 pyc 反编译的工具 github 下载
安装 cmake apt-get install cmake
使用 在下载目录下输入 cmake CMakeList.txt
会在当前目录下生成 Makefile 文件 然后输入 make 即可进行编译安装

未完成的工作:
1.未实际操作指令的跳转,未实际操作从py文件到pyc文件的每一步,序列化反序列化等仍不清楚,心里没底https://cloud.tencent.com/developer/article/1421880
2.重叠指令pyc混淆方式不懂,大佬的博客(https://blog.csdn.net/ir0nf1st/article/details/61650984)
3.pycdc系列使用说明及安装方法尚不明

初次入门python字节码混淆,初步掌握了一点知识,待续。。。

Decompyle ++ Python字节码反汇编器/反编译器 Decompyle ++旨在将已编译的Python字节码转换回有效且易于阅读的Python源代码。 尽管其他项目也取得了不同程度的成功,但Decompyle ++的独特之处在于它寻求支持任何版本的Python的字节码。 Decompyle ++包括字节码反汇编程序(pycdas)和反编译程序(pycdc)。 顾名思义,Decompyle ++是用C ++编写的。 如果您想做出贡献,请在github上进行分叉 构建Decompyle ++ 使用生成项目或makefile(有关详细信息,请参阅CMake的文档) 可以将以下选项传递给CMake以控制调试功能: -DCMAKE_BUILD_TYPE=Debug 产生调试符号 -DENABLE_BLOCK_DEBUG=ON 启用块调试输出 -DENABLE_
Pythonpyc文件 pyc文件就是由Python文件经过编译后所生成的文件,py文件编译成pyc文件后加载速度更快而且提高了代码的安全性。pyc的内容与python的版本相关,不同版本编译的pyc文件不一样 什么是pyc文件 pyc是一种二进制文件,是由Python文件经过编译后所生成的文件,它是一种byte code,Python文件变成pyc文件后,加载的速度有所提高,而且pyc还是一种跨平台的字节码,由python的虚拟机来执行的,就类似于JAVA或者.NET的虚拟机的概念。pyc的内容与pyt
long length() 返回文件的字节数 String getName() 返回对象文件或目录的名称 String getPath() 返回对象表示的文件的相对路径名 String getAbsolutePath() 返回此对 pycdc Decompyle++ Decompyle++旨在将编译后的Python字节码转换回有效的、人类可读的Python源代码。虽然其他项目取得了不同程度的成功,但Decompyle++的独特之处在于,它寻求支持来自任何版本的Python的字节码。 Decompyle++包含一个字节码反汇编器(pycda)和一个反编译器(pycdc)。 该工具需要手动编译,使用kali编译以后的样本:https://pan.baidu.com/s/1tAzVPMzFmhWko915yguO
1 什么是pyc文件 1.1 什么是pyc文件 1、pyc文件:是由Python文件经过编译后所生成的文件,它是一种字节码 byte code,因此我们直接查看就是乱码的,也对源码起到一定的保护作用,但是这种字节码byte code是可以反编译的,后面会介绍! 我们都知道计算机是不认识你在代码里写的那一行行字母的,计算机只认二进制,也只执行二进制文件,我们写的代码是需要编译器编译成二进制的。(参考) 对于Python来说你写的Python代码在执行python xxx.py时会由Python解析器翻译成Py