python反编译exe_实战 Python3.7+64位 Exe 反编译
記得有年在上海弘連培訓(xùn),其中一個逆向題就是關(guān)于python的Exe,當(dāng)時就想著寫個文檔,后來因為忙就拖延了下來;這里補上,而且是大補上:奉獻一個干貨,網(wǎng)上沒有(我沒發(fā)現(xiàn))Python3.7的反編譯教程,有的都是python2.7的,兩者有一個關(guān)鍵的地方不同(一層窗戶紙),花費了一些時間才明白,無私地分享給你,這里是不是應(yīng)該有掌聲。
一、生成python3.7+64位Exe程序
在反之前要先編一個。用文本工具寫個幾行的python代碼,如圖:
安裝Pyinstaller是一個坑,我忙乎了半天,費了幾次勁才成功,真是要看運氣。安裝成功后,用pyinstaller -F filename打包編譯成exe。
拖進exeinfo查看,果然是64位的;
運行下,可以運行,說明編譯成功。
在開工前,先準(zhǔn)備好相關(guān)的知識,為后面的順利進行打下基礎(chǔ)。(后面文字有點長,耐心點)
二、Pyc、Pyd、Pyo、Pyz介紹
(一)在實際開發(fā)中,Python作為解釋型語言,在實際的代碼分發(fā)過程中,有比較多的格式定義:.pyc\.pyd\.pyo\.pyz。
①.pyc文件是什么?python編譯后的二進制文件
Python源碼編譯的結(jié)果就是PyCodeObject(簡稱“代碼對象”),每個作用域會編譯出一個對應(yīng)的代碼對象,其中名為co_code的PyStringObject保存著代碼對象的字節(jié)碼。
一個Python源文件就是一個模塊。每個模塊頂層的代碼對象通過marshal序列化之后就得到了.pyc文件。marshal以little-endian字節(jié)序來序列化數(shù)據(jù)。
那嵌套于頂層作用域里面的那些作用域,例如函數(shù)、類的定義,它們對應(yīng)的代碼對象在哪里?它們每一個都乖乖的躺在上一層作用域的代碼對象的co_const(常量池)域里,所以其實頂層代碼對象已經(jīng)嵌套包含了底下其它作用域的代碼對象。
PyCodeObject的結(jié)構(gòu)和marshal的序列化邏輯和我們反編譯這塊沒有太大的關(guān)系,不介紹了,否則又是洋洋灑灑一大篇。
當(dāng)導(dǎo)入一個模塊時,類型為.pyc的文件將由解釋器自動生成,這將加速該模塊未來的導(dǎo)入。因此,這些文件僅在由另一個.py文件或模塊導(dǎo)入時從.py文件創(chuàng)建。
注意,使用.pyc文件只會加快程序的加載速度,而不會加快程序的實際執(zhí)行速度。這意味著您可以通過在一個模塊中編寫主程序來提高啟動時間,這個模塊由另一個更小的模塊導(dǎo)入。
pyc主要寫入三個內(nèi)容:
1).Magic num
2).Pyc創(chuàng)建時間
3).PyCodeObject.(python/marshal.c)
于是pyc magic num的作用有三:
一是拒絕完全不可能是正常的.pyc的文件,例如普通文本,圖片、音樂,或者別的二進制格式。檢查文件的頭4個字節(jié)已經(jīng)能有效的篩掉許多無效文件;
二是拒絕不慎被文本編輯器編輯而破損的文件;
三是拒絕不對應(yīng)的Python解釋器生成的.pyc文件。
由于不同Python版本的marshal算法可能不同,虛擬機采用的字節(jié)碼指令集也可能不同,所以保守起見不同版本的Python解釋器生成的.pyc文件被認為是不兼容的。
Python在不同的版本,pyc的頭部長度和內(nèi)容是不同的:
PEP 3147中指出:.pyc文件包含兩個2字節(jié)Header(表示一個MagicNum和Timestamp),后面跟序列化的PyCodeObject。每當(dāng)Python改變字節(jié)碼格式時,Magic Num會改變。Timestamp用于確保pyc文件與用于創(chuàng)建它的py文件匹配。當(dāng)Magic Num或Timestamp不匹配時,將重新編譯py文件并寫入新的pyc文件。
PEP 552中指出:.pyc頭文件目前由4個字節(jié)組成。第一個字節(jié)仍是magic number,對字節(jié)碼和pyc格式進行版本控制。第二個字節(jié)為新增加的字段,將是一個位字段(bit field),對報頭其余部分的解釋和pyc的失效行為取決于位字段的內(nèi)容。如果位字段(bit field)為0,則pyc是傳統(tǒng)的基于時間戳的pyc;第三個和第四個字節(jié)分別是時間戳和文件大小,通過比較源文件的元數(shù)據(jù)和頭文件中的元數(shù)據(jù)來進行無效判斷。
如果位字段的最低位被設(shè)置,則pyc是基于哈希的pyc。我們將第二個最低位稱為check_source標(biāo)志,位字段之后是源文件的64位散列,我們將使用帶有源文件內(nèi)容硬編碼密鑰;另一個類似MD5或BLAKE2的快速散列也可以,我們選擇SipHash是因為Python已經(jīng)從PEP 456中獲得了它的內(nèi)置實現(xiàn),盡管允許選擇SipHash鍵的接口必須公開給Python。
以下是一些常見的Magic num:
②.pyo文件:文件類型也是由解釋器在導(dǎo)入模塊時創(chuàng)建的。但是,.pyo文件是在啟用優(yōu)化設(shè)置時運行解釋器的結(jié)果。
當(dāng)我們調(diào)用Python解釋器時,通過添加“-O”標(biāo)志來啟用優(yōu)化器。
③.pyd文件:文件類型是特定于Windows操作系統(tǒng)類平臺的。因此,在個人版和企業(yè)版的Windows 10、Windows 7和其他版本中可能經(jīng)常遇到這種情況。
在Windows生態(tài)系統(tǒng)中,.pyd文件是一個包含Python代碼的庫文件,可以被其他Python應(yīng)用程序調(diào)用和使用。為了使這個庫對其他Python程序可用,它被打包為一個動態(tài)鏈接庫。
.pyd文件是一個動態(tài)鏈接庫,它包含一個Python模塊,或一組模塊,由其他Python代碼調(diào)用。要創(chuàng)建.pyd文件,需要創(chuàng)建一個名為example.pyd的模塊。在這個模塊中,需要創(chuàng)建一個名為PyInit_example()的函數(shù)。當(dāng)程序調(diào)用這個庫時,它們需要調(diào)用import foo, PyInit_example()函數(shù)將運行。
④.pyz文件:executable python zip archives,具體內(nèi)容參見下面的ZlibArchive;
(二)Python打包文件
打包文件是包含其他文件的文件,例如.tar文件、.jar文件或.zip文件。PyInstaller中使用了兩種存檔。一個是ZlibArchive,它允許高效地存儲Python模塊,并通過一些導(dǎo)入鉤子直接導(dǎo)入。另一個是CArchive,類似于.zip文件,這是一種打包(或壓縮)任意數(shù)據(jù)塊的通用方法。它的名字來源于這樣一個事實,即它可以很容易地從C和Python中操作。這兩個類都來自一個公共基類,這使得創(chuàng)建新類型的歸檔變得相當(dāng)容易。
①ZlibArchive:包含壓縮的.pyc或.pyo文件。spec文件中的PYZ類調(diào)用創(chuàng)建了一個ZlibArchive。ZlibArchive中的目錄是一個Python字典,它的Key(import語句中給定的成員名)與ZlibArchive中的查找位置和長度相關(guān)聯(lián)。ZlibArchive的所有部分都以編組格式存儲,因此與平臺無關(guān)。
ZlibArchive在運行時用于導(dǎo)入綁定的python模塊。即使使用最大壓縮,這也比正常導(dǎo)入快。而不是搜索系統(tǒng)。路徑,在字典里有一個查找。沒有目錄操作,也沒有要打開的文件(該文件已經(jīng)打開)。只有一次搜索,一次讀取和一次解壓。
Python錯誤跟蹤將指向創(chuàng)建歸檔條目的源文件(.pyc編譯、捕獲并保存到歸檔時的_file__屬性)。這不會告訴您的用戶任何有用的東西,但是如果他們向您發(fā)送Python錯誤跟蹤,您可以理解它。
②CArchive:可以包含任何類型的文件。它很像一個.zip文件。它們很容易用Python創(chuàng)建,也很容易從C代碼中解包。CArchive可以附加到另一個文件,比如ELF和COFF可執(zhí)行文件。為了實現(xiàn)這一點,存檔是在文件的末尾用它的目錄創(chuàng)建的,后面只跟一個cookie,它告訴目錄從哪里開始以及存檔本身從哪里開始。
CArchive可以嵌入到另一個CArchive中。內(nèi)部存檔可以在適當(dāng)?shù)牡胤酱蜷_和使用,而不必提取它。
每個目錄條目都有可變的長度。條目中的第一個字段給出了條目的長度。最后一個字段是相應(yīng)打包文件的名稱。名稱以空結(jié)尾。壓縮對于每個成員都是可選的。
還有一個與每個成員相關(guān)聯(lián)的類型代碼。類型代碼由自提取的可執(zhí)行程序使用。如果使用CArchive作為.zip文件,則不必擔(dān)心代碼。
ELF可執(zhí)行格式允許將任意數(shù)據(jù)連接到可執(zhí)行文件的末尾,而不影響其功能。因此,CArchive的目錄在歸檔的最后。可執(zhí)行文件可以以二進制文件的形式打開自己,查找到最后并“打開”CArchive。
三、反編譯Exe過程
因為是64位程序,用x64dbg載入查看,如圖:
發(fā)現(xiàn)PyInstaller等關(guān)鍵信息,可以確認是利用PyInstaller打包的python文件,所以我們要想辦法把python文件dump出來。
從網(wǎng)上搜索下,發(fā)現(xiàn)有工具可以直接將pyinstaller打包的Exe直接反編譯出來,拿來主義,直接用......,為了大家不走彎路,我直接給出正確途徑,如果按照網(wǎng)上的教程,你要摸索半天。
我沒用網(wǎng)上介紹的Pyinstxtractor.py,夠麻煩;我用的是用來提取的py腳本叫archive_viewer.py,將這個腳本文件和Exe放置在同一個目錄下,
python archive_viewer.py wei.exe
出現(xiàn)如下圖:
在這圖里,最重要的就是上面用紅線標(biāo)上的兩個部分,現(xiàn)在我們將它們dump出來,如下圖:
用x 命令將兩個結(jié)構(gòu)體導(dǎo)出,
會形成這兩個文件,struct這個位置在0,所以是頭部;
我們現(xiàn)在是將struct的頭部嫁接到wei.pyc的頭部,這里涉及到了pyc的頭部格式問題,我花了不少時間,因為我是實戰(zhàn)嫁接成功后才去找的原因(理論作支撐);對一個沒接觸過的東西摸索入門確實要花費很多時間,而且過程非常難以忍受,難怪路遙在寫完《平凡的世界》后第一件就是推開窗將手中的筆狠狠地扔了出去,我也有同感。
我們來看下導(dǎo)出的struct和pyc文件,當(dāng)我打開pyc文件時,010editor提示要安裝pyc.bt這個識別腳本,如圖:
肯定是選擇安裝,我信任它;可就是這個腳本害苦了我,按這個腳本的格式頭我怎么理解都相矛盾,且怎么嫁接都不成功,后來才發(fā)現(xiàn)這個腳本只能支持到python2.7,對后續(xù)的版本不支持,更別提3.7了,這也造成了我困惑很久。
對比兩個文件頭部,我們只要將struct的格式頭插入到wei.pyc的頭部,從上面的pyc的格式頭我們得知要插入16個字節(jié)的,當(dāng)初沒找到文件頭的文檔,導(dǎo)致走了不少彎路;插入完成后,如圖:
現(xiàn)在成為了一個完整的pyc格式的文件了,下面我們要做的就是將pyc轉(zhuǎn)換成py格式的,網(wǎng)上有很多的說明,這里我強調(diào)一下,不要用那個EasyPythonDecopiler,這個工具的效果并不好,其實有個網(wǎng)頁提供了pyc在線反編譯轉(zhuǎn)換功能,挺好,
到這里,反編譯過程結(jié)束了,有機會我來講解下用IDA逆向python的exe文件,屆時奉獻給大家。
這段時間連軸轉(zhuǎn),也蠻辛苦的;想想疫情前線的醫(yī)護人員,每時每刻都在同生死作搏斗,我就覺得我要努力抓緊時間多做些力所能及的事情,才夠資格向她們看齊。碼字雖累,但邊碼字邊陪著孩子,倒也其樂融融;如果您覺得作者辛苦了,請看后點個贊,鼓勵下!
另,有些人給我留言,希望能用上我寫的那些個工具軟件,我說可以,但有兩個條件:一是我要認識你嘛,好歹你要找個熟悉人介紹吧;二是你必須是網(wǎng)安的,在(一)的基礎(chǔ)上找我吧。
總結(jié)
以上是生活随笔為你收集整理的python反编译exe_实战 Python3.7+64位 Exe 反编译的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WPF自定义字体
- 下一篇: python测试用例管理模块_pytho