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


     谈到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:UsersAdministratorDesktopuncompileBreezePython.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:UsersAdministratorDesktopuncompileBreezePython.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

  • 相关阅读:
    嗯哼?考的好?不存在的。
    MyBatis的框架设计
    Mybatis数据源与连接池
    MyBatis事务管理机制
    MyBatis的SqlSession的工作过程
    Mybatis初始化机制
    [JVM-6]类加载器
    [JVM-5]类加载机制
    面试题
    TCC分布式事务的实现原理
  • 原文地址:https://www.cnblogs.com/2020-zhy-jzoj/p/13164842.html
Copyright © 2020-2023  润新知