<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          別再問我exe反編譯成Python腳本了!

          共 8311字,需瀏覽 17分鐘

           ·

          2021-08-29 03:56


          導(dǎo)讀:我曾經(jīng)寫了一篇:別再問我Python打包成exe了!(終極版),相信解決了不少小伙伴的Pyinstaller打包問題。


          不過我最近遇到了一個問題,自己打包好的exe文件還在,但是Python源文件不知什么時候被誤刪了。現(xiàn)在想改動一下功能,重寫Python腳本工程量也太大了,怎么辦?請看下文。

          作者:小小明
          來源:凹凸數(shù)據(jù)(ID:alltodata)


          今天我將教大家如何反編譯exe文件,即將自己或別人寫好的exe,還原成Python源碼。

          以最近寫Python一鍵自動整理歸類文件為例進(jìn)行演示,運(yùn)行所需的代碼和文件都會在文末提供給大家。


          打包成單文件所使用的命令為:

          pyinstaller -Fw --icon=h.ico auto_organize_gui.py --add-data="h.ico;/"

          打包成文件夾所使用的命令為:

          pyinstaller -w --icon=h.ico auto_organize_gui.py --add-data="h.ico;."

          不管是哪種打包方式都會留下一個exe文件。

          首先我們需要從exe文件中抽取出其中的pyc文件:


          01 抽取exe中的pyc文件

          抽取pyinstaller打包的exe中的pyc文件,提取pyc文件有兩種方法:

          1. 通過 pyinstxtractor.py 腳本提取pyc文件
          2. 通過 pyi-archive_viewer 工具提取pyc文件

          1. 腳本提取pyc文件

          pyinstxtractor.py 腳本可以在github項(xiàng)目 python-exe-unpacker 中下載,地址:

          https://github.com/countercept/Python-exe-unpacker

          下載該項(xiàng)目后把其中的pyinstxtractor.py腳本文件復(fù)制到與exe同級的目錄。

          然后進(jìn)入exe所在目錄的cmd執(zhí)行:

          Python pyinstxtractor.py auto_organize_gui.exe

          執(zhí)行后便得到exe文件名加上_extracted后綴的文件夾:


          對兩種打包方式產(chǎn)生的exe提取出的文件結(jié)構(gòu)稍有區(qū)別:


          2. 工具提取pyc文件

          pyi-archive_viewer是PyInstaller自己提供的工具,它可以直接提取打包結(jié)果exe中的pyc文件。

          詳細(xì)介紹可參考官方文檔:

          https://pyinstaller.readthedocs.io/en/stable/advanced-topics.html#using-pyi-archive-viewer

          執(zhí)行pyi-archive_viewer [filename]即可查看 exe 內(nèi)部的文件結(jié)構(gòu):

          pyi-archive_viewer auto_organize.exe

          操作命令:

          U: go Up one level
          O <name>: open embedded archive name
          X <name>: extract name
          Q: quit

          然后可以提取出指定需要提取的文件:


          要提取其他被導(dǎo)入的pyc文件,則需要先打開PYZ-00.pyz


          很顯然,使用PyInstaller的pyi-archive_viewer 工具操作起來比較麻煩,一次只能提取一個文件,遇到子模塊還需執(zhí)行一次打開操作。

          所以后面我也只使用pyinstxtractor.py 腳本來提取pyc文件。


          02 反編譯pyc文件為py腳本

          有很多對pyc文件進(jìn)行解密的網(wǎng)站,例如:

          https://tool.lu/pyc/

          不過我們直接使用 uncompyle6 庫進(jìn)行解碼,使用pip可以直接安裝:

          pip install uncompyle6

          uncompyle6可以反編譯.pyc后綴結(jié)尾的文件,兩種命令形式:

          1. uncompyle6 xxx.pyc>xxx.py
          2. uncompyle6 -o xxx.py xxx.pyc

          以前面編碼過程中生成的緩存為例進(jìn)行演示:

          uncompyle6 auto_organize.cpython-37.pyc>auto_organize.py

          執(zhí)行后便直接將.pyc文件反編譯成Python腳本了:


          從編譯結(jié)果看注釋也被保留了下來:


          對于不是pyc后綴結(jié)尾的文件,使用uncompyle6反編譯時會報(bào)出 must point to a Python source that can be compiled, or Python bytecode (.pyc, .pyo) 的錯誤。

          所以我們需要先對提取出的內(nèi)容人工修改后綴:


          1. 運(yùn)行入口pyc文件反編譯

          對于從pyinstaller提取出來的pyc文件并不能直接反編譯,入口運(yùn)行類共16字節(jié)的 magic 和 時間戳被去掉了。

          如果直接進(jìn)行反編譯,例如執(zhí)行

          uncompyle6 auto_organize_gui.exe_extracted/auto_organize_gui.pyc

          會報(bào)出如下錯誤:

          ImportError: Unknown magic number 227 in auto_organize_gui.exe_extracted\auto_organize_gui.pyc

          使用支持16進(jìn)制編輯的文本編輯器查看一探究竟,這里我使用UltraEdit32

          分別打開正常情況下編譯出的pyc和從pyinstaller提取出來的pyc文件進(jìn)行對比:


          可以看到前16個字節(jié)都被去掉了,其中前四個字節(jié)是magic,這四個字節(jié)會隨著系統(tǒng)和Python版本發(fā)生變化,必須一致。后四個字節(jié)包括時間戳和一些其他的信息,都可以隨意填寫。

          我們先通過UltraEdit32向pyinstaller提取的文件添加頭信息:


          選擇開頭插入16個字節(jié)后,只需要替換前4個字節(jié)為當(dāng)前環(huán)境下的magic:


          然后執(zhí)行:

          uncompyle6 auto_organize_gui.exe_extracted/auto_organize_gui.pyc>auto_organize_gui.py

          執(zhí)行后可以看到文件已經(jīng)順利的被反編譯:


          2. 依賴性pyc文件反編譯

          考慮再反編譯導(dǎo)入的其他依賴文件:


          先用UltraEdit32打開查看一下:


          可以看到對于非入口運(yùn)行的pyc文件是從12字節(jié)開始缺4個字節(jié)。

          這里我們選擇第13個字節(jié)再插入四個字節(jié)即可:


          然后再執(zhí)行:

          uncompyle6 auto_organize_gui.exe_extracted/PYZ-00.pyz_extracted/auto_organize.pyc > auto_organize.py

          然后成功的反編譯出依賴的文件:


          代碼與原文件幾乎完全一致:



          03 批量反編譯

          如果一個exe需要被反編譯的Python腳本只有3個以內(nèi)的文件,我們都完全可以人工來操作。

          但是假如一個exe涉及幾十個甚至上百個Python腳本需要反編譯的時候,人工操作未免工作量過于巨大,我們考慮將以上過程用Python實(shí)現(xiàn),從而達(dá)到批量反編譯的效果。

          1. 提取exe中的pyc

          import os
          import sys
          import pyinstxtractor

          exe_file = r"D:/PycharmProjects/gui_project/dist/auto_organize_gui.exe"
          sys.argv = ['pyinstxtractor', exe_file]
          pyinstxtractor.main()
          # 恢復(fù)當(dāng)前目錄位置
          os.chdir("..")

          [*] Processing D:/PycharmProjects/gui_project/dist/auto_organize_gui.exe
          [*] Pyinstaller version: 2.1+
          [*] Python version: 37
          [*] Length of package: 9491710 bytes
          [*] Found 984 files in CArchive
          [*] Beginning extraction...please standby
          [*] Found 157 files in PYZ archive
          [*] Successfully extracted pyinstaller archive: D:/PycharmProjects/gui_project/dist/auto_organize_gui.exe

          You can now use a Python decompiler on the pyc files within the extracted directory

          2. 預(yù)處理pyc文件修護(hù)校驗(yàn)頭

          def find_main(pyc_dir):
              for pyc_file in os.listdir(pyc_dir):
                  if not pyc_file.startswith("pyi-"and pyc_file.endswith("manifest"):
                      main_file = pyc_file.replace(".exe.manifest""")
                      result = f"{pyc_dir}/{main_file}"
                      if os.path.exists(result):
                          return main_file

          pyc_dir = os.path.basename(exe_file)+"_extracted"
          main_file = find_main(pyc_dir)
          main_file

          讀取從pyz目錄抽取的pyc文件的前4個字節(jié)作基準(zhǔn):

          pyz_dir = f"{pyc_dir}/PYZ-00.pyz_extracted"
          for pyc_file in os.listdir(pyz_dir):
              if pyc_file.endswith(".pyc"):
                  file = f"{pyz_dir}/{pyc_file}"
                  break
          with open(file, "rb"as f:
              head = f.read(4)
          list(map(hex, head))

          ['0x42''0xd''0xd''0xa']

          校準(zhǔn)入口類:

          import shutil
          if os.path.exists("pycfile_tmp"):
              shutil.rmtree("pycfile_tmp")
          os.mkdir("pycfile_tmp")
          main_file_result = f"pycfile_tmp/{main_file}.pyc"
          with open(f"{pyc_dir}/{main_file}""rb"as read, open(main_file_result, "wb"as write:
              write.write(head)
              write.write(b"\0"*12)
              write.write(read.read())

          校準(zhǔn)子類:

          pyz_dir = f"{pyc_dir}/PYZ-00.pyz_extracted"
          for pyc_file in os.listdir(pyz_dir):
              pyc_file_src = f"{pyz_dir}/{pyc_file}"
              pyc_file_dest = f"pycfile_tmp/{pyc_file}"
              print(pyc_file_src, pyc_file_dest)
              with open(pyc_file_src, "rb"as read, open(pyc_file_dest, "wb"as write:
                  write.write(read.read(12))
                  write.write(b"\0"*4)
                  write.write(read.read())

          3. 開始反編譯

          from uncompyle6.bin import uncompile

          if not os.path.exists("py_result"):
              os.mkdir("py_result")
          for pyc_file in os.listdir("pycfile_tmp"):
              sys.argv = ['uncompyle6''-o',
                          f'py_result/{pyc_file[:-1]}'f'pycfile_tmp/{pyc_file}']
              uncompile.main_bin()


          完整代碼下載見文末。

          這樣我們只需將Python腳本、exe文件和pyinstxtractor.py腳本文件 放置到同一文件夾下,運(yùn)行我們的Python腳本。即可反編譯exe。

          可以看到已經(jīng)完美的反編譯出exe其中的Python腳本:


          好了,相信大家已經(jīng)明白了反編譯的原理。那么既然是攻防,如何防止自己打包的exe被反編譯呢?


          04 如何防止exe被反編譯呢

          只需在打包命令后面加上--key命令即可,例如文章開頭的命令可以更換為:

          pyinstaller -Fw --icon=h.ico auto_organize_gui.py --add-data="h.ico;/" --key 123456

          123456是你用來加密的密鑰,可以隨意更換。

          該加密參數(shù)依賴tinyaes,可以通過以下命令安裝:

          pip install tinyaes

          打包后再次執(zhí)行反編譯:

          exe_file = r"D:/PycharmProjects/gui_project/dist/auto_organize_gui.exe"
          uncompyle_exe(exe_file, True)

          結(jié)果只有入口腳本反編譯成功,被依賴的腳本均被加密,無法直接被反編譯:


          可以看到抽取的中間結(jié)果變成了.pyc.encrypted格式,無法直接被反編譯:


          可以看到,常規(guī)手段就無法直接反編譯了。

          這個時候還想反編譯就需要底層的逆向分析研究了,或者pyinstaller的源碼完整研究一遍,了解其加密處理的機(jī)制,看看有沒有破解的可能。


          05 下載地址

          如果大家想測試exe反編譯,手頭又沒有合適的文件,在后臺對話框回復(fù)反編即可獲取。

          關(guān)于作者:小小明,數(shù)據(jù)、Python愛好者。
          個人博客地址:
          https://blog.csdn.net/as604049322


          延伸閱讀??

          延伸閱讀《利用Python進(jìn)行數(shù)據(jù)分析》

          干貨直達(dá)??


          更多精彩??

          在公眾號對話框輸入以下關(guān)鍵詞
          查看更多優(yōu)質(zhì)內(nèi)容!

          讀書 | 書單 | 干貨 | 講明白 | 神操作 | 手把手
          大數(shù)據(jù) | 云計(jì)算 | 數(shù)據(jù)庫 | Python | 爬蟲 | 可視化
          AI | 人工智能 | 機(jī)器學(xué)習(xí) | 深度學(xué)習(xí) | NLP
          5G | 中臺 | 用戶畫像 數(shù)學(xué) | 算法 數(shù)字孿生

          據(jù)統(tǒng)計(jì),99%的大咖都關(guān)注了這個公眾號
          ??
          瀏覽 22
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文字幕 国产精品 | 开心情色站 | 蜜桃臀美女被操 | 成人一级片 | 国产一区乱伦 |