實戰 Python3.7+64位 Exe 反編譯

記得有年在上海弘連培訓,其中一個逆向題就是關於python的Exe,當時就想着寫個文檔,後來由於忙就拖延了下來;這裏補上,並且是大補上:奉獻一個乾貨,網上沒有(我沒發現)Python3.7的反編譯教程,有的都是python2.7的,二者有一個關鍵的地方不一樣(一層窗戶紙),花費了一些時間才明白,無私地分享給你,這裏是否是應該有掌聲。html

 

    1、生成python3.7+64位Exe程序python

    在反以前要先編一個。用文本工具寫個幾行的python代碼,如圖:算法

    安裝Pyinstaller是一個坑,我忙乎了半天,費了幾回勁才成功,真是要看運氣。安裝成功後,用pyinstaller -F filename打包編譯成exe。微信


    拖進exeinfo查看,果真是64位的;cookie


    運行下,能夠運行,說明編譯成功。python2.7

    在開工前,先準備好相關的知識,爲後面的順利進行打下基礎。(後面文字有點長,耐心點)編輯器

 

2、Pyc、Pyd、Pyo、Pyz介紹函數

    (一)在實際開發中,Python做爲解釋型語言,在實際的代碼分發過程當中,有比較多的格式定義:.pyc\.pyd\.pyo\.pyz工具


    ①.pyc文件是什麼? python編譯後的二進制文件優化

     

    Python源碼編譯的結果就是PyCodeObject(簡稱「代碼對象」),每一個做用域會編譯出一個對應的代碼對象,其中名爲co_codePyStringObject保存着代碼對象的字節碼。     


    一個Python源文件就是一個模塊。每一個模塊頂層的代碼對象經過marshal序列化以後就獲得了.pyc文件。marshallittle-endian字節序來序列化數據。     


    那嵌套於頂層做用域裏面的那些做用域,例如函數、類的定義,它們對應的代碼對象在哪裏?它們每個都乖乖的躺在上一層做用域的代碼對象的co_const(常量池)域裏,因此其實頂層代碼對象已經嵌套包含了底下其它做用域的代碼對象。  

   

    PyCodeObject的結構和marshal的序列化邏輯和咱們反編譯這塊沒有太大的關係,不介紹了,不然又是洋洋灑灑一大篇。 


    當導入一個模塊時,類型爲.pyc的文件將由解釋器自動生成,這將加速該模塊將來的導入。所以,這些文件僅在由另外一個.py文件或模塊導入時從.py文件建立。


    注意,使用.pyc文件只會加快程序的加載速度,而不會加快程序的實際執行速度。這意味着您能夠經過在一個模塊中編寫主程序來提升啓動時間,這個模塊由另外一個更小的模塊導入。

 

    pyc主要寫入三個內容:

    1).Magic num

    2).Pyc建立時間

    3).PyCodeObject.(python/marshal.c)


    因而pyc magic num的做用有三: 

    一是拒絕徹底不多是正常的.pyc的文件,例如普通文本,圖片、音樂,或者別的二進制格式。檢查文件的頭4個字節已經能有效的篩掉許多無效文件;

    二是拒毫不慎被文本編輯器編輯而破損的文件;     

    三是拒毫不對應的Python解釋器生成的.pyc文件。


    因爲不一樣Python版本的marshal算法可能不一樣,虛擬機採用的字節碼指令集也可能不一樣,因此保守起見不一樣版本的Python解釋器生成的.pyc文件被認爲是不兼容的。 


    Python在不一樣的版本,pyc的頭部長度和內容是不一樣的:

     PEP 3147中指出:.pyc文件包含兩個2字節Header(表示一個Magic Num和Timestamp),後面跟序列化的PyCodeObject每當Python改變字節碼格式時,Magic Num會改變。Timestamp用於確保pyc文件與用於建立它的py文件匹配。Magic NumTimestamp不匹配時,將從新編譯py文件並寫入新的pyc文件。


    PEP 552中指出:.pyc頭文件目前由4個字節組成。第一個字節還是magic number,對字節碼和pyc格式進行版本控制。第二個字節爲新增長的字段,將是一個位字段(bit field),對報頭其他部分的解釋和pyc的失效行爲取決於位字段的內容。若是位字段(bit field)爲0,則pyc是傳統的基於時間戳的pyc;第三個和第四個字節分別是時間戳和文件大小,經過比較源文件的元數據和頭文件中的元數據來進行無效判斷。


    若是位字段的最低位被設置,則pyc是基於哈希的pyc。咱們將第二個最低位稱爲check_source標誌,位字段以後是源文件的64位散列,咱們將使用帶有源文件內容硬編碼密鑰;另外一個相似MD5或BLAKE2的快速散列也能夠,咱們選擇SipHash是由於Python已經從PEP 456中得到了它的內置實現,儘管容許選擇SipHash鍵的接口必須公開給Python。


    如下是一些常見的Magic num:

     ②.pyo文件:文件類型也是由解釋器在導入模塊時建立的。可是,.pyo文件是在啓用優化設置時運行解釋器的結果。


    當咱們調用Python解釋器時,經過添加「-O」標誌來啓用優化器。


    ③.pyd文件:文件類型是特定於Windows操做系統類平臺的。所以,在我的版和企業版的Windows 十、Windows 7和其餘版本中可能常常遇到這種狀況。


    在Windows生態系統中,.pyd文件是一個包含Python代碼的庫文件,能夠被其餘Python應用程序調用和使用。爲了使這個庫對其餘Python程序可用,它被打包爲一個動態連接庫。


    .pyd文件是一個動態連接庫,它包含一個Python模塊,或一組模塊,由其餘Python代碼調用。要建立.pyd文件,須要建立一個名爲example.pyd的模塊。在這個模塊中,須要建立一個名爲PyInit_example()的函數。當程序調用這個庫時,它們須要調用import foo, PyInit_example()函數將運行。


    ④.pyz文件: executable python zip archives具體內容參見下面的ZlibArchive


    (二)Python打包文件

    打包文件是包含其餘文件的文件,例如.tar文件、.jar文件或.zip文件。PyInstaller中使用了兩種存檔。一個是ZlibArchive,它容許高效地存儲Python模塊,並經過一些導入鉤子直接導入。另外一個是CArchive,相似於.zip文件,這是一種打包(或壓縮)任意數據塊的通用方法。它的名字來源於這樣一個事實,即它能夠很容易地從C和Python中操做。這兩個類都來自一個公共基類,這使得建立新類型的歸檔變得至關容易。


    ①ZlibArchive:包含壓縮的.pyc或.pyo文件。spec文件中的PYZ類調用建立了一個ZlibArchiveZlibArchive中的目錄是一個Python字典,它的Key(import語句中給定的成員名)與ZlibArchive中的查找位置和長度相關聯。ZlibArchive的全部部分都以編組格式存儲,所以與平臺無關。


    ZlibArchive在運行時用於導入綁定的python模塊。即便使用最大壓縮,這也比正常導入快。而不是搜索系統。路徑,在字典裏有一個查找。沒有目錄操做,也沒有要打開的文件(該文件已經打開)。只有一次搜索,一次讀取和一次解壓。


    Python錯誤跟蹤將指向建立歸檔條目的源文件(.pyc編譯、捕獲並保存到歸檔時的_file__屬性)。這不會告訴您的用戶任何有用的東西,可是若是他們向您發送Python錯誤跟蹤,您能夠理解它。


    ②CArchive:能夠包含任何類型的文件。它很像一個.zip文件。它們很容易用Python建立,也很容易從C代碼中解包。CArchive能夠附加到另外一個文件,好比ELF和COFF可執行文件。爲了實現這一點,存檔是在文件的末尾用它的目錄建立的,後面只跟一個cookie,它告訴目錄從哪裏開始以及存檔自己從哪裏開始。


    CArchive能夠嵌入到另外一個CArchive中。內部存檔能夠在適當的地方打開和使用,而沒必要提取它。


    每一個目錄條目都有可變的長度。條目中的第一個字段給出了條目的長度。最後一個字段是相應打包文件的名稱。名稱以空結尾。壓縮對於每一個成員都是可選的。


    還有一個與每一個成員相關聯的類型代碼。類型代碼由自提取的可執行程序使用。若是使用CArchive做爲.zip文件,則沒必要擔憂代碼。


    ELF可執行格式容許將任意數據鏈接到可執行文件的末尾,而不影響其功能。所以,CArchive的目錄在歸檔的最後。可執行文件能夠以二進制文件的形式打開本身,查找到最後並「打開」CArchive


    3、反編譯Exe過程

    由於是64位程序,用x64dbg載入查看,如圖:

    發現PyInstaller等關鍵信息,能夠確認是利用PyInstaller打包的python文件,因此咱們要想辦法把python文件dump出來。


    從網上搜索下,發現有工具能夠直接將pyinstaller打包的Exe直接反編譯出來,拿來主義,直接用......,爲了你們不走彎路,我直接給出正確途徑,若是按照網上的教程,你要摸索半天。


    我沒用網上介紹的Pyinstxtractor.py,夠麻煩;我用的是用來提取的py腳本叫archive_viewer.py,將這個腳本文件和Exe放置在同一個目錄下,

    python archive_viewer.py wei.exe

    出現以下圖:

    在這圖裏,最重要的就是上面用紅線標上的兩個部分,如今咱們將它們dump出來,以下圖:

    用x 命令將兩個結構體導出,

