python编译&反编译,你不知道的心机与陷阱

时间:2024-02-23 16:43:14

 谈到python的文件后缀,说眼花缭乱也不为过.来看看你遇到过哪些类型!

.py      

如果这个不知道,呵呵…那请出门左拐,你还是充钱那个少年,没有一丝丝改变。接着打游戏去吧…

.pyc      

这个后缀应该算是除了python的py代码外,遇到最多的一种文件类型了。虽然python被普遍认为是一种解释性语言,但谁说它就不能被编译后执行呢?python通过compile生成的pyc文件,然后由python的虚拟机执行。相对于py文件来说,编译成pyc本质上和py没有太大区别,只是对于这个模块的加载速度提高了,并没有提高代码的执行速度,通常情况下不用主动去编译pyc文件。那pyc文件存在的意义在哪里?
除了略微提高的模块加载速度外,更多的当然是为了一定意义上的保护源码不被泄露了。
当然为什么说一定意义?因为既然有编译,就毕竟存在反编译喽,这个一会儿说…
如何将py文件编译成pyc文件呢?方法有三:

1.使用python直接编译

python -m sample.py

 2. py_compile

import py_compile
py_compile.compile(\'sample.py\')

3. compileall

import compileall
compileall.compile_file(\'sample.py\')
compileall.compile_dir(dirpath)

三种方式一看便知,区别在于compileall可以一次递归编译文件夹下所有的py文件

.pyo      

pyo是源文件优化编译后的文件,pyo文件在大小上,一般小于等于pyc文件。这个优化没有多大作用,只是移除了断言。原文如下:

When the Python interpreter is invoked with the -O flag, optimized code is generated and stored in .pyo files. The optimizer currently doesn’t help much; it only removes assert statements. When -O is used, all bytecode is optimized; .pyc files are ignored and .py files are compiled to optimized bytecode.

使用方式:python -O -m py_compile sample.py

.pyd    

看到了前几种,相信d这个字母大家猜猜就能想到,它是python的动态链接库;动态链接库(DLL)文件是一种可执行文件,允许程序共享执行特殊任务所必需的代码和其他资源;你在python安装目录的DLL文件夹下,就可以看到它们了。

.pyz(w)

从Python 3.5开始,定义了.pyz和.pyzw分别作为“Python Zip应用”和“Windows下Python Zip应用”的扩展名。
新增了内置zipapp模块来进行简单的管理,可以用Zip打包Python程序到一个可执行.pyz文件。
使用也比较简单,但无法想pyinstaller等打包exe工具一样脱离python环境单独运行,而且这类文件往往拿文本编辑器就能看到它的源码信息,总体来说还是比较鸡肋。

.exe      

为什么会再单独提到exe文件,因为python有很多可以将python源码打包成exe的工具,从而脱离python环境单独运行。感兴趣的朋友可以去看看我之前总结的文章:Python打包工具--Pyinstaller详细介绍

python反编译

所谓道高一尺魔高一丈,不管是python、java还是C语言,只要有编译,必然存在反编译的操作。不管再怎么去考虑加密,势必都有人能破解,只是成本不同而已,所以编译只可作为一种防君子不防小人的操作。今天主要和大家聊聊pyc文件的反编译,其中有哪些小心机的做法与陷阱。
首先,反编译既然是一种非正常手段,python自然不会原生附带该模块,我们需要安装它:
pip install uncompyle6
操作呢,也比较简单命令行下输入:
uncompyle6 sample.pyc(pyo) > sample.py
这就完了?说好的心机与陷阱呢?哈哈,接着看…

使用注释的心机

我们来创建一个名为BreezePython.py的文件,并将下面的代码进行编译和反编译,来看看反编译后的内容与原始代码有什么差异

# -*- coding: utf-8 -*-
# @Author : 王翔
# @微信号 : King_Uranus
# @公众号 : 清风Python
# @GitHub : https://github.com/BreezePython
# @Date : 2019/12/15 11:16
# @Software : PyCharm
# @version :Python 3.7.3
# @File : tmp.py

import platform

def test():
    """
    这是一个测试方法,用来验证反编译
    :return: None
    """
    # 打印系统详情
    print(platform.platform())

test()

编译:

python -m BreezePython.py

反编译:
uncompyle6 BreezePython.cpython-37.pyc > output.py
下来看看反编译后的文件有什么区别吧:

# uncompyle6 version 3.6.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: C:\Users\Administrator\Desktop\uncompile\BreezePython.py
# Size of source mod 2**32: 453 bytes
import platform

def test():
    """
    这是一个测试方法,用来验证反编译
    :return: None
    """
    print(platform.platform())


test()
# okay decompiling BreezePython.cpython-37.pyc

相比于原始的代码,有什么差别?没错,注释…文档注释默认会保留,但使用# 进行的注释,却在编译+反编译的过程中丢失了。那么如果你是个心机boy,代码注释都用#去写吧,即便反编译了代码,没有注释的情况下,即便我们自己也会被大段没有注释的代码搞疯掉了,哈哈。

反编译的陷阱

反编译就万无一失么?NO…举一个例子吧,这段代码随意而为,只是为了让反编译的时候抛出错误:

class Demo:
    def __init__(self, color):
        self.color = color

    def jduge(self):
        if not self.color:
            return "get None"
        if not self.color == \'red\':
            mood = "disappointed"
            return "I\'m %s,I like white best" % mood
        return "get None..."


if __name__ == \'__main__\':
    Main = Demo("red")
    print(Main.jduge())

正常情况这段代码应该返回:get None…
但这段代码反编译后成了什么样子?

# uncompyle6 version 3.6.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: C:\Users\Administrator\Desktop\uncompile\BreezePython.py
# Size of source mod 2**32: 391 bytes

class Demo:
    def __init__(self, color):
        self.color = color
    def jduge(self):
        if not self.color:
            return \'get None\'
        else:
            mood = self.color == \'red\' or \'disappointed\'
            return "I\'m %s,I like white best" % mood
        return \'get None...\'

if __name__ == \'__main__\':
    Main = Demo(\'red\')
    print(Main.jduge())
# okay decompiling __pycache__\BreezePython.cpython-37.pyc

编译后的代码返回:I\'m True,I like white best
什么鬼?这编译成什么了乱七八糟的了东西了。京剧多年采坑经验,反编译的代码,当遇到多个if not 的时候,最后一个if not就会被所谓的简要写法弄的变了为了,导致最终反编译出错。这个是个坑,同样你是否也可以利用它呢?

今天的文章就到这里,希望通过这篇文章能让你对python的文件类型与编译、反编译操作有所了解。

作者:华为云特约供稿开发者 清风Python