(轉)Python - 淺談Python的編譯與反編譯

https://www.cnblogs.com/anliven/p/9185549.html

目錄

1 - Python編譯過程涉及的文件

py

源代碼文件,由python.exe解釋,可在控制檯下運行,可用文本編輯器進行編輯;

pyc

源代碼文件經過編譯後生成的二進制文件,無法用文本編輯器進行編輯;
執行一個.py文件後,並不會自動生成對應的.pyc文件,需要指定觸發Python來創建pyc文件;
- pyc是由py文件經過編譯後生成的二進制字節碼(byte code)文件;
- pyc文件的加載速度比py文件快;
- pyc文件是一種跨平臺的字節碼,由python的虛擬機來執行;
- pyc文件的內容跟python版本相關,不同的python版本編譯生成不同的pyc文件,只能在相同版本環境下執行;

pyo

源代碼文件經過優化編譯後生成的文件,無法用文本編輯器進行編輯;
Python3.5之後,不再使用.pyo文件名,而是使用類似“xxx.opt-n.pyc的文件名;

pyd 

是python的動態鏈接庫;
動態鏈接庫(DLL)文件是一種可執行文件,允許程序共享執行特殊任務所必需的代碼和其他資源;
pyd文件雖然是作爲python的動態模塊,但實質上還是DLL文件,只是後綴改爲pyd;
一般是用C、C++、D語言按照一定的格式編寫;
參考信息:https://docs.python.org/3/faq/windows.html?highlight=pyd#is-a-pyd-file-the-same-as-a-dll

pyz