會造成這兩個文件,struct這個位置在0,因此是頭部;


    咱們如今是將struct的頭部嫁接到wei.pyc的頭部,這裏涉及到了pyc的頭部格式問題,我花了很多時間,由於我是實戰嫁接成功後纔去找的緣由(理論做支撐);對一個沒接觸過的東西摸索入門確實要花費不少時間,並且過程很是難以忍受,難怪路遙在寫完《平凡的世界》後第一件就是推開窗將手中的筆狠狠地扔了出去,我也有同感。

 

    咱們來看下導出的struct和pyc文件,當我打開pyc文件時,010editor提示要安裝pyc.bt這個識別腳本,如圖:

    確定是選擇安裝,我信任它;可就是這個腳本害苦了我,按這個腳本的格式頭我怎麼理解都相矛盾,且怎麼嫁接都不成功,後來才發現這個腳本只能支持到python2.7,對後續的版本不支持,更別提3.7了,這也形成了我困惑好久。


    對比兩個文件頭部,咱們只要將struct的格式頭插入到wei.pyc的頭部,從上面的pyc的格式頭咱們得知要插入16個字節的,當初沒找到文件頭的文檔,致使走了很多彎路;插入完成後,如圖:

    

    如今成爲了一個完整的pyc格式的文件了,下面咱們要作的就是將pyc轉換成py格式的,網上有不少的說明,這裏我強調一下,不要用那個EasyPythonDecopiler,這個工具的效果並很差,其實有個網頁提供了pyc在線反編譯轉換功能,挺好,

    到這裏,反編譯過程結束了,有機會我來說解下用IDA逆向python的exe文件,屆時奉獻給你們。


    這段時間連軸轉,也蠻辛苦的;想一想疫情前線的醫護人員,每時每刻都在同生死做搏鬥,我就以爲我要努力抓緊時間多作些力所能及的事情,纔夠資格向她們看齊。碼字雖累,但邊碼字邊陪着孩子,倒也其樂融融;若是您以爲做者辛苦了,請看後點個贊,鼓勵下!


    另,有些人給我留言,但願能用上我寫的那些個工具軟件,我說能夠,但有兩個條件:一是我要認識你嘛,好歹你要找個熟悉人介紹吧;二是你必須是網安的,在(一)的基礎上找我吧。



本文分享自微信公衆號 - MicroPest(gh_696c36c5382b)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索