Py学习  »  Python

Python 反编译,这些你不知道的心机与陷阱

AirPython • 3 年前 • 473 次点击  

谈到 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反编译

所谓道高一尺魔高一丈,不管是 Python、JAVA 还是 C 语言,只要有编译,必然存在反编译的操作。

不管再怎么去考虑加密,势必都有人能破解,只是成本不同而已,所以编译只可作为一种防君子不防小人的操作。

今天主要和大家聊聊 pyc 文件的反编译,其中有哪些小心机的做法与陷阱。

首先,反编译既然是一种非正常手段,Python 自然不会原生附带该模块。

我们需要先安装依赖:

# 安装依赖
pip install uncompyle6

操作也比较简单,命令行下输入:

uncompyle6 sample.pyc(pyo) > sample.py

这就完了?说好的心机与陷阱呢?哈哈,接着看…

使用注释的心机

我们来创建一个名为 BreezePython.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 的文件类型与编译、反编译操作有所了解。如果有收获,期待你的支持与转发。


留言送书

今日赠书:《亿级流量Java高并发及网络编程实战》
PS:最近当当有满 100 减 50 的活动


推荐阅读


用 Python 分析微信群聊记录,是怎样一种体验?

只需 10 行代码,Python 教你自制屏幕翻译工具,高效办公

为了追到小姐姐,我用 Python 制作了一个机器人



Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/63349
 
473 次点击  
文章 [ 1 ]  |  最新文章 3 年前