從Python 3.5開始,定義了.pyz和.pyzw分別作爲“Python Zip應用”和“Windows下Python Zip應用”的擴展名。
新增了內置zipapp模塊來進行簡單的管理,可以用Zip打包Python程序到一個可執行.pyz文件。
- zipapp — Manage executable python zip archives
- https://docs.python.org/3/library/zipapp.html
詳細內容請見PEP441(https://www.python.org/dev/peps/pep-0441/)

2 - 生成pyc文件

執行一個.py文件後,並不會自動生成對應的.pyc文件,需要指定觸發Python來創建pyc文件。
可以利用Python的import機制創建pyc文件:

  • 內置的py_compile模塊可以把py文件編譯爲pyc或pyo文件;
  • 內置的compileall模塊可以把整個目錄中的py文件編譯爲pyc或pyo文件;

 生成pyc文件的過程:

Python在執行import語句時(例如“import abc”),將會到已設定的path中尋找abc.pyc或abc.dll文件。
如果只是發現了abc.py,那麼Python會首先將abc.py編譯成相應的PyCodeObject中間結果,然後創建abc.pyc文件,並將中間結果寫入該文件。
然後,Python會import這個abc.pyc文件,實際上也就是將abc.pyc文件中的PyCodeObject重新在內存中複製出來。

生成pyc文件的方法:

python -m py_compile file.py  # 生成單個pyc文件
python -m py_compile /dir/{file1,file2}.py  # 生成多個pyc文件
python -m compileall /dir/  # 生成目錄下所有py文件對應的pyc文件

腳本形式:compile模塊的compile函數 

import py_compile  # 相當於命令行中的“-m py_compile”
py_compile.compile('py file path')

腳本形式:compileall模塊的compile_dir函數

import compileall
compileall.compile_dir("py files dir")

 生成pyc文件示例:

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ ls -l
total 2
-rw-r--r-- 1 anliven 197121 50 3月   7 22:55 sample.py
-rw-r--r-- 1 anliven 197121 49 3月   7 23:40 sample2.py

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ cat sample.py
# -*- coding: utf-8 -*-
print("Hello Python !")

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ cat sample2.py
# -*- coding: utf-8 -*-
print("Hello World !")

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ python -m compileall ./
Listing './'...
Compiling './sample.py'...
Compiling './sample2.py'...

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ ls -l
total 6
drwxr-xr-x 1 anliven 197121  0 3月   7 23:42 __pycache__/
-rw-r--r-- 1 anliven 197121 50 3月   7 22:55 sample.py
-rw-r--r-- 1 anliven 197121 49 3月   7 23:40 sample2.py

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ ls -l __pycache__/
total 2
-rw-r--r-- 1 anliven 197121 122 3月   7 23:41 sample.cpython-36.pyc
-rw-r--r-- 1 anliven 197121 122 3月   7 23:41 sample2.cpython-36.pyc

3 - 生成pyo文件

與生成pyc文件類似,但要額外使用-O和-OO選項來生成pyo文件。
但在Python3.5之後,不再使用.pyo文件名,而是生成文件名類似“xxx.opt-n.pyc的文件。

命令示例:

python -O -m py_compile file.py
python -O -m py_compile /dir/{file1,file2}.py
python -O -m compileall /dir/

示例:python3.6生成pyo文件

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ python -O -m compileall ./
Listing './'...
Compiling './sample.py'...
Compiling './sample2.py'...

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ ls -l __pycache__/
total 4
-rw-r--r-- 1 anliven 197121 122 3月   7 23:42 sample.cpython-36.opt-1.pyc
-rw-r--r-- 1 anliven 197121 122 3月   7 23:41 sample.cpython-36.pyc
-rw-r--r-- 1 anliven 197121 122 3月   7 23:42 sample2.cpython-36.opt-1.pyc
-rw-r--r-- 1 anliven 197121 122 3月   7 23:41 sample2.cpython-36.pyc

示例:python2.7生成pyo文件

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ ls -l
total 6
drwxr-xr-x 1 anliven 197121  0 3月   7 23:42 __pycache__/
-rw-r--r-- 1 anliven 197121 50 3月   7 22:55 sample.py
-rw-r--r-- 1 anliven 197121 49 3月   7 23:40 sample2.py

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ py -2 -O -m compileall ./
Listing ./ ...
Listing ./__pycache__ ...
Compiling ./sample.py ...
Compiling ./sample2.py ...

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ ls -l
total 8
drwxr-xr-x 1 anliven 197121   0 3月   7 23:42 __pycache__/
-rw-r--r-- 1 anliven 197121  50 3月   7 22:55 sample.py
-rw-r--r-- 1 anliven 197121 122 3月   7 23:45 sample.pyo
-rw-r--r-- 1 anliven 197121  49 3月   7 23:40 sample2.py
-rw-r--r-- 1 anliven 197121 122 3月   7 23:45 sample2.pyo

 

4 - 運行pyc或pyo文件

運行pyc文件

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test/__pycache__
$ python sample.cpython-36.pyc
Hello Python !

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test/__pycache__
$ python sample2.cpython-36.pyc
Hello World !

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test/__pycache__
$ python sample.cpython-36.opt-1.pyc
Hello Python !

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test/__pycache__
$ python sample2.cpython-36.opt-1.pyc
Hello World !

運行pyo文件

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ ls -l
total 8
drwxr-xr-x 1 anliven 197121   0 3月   7 23:42 __pycache__/
-rw-r--r-- 1 anliven 197121  50 3月   7 22:55 sample.py
-rw-r--r-- 1 anliven 197121 122 3月   7 23:45 sample.pyo
-rw-r--r-- 1 anliven 197121  49 3月   7 23:40 sample2.py
-rw-r--r-- 1 anliven 197121 122 3月   7 23:45 sample2.pyo

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ py -2 sample.pyo
Hello Python !

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test
$ py -2 sample2.pyo
Hello World !

5 - 利用uncompyle6進行Python反編譯

uncompyle6

  • Python cross-version byte-code decompiler
  • PyPI:https://pypi.python.org/pypi/uncompyle6
  • GitHub:https://github.com/rocky/python-uncompyle6

安裝uncompyle6

$ pip3 install --proxy="10.144.1.10:8080" uncompyle6
Collecting uncompyle6
  Downloading uncompyle6-3.0.0-py36-none-any.whl (195kB)
    100% |████████████████████████████████| 204kB 321kB/s
Requirement already satisfied: six in c:\python36\lib\site-packages (from uncompyle6)
Collecting spark-parser<1.9.0,>=1.8.5 (from uncompyle6)
  Downloading spark_parser-1.8.5-py36-none-any.whl
Collecting xdis<3.7.0,>=3.6.9 (from uncompyle6)
  Downloading xdis-3.6.11-py36-none-any.whl (74kB)
    100% |████████████████████████████████| 81kB 153kB/s
Collecting click (from spark-parser<1.9.0,>=1.8.5->uncompyle6)
  Using cached click-6.7-py2.py3-none-any.whl
Installing collected packages: click, spark-parser, xdis, uncompyle6
Successfully installed click-6.7 spark-parser-1.8.5 uncompyle6-3.0.0 xdis-3.6.11

$ pip3 show uncompyle6
Name: uncompyle6
Version: 3.0.0
Summary: Python cross-version byte-code decompiler
Home-page: https://github.com/rocky/python-uncompyle6/
Author: Rocky Bernstein, Hartmut Goebel, John Aycock, and others
Author-email: [email protected]
License: MIT
Location: c:\python36\lib\site-packages
Requires: xdis, spark-parser, six

示例:反編譯pyc文件

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test/__pycache__
$ ls -l
total 4
-rw-r--r-- 1 anliven 197121 122 3月   7 23:42 sample.cpython-36.opt-1.pyc
-rw-r--r-- 1 anliven 197121 122 3月   7 23:41 sample.cpython-36.pyc
-rw-r--r-- 1 anliven 197121 122 3月   7 23:42 sample2.cpython-36.opt-1.pyc
-rw-r--r-- 1 anliven 197121 122 3月   7 23:41 sample2.cpython-36.pyc

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test/__pycache__
$

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test/__pycache__
$ uncompyle6 sample.cpython-36.pyc > s1.py

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test/__pycache__
$ ls -l
total 5
-rw-r--r-- 1 anliven 197121 335 3月   8 00:01 s1.py
-rw-r--r-- 1 anliven 197121 122 3月   7 23:42 sample.cpython-36.opt-1.pyc
-rw-r--r-- 1 anliven 197121 122 3月   7 23:41 sample.cpython-36.pyc
-rw-r--r-- 1 anliven 197121 122 3月   7 23:42 sample2.cpython-36.opt-1.pyc
-rw-r--r-- 1 anliven 197121 122 3月   7 23:41 sample2.cpython-36.pyc

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test/__pycache__
$ cat s1.py
# uncompyle6 version 3.0.1
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)]
# Embedded file name: ./sample.py
# Compiled at: 2018-03-07 22:55:30
# Size of source mod 2**32: 50 bytes
print('Hello Python !')
# okay decompiling sample.cpython-36.pyc

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test/__pycache__
$

 示例:反編譯pyo文件

anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test                                        
$ ls -l                                                                                             
total 8                                                                                             
drwxr-xr-x 1 anliven 197121   0 3月   8 00:01 __pycache__/                                           
-rw-r--r-- 1 anliven 197121  50 3月   7 22:55 sample.py                                              
-rw-r--r-- 1 anliven 197121 122 3月   7 23:45 sample.pyo                                             
-rw-r--r-- 1 anliven 197121  49 3月   7 23:40 sample2.py                                             
-rw-r--r-- 1 anliven 197121 122 3月   7 23:45 sample2.pyo                                            
                                                                                                    
anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test                                        
$                                                                                                   
                                                                                                    
anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test                                        
$ uncompyle6 sample2.pyo > s2.py                                                                    
                                                                                                    
anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test                                        
$ cat s2.py                                                                                         
# uncompyle6 version 3.0.1                                                                          
# Python bytecode 2.7 (62211)                                                                       
# Decompiled from: Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)] 
# Embedded file name: ./sample2.py                                                                  
# Compiled at: 2018-03-07 23:40:22                                                                  
print 'Hello World !'                                                                               
# okay decompiling sample2.pyo                                                                      
                                                                                                    
anliven@DESKTOP-68OFQFP MINGW64 /d/Anliven/Anliven-Code/Test                                        
$                                                                

 uncompyle6的幫助信息

$ uncompyle6 --help

Usage:
  uncompyle6 [OPTIONS]... [ FILE | DIR]...
  uncompyle6 [--help | -h | --V | --version]

Examples:
  uncompyle6      foo.pyc bar.pyc       # decompile foo.pyc, bar.pyc to stdout
  uncompyle6 -o . foo.pyc bar.pyc       # decompile to ./foo.pyc_dis and ./bar.pyc_dis
  uncompyle6 -o /tmp /usr/lib/python1.5 # decompile whole library

Options:
  -o <path>     output decompiled files to this path:
                if multiple input files are decompiled, the common prefix
                is stripped from these names and the remainder appended to
                <path>
                  uncompyle6 -o /tmp bla/fasel.pyc bla/foo.pyc
                    -> /tmp/fasel.pyc_dis, /tmp/foo.pyc_dis
                  uncompyle6 -o /tmp bla/fasel.pyc bar/foo.pyc
                    -> /tmp/bla/fasel.pyc_dis, /tmp/bar/foo.pyc_dis
                  uncompyle6 -o /tmp /usr/lib/python1.5
                    -> /tmp/smtplib.pyc_dis ... /tmp/lib-tk/FixTk.pyc_dis
  -c <file>     attempts a disassembly after compiling <file>
  -d            print timestamps
  -p <integer>  use <integer> number of processes
  -r            recurse directories looking for .pyc and .pyo files
  --fragments   use fragments deparser
  --verify      compare generated source with input byte-code
  --verify-run  compile generated source, run it and check exit code
  --weak-verify compile generated source
  --linemaps    generated line number correspondencies between byte-code
                and generated source output
  --help        show this message

Debugging Options:
  --asm     -a  include byte-code         (disables --verify)
  --grammar -g  show matching grammar
  --tree    -t  include syntax tree       (disables --verify)

Extensions of generated files:
  '.pyc_dis' '.pyo_dis'   successfully decompiled (and verified if --verify)
    + '_unverified'       successfully decompile but --verify failed
    + '_failed'           decompile failed (contact author for enhancement)

6 - 其他Python反編譯工具

Decompyle++

A Python Byte-code Disassembler/Decompiler
https://github.com/zrax/pycdc

Easy Python Decompiler

https://sourceforge.net/projects/easypythondecompiler/
Easy Python Decompiler is python bytecode decompiler, decompiles pyc & pyo files.


Action is the antidote to despair!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章